Qt中对于XML文件的写入有两种方式,一个是使用QXmlStreamWriter,另一个则为使用Dom。stream流的形式相对来说更加灵活,而且适合处理大文件。Dom方式由于是将内容加载到了内存中进行操作,所以对于小内存设备则有一定得局限性。
根据《QtCreator快速入门》和网上的一些例子练习了Qt XML的使用,做一个记录,以下是采用Dom方式实现的
实现界面
编写的XML文件
<?xml version="1.0" encoding="UTF-8"?>
<information>
<pin id="">
<card id="">
<port id="" flag=""/>
<port id="" flag=""/>
<port id="" flag=""/>
<port id="" flag=""/>
<port id="" flag=""/>
<port id="" flag=""/>
<port id="" flag=""/>
</card>
<card id="">
<port id="" flag=""/>
<port id="" flag=""/>
<port id="" flag=""/>
<port id="" flag=""/>
<port id="" flag=""/>
<port id="" flag=""/>
</card>
<card id="">
<port id="" flag=""/>
<port id="" flag=""/>
<port id="" flag=""/>
<port id="" flag=""/>
<port id="" flag=""/>
</card>
</pin>
<pin id="">
<card id="">
<port id="" flag=""/>
<port id="" flag=""/>
<port id="" flag=""/>
<port id="" flag=""/>
<port id="" flag=""/>
<port id="" flag=""/>
<port id="" flag=""/>
</card>
<card id="">
<port id="" flag=""/>
<port id="" flag=""/>
<port id="" flag=""/>
<port id="" flag=""/>
<port id="" flag=""/>
<port id="" flag=""/>
</card>
</pin>
</information>
一共有三层,pin->card->port,最内层的port有id和flag两种属性
首先是编写XML文件
代码如下:
void xml::writeXML()
{
QFile file("../project/ini/write.xml");
if(!file.open(QIODevice::WriteOnly|QIODevice::Truncate))
{
return;
}
QDomDocument doc;
QDomProcessingInstruction instruction; instruction = doc.createProcessingInstruction("xml", "version=\"1.0\" encoding=\"UTF-8\"");
doc.appendChild(instruction);
QDomElement root=doc.createElement(tr("information"));
doc.appendChild(root); QDomElement Pin;
QDomElement Card;
QDomElement Port;
QDomAttr ID = doc.createAttribute("id");
QDomAttr Flag = doc.createAttribute("flag");
//QDomText text; for (int i = ;i < ;i++)
{
Pin = doc.createElement(tr("pin")); ID = doc.createAttribute("id");
ID.setValue(tr("%1").arg(i));
Pin.setAttributeNode(ID); //Pin.appendChild(Card);
//Card = doc.createElement(tr("card"));
for (int j = ;j < - i;j++)
{
Card = doc.createElement(tr("card")); ID = doc.createAttribute("id");
ID.setValue(tr("%1").arg(j));
Card.setAttributeNode(ID); for (int k = ;k < - j;k++)
{
Port = doc.createElement(tr("port")); ID = doc.createAttribute("id");
ID.setValue(tr("%1").arg(k));
Port.setAttributeNode(ID);
Flag = doc.createAttribute("flag");
Flag.setValue("");
Port.setAttributeNode(Flag); Card.appendChild(Port);
}
Pin.appendChild(Card);
}
root.appendChild(Pin);
} QTextStream out(&file);
doc.save(out,);
file.close();
}
QDomProcessingInstruction instruction;
instruction = doc.createProcessingInstruction("xml","version=/"1.0/" encoding=/"UTF-8/"");
用来写入XML文件的声明,这对于一个XML文件来说不可缺少。
writeXML()中采用三层for循环进行XML的存储。 好了,现在有我们需要操作的XML文件了,下一步就是就是进行读取,并在界面上显示
我是采用三个listWidget分别显示pin card port信息的
代码如下:
/*
*函数名:_CreateListWidget
*功能:初始化探针、板卡、端口信息
*
*/
void configure_win::_CreateListWidget()
{
ui->cardlistWidget->clear();
ui->portlistWidget->clear();
ui->pinlistWidget->clear();
int flag_card = ;
int flag_port = ;//判断标识,确定listWidget初始化显示一次
QDomDocument doc;
QFile file("../project/ini/write.xml");
if (!file.open(QIODevice::ReadOnly))
{
return;
}
if (!doc.setContent(&file))
{
file.close();
return;
}
file.close();
QDomElement docElem = doc.documentElement();
QDomNode n = docElem.firstChild();
while (!n.isNull())
{
if (n.isElement())
{
QDomElement e = n.toElement();
ui->pinlistWidget->addItem(e.toElement().tagName()+e.toElement().attribute("id"));
//ui->pinlistWidget->addItem("探针"+e.toElement().attribute("id"));
QDomNodeList listcard = e.childNodes();
if (flag_card)
{
for (int i = ;i < listcard.count();i++)
{
QDomNode node = listcard.at(i);
if (node.isElement())
{
ui->cardlistWidget->addItem(node.toElement().tagName()+node.toElement().attribute("id"));
//ui->cardlistWidget->addItem("板卡"+node.toElement().attribute("id"));
QDomNodeList listport = node.childNodes();
if (flag_port)
{ for (int j = ;j < listport.count();j++)
{
QDomNode port = listport.at(j);
if (port.isElement())
{
QListWidgetItem *item = new QListWidgetItem;
item->setText(port.toElement().tagName() + port.toElement().attribute("id"));
item->setFlags(Qt::ItemIsEnabled|Qt::ItemIsSelectable|Qt::ItemIsUserCheckable);
item->setCheckState(Qt::Unchecked);
ui->portlistWidget->addItem(item);
//ui->portlistWidget->addItem(port.toElement().tagName() + port.toElement().attribute("id"));
//ui->portlistWidget->addItem("端口" + port.toElement().attribute("id"));
}
}
}
flag_port = ;
}
node = node.nextSibling();
}
}
flag_card = ;
}
n = n.nextSibling();
}
ui->pinlistWidget->item()->setSelected(true);
ui->cardlistWidget->item()->setSelected(true);
g_pin = ui->pinlistWidget->item()->text();//初始化存储pin card port
g_card = ui->cardlistWidget->item()->text();
g_port = ui->portlistWidget->item()->text();
}
因为涉及到获取listWidget的item的信息,而界面上只能有一个焦点,所以我使用了三个全局变量保存当前被激活的item项
并且默认为第一项被选中,这样就完成了开始界面的呈现
现在只是完成了XML数据的静态呈现,还没有动态显示,所以下一步需要对listWidget添加一些信号槽函数
connect(ui->pinlistWidget,SIGNAL(itemClicked(QListWidgetItem*)),this,SLOT(xmlChanged(QListWidgetItem*)));//点击探针
connect(ui->cardlistWidget,SIGNAL(itemClicked(QListWidgetItem*)),this,SLOT(xmlChanged(QListWidgetItem*)));//点击板卡
代码如下:
/*
*槽函数名:xmlChanged
*功能:根据选择读取xml信息
*
*/
void configure_win::xmlChanged(QListWidgetItem *item)
{
QString str = item->text();
QString str2 = str.right();//取最后一位1
QString str1 = str.left(str.length() - );//去掉最后一位 card
if (str1 == "pin")//点击pin
{
g_pin = str;//选中的pin改变
g_card = "card1";//默认card1
g_port = "port1";
_SelectChanged(str,"card1");
ui->cardlistWidget->item()->setSelected(true);
}
else if (str1 == "card")//点击card
{
g_card = str;//选中的card改变
g_port = "port1";
QString strpin;
if (ui->pinlistWidget->currentRow() == -)//获得选中card对应探针????初始化pin第一项pin为选中,为什么返回-1而不是0
{
strpin = ui->pinlistWidget->item()->text();
}
else
{
strpin = ui->pinlistWidget->currentItem()->text();
}
int i = ui->cardlistWidget->currentRow();//获取选中card项
_SelectChanged(strpin,str);//更新listWidget
ui->cardlistWidget->item(i)->setSelected(true);//重新设置card为选中
}
}
/*
*函数名:_SelectChanged
*功能:更新探针、板卡、端口信息
*
*/
void configure_win::_SelectChanged(QString str1, QString str2)
{ QDomDocument doc;
QFile file("../project/ini/write.xml");
if (!file.open(QIODevice::ReadOnly))
{
return;
}
if (!doc.setContent(&file))
{
file.close();
return;
}
file.close(); //QString str = item->text();
//qDebug()<<str1;
QString strNumberPin = str1.right();
QString strTypePin = str1.left(str1.length() - );//去掉最后一位
QString strNumberCard = str2.right();
QString strTypeCard = str2.left(str1.length() - );//去掉最后一位
//qDebug()<<strNumberPin<<strTypePin<<strNumberCard<<"\n"<<strTypeCard;
//if (i)//点击板卡
{
QDomNodeList list = doc.elementsByTagName(strTypePin);//查找探针项
for (int i = ;i < list.count();i++)
{
QDomElement e = list.at(i).toElement();
if (e.attribute("id") == strNumberPin)//找到对应的探针
{
ui->cardlistWidget->clear();//清空板卡
QDomNodeList childCard = e.childNodes();
for (int j = ;j < childCard.count();j++)//遍历板卡
{
QDomElement card = childCard.at(j).toElement();
//QDomNode port = childCard.at(j);
//qDebug()<<" "<<qPrintable(card.tagName())<<qPrintable(card.attribute("id"));
QListWidgetItem *item = new QListWidgetItem;
item->setText(card.tagName() + card.attribute("id")); //item->setFlags(Qt::ItemIsEnabled|Qt::ItemIsSelectable|Qt::ItemIsUserCheckable);
//item->setCheckState(Qt::Unchecked);
ui->cardlistWidget->addItem(item);
if (card.attribute("id") == strNumberCard)
{
ui->portlistWidget->clear();//清空端口
QDomNodeList childPort = card.childNodes(); for (int k = ;k < childPort.count();k++)
{
QDomNode port = childPort.at(k);
//qDebug()<<" "<<qPrintable(port.toElement().tagName())<<qPrintable(port.toElement().attribute("id"));
QListWidgetItem *item = new QListWidgetItem;
item->setText(port.toElement().tagName() + port.toElement().attribute("id"));
item->setFlags(Qt::ItemIsEnabled|Qt::ItemIsSelectable|Qt::ItemIsUserCheckable);
if (port.toElement().attribute("flag") == "")
{
item->setCheckState(Qt::Unchecked);
}
else
{
item->setCheckState(Qt::Checked);
}
ui->portlistWidget->addItem(item);
}
}
}
return;//完成部分显示后跳出遍历
}
}
}
}
xmlChanged()函数响应信号,获取item信息
_SelectChanged()函数根据pin,card读取XML进行界面的动态呈现,还是默认card、port的第一项为选中 到现在为止,我只进行了XML的读取(全部和选择部分),下面是进行XML的修改,这里我选择更改port的flag属性,默认为0,选中为1
<?xml version='1.0' encoding='UTF-8'?>
<information>
<pin id="">
<card id="">
<port id="" flag=""/>
<port id="" flag=""/>
<port id="" flag=""/>
<port id="" flag=""/>
<port id="" flag=""/>
<port id="" flag=""/>
<port id="" flag=""/>
</card>
<card id="">
<port id="" flag=""/>
<port id="" flag=""/>
<port id="" flag=""/>
<port id="" flag=""/>
<port id="" flag=""/>
<port id="" flag=""/>
</card>
<card id="">
<port id="" flag=""/>
<port id="" flag=""/>
<port id="" flag=""/>
<port id="" flag=""/>
<port id="" flag=""/>
</card>
</pin>
<pin id="">
<card id="">
<port id="" flag=""/>
<port id="" flag=""/>
<port id="" flag=""/>
<port id="" flag=""/>
<port id="" flag=""/>
<port id="" flag=""/>
<port id="" flag=""/>
</card>
<card id="">
<port id="" flag=""/>
<port id="" flag=""/>
<port id="" flag=""/>
<port id="" flag=""/>
<port id="" flag=""/>
<port id="" flag=""/>
</card>
</pin>
</information>
pin2的card1中的一些端口flag状态已经改变
代码如下:
connect(ui->portlistWidget,SIGNAL(itemClicked(QListWidgetItem*)),this,SLOT(portSelected(QListWidgetItem*)));//点击端口
void configure_win::portSelected(QListWidgetItem *item)
{
//qDebug()<<"&&&&&&&&&&&&&&&&&&&";
g_port = item->text();
if (item->checkState() == Qt::Checked)//添加
{
_linkUpdate(g_pin,g_card,g_port,);//链路
_xmlUpdate(g_pin,g_card,g_port,);//XML文件
}
else//删除
{
_linkUpdate(g_pin,g_card,g_port,);;//链路
_xmlUpdate(g_pin,g_card,g_port,);//XML文件
}
}
/*
*函数名:_xmlUpdate
*功能:更新xml文件信息
*
*/
void configure_win::_xmlUpdate(QString pin, QString card, QString port, int number)
{
//qDebug()<<i;
QDomDocument doc;
QDomAttr Flag = doc.createAttribute("flag");
QFile file("../project/ini/write.xml");
if (!file.open(QIODevice::ReadWrite))
{
return;
}
if (!doc.setContent(&file))
{
file.close();
return;
}
file.close(); QString strNumberPin = pin.right();
QString strTypePin = pin.left(pin.length() - );//去掉最后一位
QString strNumberCard = card.right();
QString strTypeCard = card.left(card.length() - );//去掉最后一位
QString strNumberPort = port.right();
QString strTypePort = port.left(port.length() - );//去掉最后一位port QDomNodeList list = doc.elementsByTagName(strTypePin);//查找探针项
for (int i = ;i < list.count();i++)
{
QDomElement e = list.at(i).toElement();
if (e.attribute("id") == strNumberPin)//找到对应的探针
{
//ui->cardlistWidget->clear();//清空板卡
QDomNodeList childCard = e.childNodes();
for (int j = ;j < childCard.count();j++)//遍历板卡
{
QDomElement card = childCard.at(j).toElement();
//qDebug()<<" "<<qPrintable(card.tagName())<<qPrintable(card.attribute("id"));
if (card.attribute("id") == strNumberCard)
{
QDomNodeList childPort = card.childNodes(); for (int k = ;k < childPort.count();k++)
{
QDomNode port = childPort.at(k); if (port.toElement().attribute("id") == strNumberPort)
{
QString num = QString::number(number);
//qDebug()<<num;
port.toElement().setAttribute("flag",num); //把属性写入xml文件
goto _NEXT;
}
}
}
}
}
}
_NEXT:
QFile f("../project/ini/write.xml");
//if(!f.open(QIODevice::WriteOnly|QIODevice::Truncate))
if(!f.open(QIODevice::WriteOnly|QIODevice::Truncate))
{
return;
}
QTextStream out(&f);
doc.save(out,);
f.close();
}
好了,我的XML操作到这里都实现了,最后我根据需要,添加了移除按钮的信息,也对XML中的port属性进行修改
connect(ui->linkWidget,SIGNAL(itemClicked(QListWidgetItem*)),this,SLOT(mylinkWidgetSelect(QListWidgetItem*)));//移除,禁用按钮可用
/*
*槽函数名:on_removeBtn_clicked
*功能:单击移除按钮,移除选中端口
*
*/
void configure_win::on_removeBtn_clicked()
{
QMessageBox msgBox;
msgBox.setWindowTitle(tr("提示"));
msgBox.setText(tr("确定要删除端口?"));
msgBox.setIcon(QMessageBox::Information);
msgBox.addButton(tr("确定(&Y)"),QMessageBox::YesRole);
msgBox.addButton(tr("取消(&N)"),QMessageBox::NoRole);
msgBox.setStyleSheet("background-color:grey");
int ret = msgBox.exec();
if (ret == )//删除端口
{
int row = ui->linkWidget->currentRow();
QString strlink = ui->linkWidget->item(row)->text();
QStringList listlink = strlink.split("->");
QString pin = listlink.at();
QString card = listlink.at();
QString port = listlink.at();
_xmlUpdate(pin,card,port,);//更新xml文件
if (pin == g_pin && card == g_card)
{
for (int j =;j < ui->portlistWidget->count();j++)
{
//qDebug()<<ui->portlistWidget->item(j)->text();
if (ui->portlistWidget->item(j)->text() == port)
{ ui->portlistWidget->item(j)->setCheckState(Qt::Unchecked);
break;
}
}
}
ui->linkWidget->takeItem(row);
}
}
忘了加一句,需要在.pro文件中添加QT += xml
这样才能使用XML的一些类
花了两天半的时间,终于把项目需要的一些对XML的操作搞定了