Qt使用XML设计多级关联菜单

在上一篇博客中,我们介绍了如何生成一个XML文件,今天我们利用生成的XML文件来进行解析并设计出多级关联菜单。在以前的文章中解决了如何生成二级菜单,不过之前的方法过于笨重,采用手动添加代码的方式,不仅代码重复率高而且不利于修改(维护)。所以,今天我们换用读XML的方式再次解决这个问题。

本打算采用model/view架构加下拉框列表设计为下拉表格的形式,但是遇到一个问题,下拉框的currentIndex始终无法正确的设置,它默认把表格的一行当成一个Item,所有当鼠标选择了table中的某一个数据时,comboBox的值始终是选中的数据所在行的第一列。晕,只好采用原始的下拉列表来做了。

效果图大致如下:我们先选择一个XML文件,这个XML文件就是我们前面一篇博客中生成的。此时,XML文件会被解析并将结果写入两个TXT文件中,在通过加载按钮将文件内容展示出来。这里我们使用了文件来做一次中转,实际上不用文件,也完全可以直接将XML的内容解析出来并展示,但是生成一个本地文件有个好处就是:我们不用每次打开程序都要去解析这个XMl文件,只要这个XML文件没有发生变化,我们就可以只解析一次,后面都直接读取文件即可。

Qt使用XML设计多级关联菜单_第1张图片
Qt使用XML设计多级关联菜单_第2张图片

Qt使用XML设计多级关联菜单_第3张图片

程序源代码:
ParseXML.h

#ifndef PARSEXML_H
#define PARSEXML_H

//#include "ComboBoxTableView.h"
#include "TableModel.h"
#include "TableView.h"

#include 
#include 
#include 
#include 
#include 
#include 

namespace Ui {
class ParseXML;
}

class ParseXML : public QMainWindow
{
    Q_OBJECT

public:
    explicit ParseXML(QWidget *parent = 0);
    ~ParseXML();

    void ParseProvincesElement(const QDomElement &rootElement);
    void ParseCityElement(const QDomElement &element);
    void ParseDistrictElement(const QDomElement &element);

    void WriteFile(const QString &fileName, const QStringList &list);
    void WriteFile(const QString &fileName, const QList &list);

    void ReadFile();

//    void LoadCity();
//    void LoadDistrict();

    void LoadComboBox(QComboBox *comboBox, const QStringList &list);

private slots:
    void on_btn_choose_file_clicked();

    void on_btn_load_clicked();

    void currentIndexChanged_comboBox_1(const QString &text);

    void currentIndexChanged_comboBox_2(const QString &text);
private:
    Ui::ParseXML *ui;

    QStringList provincesList;
    QStringList cityList;
    QStringList districtList;

    QStringList stringList_provinces_city_district;
    QList list_provinces_city_district;

    QString provinceName;
    QStringList provinceList;

//    TableModel *model;
//    TableView *view;
};

#endif // PARSEXML_H

ParseXML.cpp

#include "ParseXML.h"
#include "ui_ParseXML.h"

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

ParseXML::ParseXML(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::ParseXML)
{
    ui->setupUi(this);

    connect(ui->comboBox_1,
            static_cast(&QComboBox::currentIndexChanged),
            this,
            &ParseXML::currentIndexChanged_comboBox_1);
    connect(ui->comboBox_2,
            static_cast(&QComboBox::currentIndexChanged),
            this,
            &ParseXML::currentIndexChanged_comboBox_2);

//    model = new TableModel();
//    view = new TableView(ui->comboBox_1);

    //connect(view, SIGNAL(currentChanged(int)), this, SLOT(currentChangedFromView(int)));

    //隐藏表头
//    view->horizontalHeader()->setVisible(false);
//    view->verticalHeader()->setVisible(false);
//    view->setAlternatingRowColors(true);
//    view->resizeColumnsToContents();
}

ParseXML::~ParseXML()
{
    delete ui;
}

void ParseXML::on_btn_choose_file_clicked()
{
    QString path = QFileDialog::getOpenFileName(this, QStringLiteral("选择文件"),
                                                QCoreApplication::applicationDirPath(),
                                                tr("XML files (*.xml)"));
    if (path.isEmpty())
    {
        return;
    }

    QFile file(path);
    if (!file.open(QIODevice::ReadOnly))
    {
        QMessageBox::critical(this, tr("Read File"), tr("Connot open file : \\n%1").arg(path));
        return;
    }

    QString errorMsg;
    int errorLine;
    int errorColumn;

    QDomDocument doc;
    if (!doc.setContent(&file, false, &errorMsg, &errorLine, &errorColumn))
    {
        QMessageBox::critical(this, tr("wrong"), tr("Parse error at line %1,column %2 : %3")
                              .arg(errorLine).arg(errorColumn).arg(errorMsg));
        return;
    }

    QDomElement rootElement = doc.documentElement();
    ParseProvincesElement(rootElement);

    file.close();

    //开始写文件
    WriteFile("provinces.txt", provinceList);
    WriteFile("provinces_city_district.txt", list_provinces_city_district);

}

void ParseXML::ParseProvincesElement(const QDomElement &rootElement)
{
    QDomNode provincesNode= rootElement.firstChild();//注意QDomNode::firstChild()和QDomNode::nextSibling()都是QDomNode成员函数,返回值都是QDomNode

    stringList_provinces_city_district.clear();
    provinceList.clear();

    while(!provincesNode.isNull())
    {
        if (provincesNode.toElement().tagName() == QStringLiteral("省"))
        {
            provinceName = provincesNode.toElement().attribute("name").trimmed();

            stringList_provinces_city_district << provinceName << "\n";
            provinceList << provinceName;
            ParseCityElement(provincesNode.toElement());
        }

        list_provinces_city_district << stringList_provinces_city_district;
        stringList_provinces_city_district.clear();

        provincesNode = provincesNode.nextSibling();
    }
}

void ParseXML::ParseCityElement(const QDomElement &element)
{
    QDomNode cityNode = element.firstChild();
    QString cityName_original;
    QString cityName_addcomma;

    while (!cityNode.isNull())
    {
        if (cityNode.toElement().tagName() == QStringLiteral("市"))
        {
            cityName_original = cityNode.toElement().attribute("name").trimmed();
            cityName_addcomma = "," + cityName_original;

//            stringList_provinces_city_district.removeLast();
//            stringList_provinces_city_district << "\n";
            stringList_provinces_city_district << cityName_original;

            //stringList_provinces_city << cityName_addcomma;
            //strList_city_district << cityName_original;

            ParseDistrictElement(cityNode.toElement());
        }
        cityNode = cityNode.nextSibling();
    }
}

void ParseXML::ParseDistrictElement(const QDomElement &element)
{
    QDomNode districtNode = element.firstChild();
    QString districtName_addcomma;

    while (!districtNode.isNull())
    {
        if (districtNode.toElement().tagName() == QStringLiteral("区"))
        {
            districtName_addcomma = "," + districtNode.toElement().text().trimmed();
            stringList_provinces_city_district << districtName_addcomma;
        }
        districtNode = districtNode.nextSibling();
    }
    stringList_provinces_city_district << "\n";
}

//重载函数
void ParseXML::WriteFile(const QString &fileName, const QStringList &list)
{
    Q_ASSERT(!fileName.isEmpty());

    QString path = QCoreApplication::applicationDirPath() + QStringLiteral("/XML数据/") + fileName;
    QFile file(path);

    if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
    {
        return;
    }

    QTextStream out(&file);

    for (int i = 0; i < list.size(); i++)
    {
        out << list.at(i) << "\n";
    }
    file.close();
}

//重载函数
void ParseXML::WriteFile(const QString &fileName, const QList &list)
{
    Q_ASSERT(!fileName.isEmpty());

    QString path = QCoreApplication::applicationDirPath() + QStringLiteral("/XML数据/") + fileName;
    QFile file(path);
    QStringList strList;

    if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
    {
        return;
    }

    QTextStream out(&file);

    for (int i = 0; i < list.size(); i++)
    {
        strList = list.at(i);
        for (int j = 0; j < strList.size(); j++)
        {
            out << strList.at(j);
        }
        out << "\n";
    }
    file.close();
}

void ParseXML::ReadFile()
{
    QString path = QCoreApplication::applicationDirPath() + QStringLiteral("/XML数据/") + QString("provinces.txt");
    QFile file(path);

    if (!file.open(QIODevice::ReadOnly))
    {
        return;
    }

    QTextStream in(&file);
    while (!in.atEnd())
    {
        ui->comboBox_1->addItem(in.readLine());
    }
    ui->comboBox_1->setCurrentIndex(0);

    //int row = ((provincesTableList.count() % 4) == 0) ? (provincesTableList.count()/4):((provincesTableList.count()/4) + 1);
    //model->List2Table(provincesTableList, 9, 4);

    file.close();

//    ui->comboBox_1->setModel(model);
//    ui->comboBox_1->setView(view);

    //注意到QTableView类还有一个成员方法:setColumnWidth(),显然是用来设置表格列宽的。但是,这里有个要求。
    //所有对setColumnWidth()的调用都要放在setModel()之后。如果在设置View类的Model之前就调用该方法来设置列宽,是不会起作用的。
//    view->setColumnWidth(0, 100);
//    view->setColumnWidth(1, 100);
//    view->setColumnWidth(2, 100);
//    view->setColumnWidth(3, 100);
//    view->horizontalHeader()->setStretchLastSection(true);
}

void ParseXML::on_btn_load_clicked()
{
    ReadFile();
}

//参数text即为选中的省的名称
void ParseXML::currentIndexChanged_comboBox_1(const QString &text)
{
    QString path = QCoreApplication::applicationDirPath() + QStringLiteral("/XML数据/") + QString("provinces_city_district.txt");
    QFile file(path);
    QString result;
    QStringList list;

    if (!file.open(QIODevice::ReadOnly))
    {
        return;
    }

    cityList.clear();

    QTextStream in(&file);

    while (!in.atEnd())
    {
        result = in.readLine();
        if (result == text)
        {
            while (!result.isEmpty())
            {
                result = in.readLine();
                list = result.split(",");
                if (!(list.at(0)).isEmpty())
                {
                    cityList.append(list.at(0));
                }
            }
            break;  //提前结束循环,防止不必要的循环
        }
    }

    file.close();

    LoadComboBox(ui->comboBox_2, cityList);
}

//参数text即为选中的市的名称
void ParseXML::currentIndexChanged_comboBox_2(const QString &text)
{
    QString province_name = ui->comboBox_1->currentText();

    QString path = QCoreApplication::applicationDirPath() + QStringLiteral("/XML数据/") + QString("provinces_city_district.txt");
    QFile file(path);
    QString result;
    QStringList list;

    if (!file.open(QIODevice::ReadOnly))
    {
        return;
    }

    districtList.clear();

    QTextStream in(&file);
    while (!in.atEnd())
    {
        result = in.readLine();
        if (result == province_name)
        {
            while (!result.isEmpty())
            {
                result = in.readLine();
                list = result.split(",");
                if (list.at(0) == text)
                {
                    for (int i = 1; i < list.size(); i++)
                    {
                        districtList.append(list.at(i));
                    }
                }
            }
            break;
        }
    }
    file.close();

    LoadComboBox(ui->comboBox_3, districtList);
}

void ParseXML::LoadComboBox(QComboBox *comboBox, const QStringList &list)
{
    comboBox->clear();
    for (int i = 0; i < list.size(); i++)
    {
        comboBox->addItem(list.at(i));
    }
    comboBox->setCurrentIndex(0);
}

项目工程上传至GitHub,下载地址:https://github.com/Hhuazai/Multilevel-menu
工程环境:windows 、 Qt Creator

你可能感兴趣的:(QT)