Qt Writing Gas Safety Management System 26-Configuration Design

Posted by az_wraith on Sat, 07 Dec 2019 00:30:00 +0100

1. Preface

Configuration design is added at the request of a customer. The design concept is to provide a primary function of configuration design, such as reading the custom control dynamic library, loading all the controls into the control bar (somewhat like the qtcreator's control bar) Users drag the corresponding controls onto the canvas, automatically generate controls, which can be stretched and dragged directly on the canvasAdjust the position, set some properties of the control itself, such as various colors, and also provide user property setting function. Users can customize many properties to bind to this control. Generally speaking, a control will define some Q_PROPERTY things to expose the properties for setting, but after all, it is limited, most of the time users want to except these fromIn addition to the properties of the tape itself, you can customize the properties of some users to bind to this control, such as device number, device name, and so on. The information needs to be saved, automatically loaded and applied next time.

Weak attribute mechanism is very powerful in Qt. This kind of user attributes processing must use weak attributes. Weak attributes can be read directly with widget->property(name) or assigned with direct widget->setProperty(name, value). It is very convenient to use and support multiple attributes. If the name is the last one, it will be overwritten automatically. So you want to use device number, device name and so on.Once the user property is bound to the control, it can be processed directly elsewhere in the program using the property function.For this reason, I have made a custom control property designer, which supports automatic mapping of Chinese and English properties. There is a series of articles dedicated to that property designer.

Skin Open Source: https://gitee.com/feiyangqingyun/QWidgetDemo https://github.com/feiyangqingyun/QWidgetDemo File name: styledemo

Experience address: https://gitee.com/feiyangqingyun/QWidgetExe https://github.com/feiyangqingyun/QWidgetExe File name: bin_sams.zip

2. Functional features

  1. Collect data port, support serial port + network port, support free serial port + baud rate, network support free set IP address + communication port, each port supports collection cycle, default 1 second address, support set communication timeout, default 3 times, support maximum reconnection time, for re-reading offline devices.
  2. Controller information, the ability to add a controller name, select a controller address + controller model, and set the number of detectors under the controller.
  3. Detector information, can add bit number, freely select the type of detector, gas type, gas symbol, high report value, low report value, buffer value, zero value, whether enabled, alarm sound, background map, storage period, numeric conversion of decimal places, alarm delay time, alarm type (HH,LL,HL), etc.
  4. Controller model + detector model + gas type + gas symbol, can be freely configured.
  5. Maps support import and delete, and all detectors are free to drag and save map locations.
  6. Port information + Controller information + Detector information, support import export + export to excel + print.
  7. Run records + alarm records + user records, support multi-conditional combination queries, such as time period + controller + detector, all records support export to excel + print.
  8. Records exported to excel support all forms file versions such as excel+wps and do not rely on software such as excel.
  9. Data within a specified time range can be deleted, early data can be automatically cleaned up, and the maximum number of records can be saved.
  10. Supports alarm text message forwarding, supports multiple receiving mobile phone numbers, and can set the sending interval, such as sending all alarm messages instantly or once in 6 hours, the text message content is too long, and automatically splits multiple text messages.
  11. Supports alarm mail forwarding, supports multiple receiving mailboxes, and can set sending intervals, such as sending all alarm messages instantly or once in six hours, and supports attachment sending.
  12. High report color + low report color + normal color + 0 value color + curve background + curve color, etc., can be chosen freely.
  13. The Chinese title, English title, logo path and copyright of the software can be set freely.
  14. Provides switch settings for on-off operation + alarm sound + automatic logon + password remembering, etc.
  15. The alarm sound sets the number of playback times and the interface provides 17 skin file choices.
  16. Supports cloud data synchronization by setting up information about cloud databases, such as database name, user name + password, etc.
  17. Supports network forwarding and network reception. After network reception is turned on, the software receives data from udp for analysis.Network forwarding supports multiple target IP, which enables locally collected software to freely transfer data to the client and view detector data at any time.
  18. Automatically remember the user's last remaining interface + other information and apply it automatically after restart.
  19. The alarm automatically switches to the corresponding map, and the detector button flashes.
  20. Double-click the detector icon for control.
  21. Support user rights management, administrator + operator two categories, user login + user exit, can remember password and automatic login, more than three error prompts and close the program.
  22. Supports four monitoring modes, device panel monitoring + map monitoring + tabular data monitoring + curve data monitoring, can switch freely, and four simultaneous applications.
  23. Supports alarm relay linkage, a bit number can link multiple modules and relay numbers across the serial port, supporting many-to-many.
  24. Local data stores support sqlite+mysql and remote data synchronization to cloud databases.Automatic reconnection.
  25. Data collected by local devices is uploaded to the cloud in real time for extraction by other means such as mobile APP or web.
  26. Two kinds of data sources are supported, one is serial port and network to collect device data through protocol, the other is database collection.The database collection mode can be used as a common system.
  27. The built-in device emulation tool supports 16 device data emulation as well as database data emulation to test data when there is no device.
  28. The default communication protocol uses the modbus protocol, and later adds the support of Internet of Things protocols such as mqtt to make a universal system.
  29. Supports all windows operating systems + linux operating systems and other operating systems.

3. Effect Charts

4. Core Code

void frmConfigXml::initPlugin()
{
    //Load the default plug-in, debug loads the file with the end of d
    QString appPath = qApp->applicationDirPath();
#ifdef QT_NO_DEBUG
    QString str = "";
#else
    QString str = "d";
#endif

#if defined(Q_OS_WIN)
    QString fileName = QString("%1/quc%2.dll").arg(appPath).arg(str);
#elif defined(Q_OS_UNIX)
    QString fileName = QString("%1/libquc%2.so").arg(appPath).arg(str);
#elif defined(Q_OS_MAC)
    QString fileName = QString("%1/libquc%2.dylib").arg(appPath).arg(str);
#endif

    //Load default custom control plug-in
    loadPlugin(fileName);
    //Load default control xml data
    openFile(appPath + "/quc.xml");
    //Automatic scrollbar rolls to specified position
    qApp->processEvents();
    ui->listWidget->verticalScrollBar()->setValue(0);    
}

void frmConfigXml::loadPlugin(const QString &fileName)
{
    openPlugin(fileName);
}

void frmConfigXml::openPlugin(const QString &fileName)
{
    //Clear the original control first
    qDeleteAll(listWidgets);
    listWidgets.clear();
    listNames.clear();
    ui->listWidget->clear();

    //Load custom control plug-in collection information, including getting class name + Icon
    QPluginLoader loader(fileName);
    if (loader.load()) {
        QObject *plugin = loader.instance();

        //Get the plug-in containers and walk through them one by one to find the individual plug-ins
        QDesignerCustomWidgetCollectionInterface *interfaces = qobject_cast<QDesignerCustomWidgetCollectionInterface *>(plugin);
        if (interfaces)  {
            listWidgets = interfaces->customWidgets();
            int count = listWidgets.count();
            for (int i = 0; i < count; i++) {
                //Remove icon and class name
                QIcon icon = listWidgets.at(i)->icon();
                QString className = listWidgets.at(i)->name();
                QListWidgetItem *item = new QListWidgetItem(ui->listWidget);
                item->setText(className);
                item->setIcon(icon);
                listNames << className;
            }
        }

        //Get the class names of all plug-ins
        const QObjectList objList = plugin->children();
        foreach (QObject *obj, objList) {
            QString className = obj->metaObject()->className();
            //qDebug() << className;
        }
    }
}

void frmConfigXml::openFile(const QString &fileName)
{
    //Do not continue if control list does not exist
    if (ui->listWidget->count() == 0) {
        return;
    }

    //Open File
    QFile file(fileName);
    if (!file.open(QFile::ReadOnly | QFile::Text)) {
        return;
    }

    //Fill the file in the dom container
    QDomDocument doc;
    if (!doc.setContent(&file)) {
        file.close();
        return;
    }

    file.close();

    listSelect.clear();
    listUserProperty.clear();
    xmlName = fileName;

    //Clear the original control first
    QList<QWidget *> widgets = ui->centralwidget->findChildren<QWidget *>();
    qDeleteAll(widgets);
    widgets.clear();

    //First determine if the root element is correct
    QDomElement docElem = doc.documentElement();
    if (docElem.tagName() == "canvas") {
        QDomNode node = docElem.firstChild();
        QDomElement element = node.toElement();
        while(!node.isNull()) {
            //Control Name
            QString name = element.tagName();
            //Remove the index of the current control in the control list, or if it does not exist, it means that the control in the configuration file does not exist
            int index = listNames.indexOf(name);
            if (index < 0) {
                continue;
            }

            //Stores the coordinate position and width height of the control
            int x, y, width, height;
            //Store custom control properties
            QList<QPair<QString, QVariant> > propertys;
            //Store Control Custom Properties
            QStringList userProperty;

            //Node name is not empty to continue
            if (!name.isEmpty()) {
                //Traversing the attribute name and value of a node
                QDomNamedNodeMap attrs = element.attributes();
                for (int i = 0; i < attrs.count(); i++) {
                    QDomNode node = attrs.item(i);
                    QString nodeName = node.nodeName();
                    QString nodeValue = node.nodeValue();
                    //qDebug() << name << nodeName << nodeValue;

                    //Preferred coordinate + width and height attributes, which cannot be achieved by setting weak attributes
                    if (nodeName == "x") {
                        x = nodeValue.toInt();
                    } else if (nodeName == "y") {
                        y = nodeValue.toInt();
                    } else if (nodeName == "width") {
                        width = nodeValue.toInt();
                    } else if (nodeName == "height") {
                        height = nodeValue.toInt();
                    } else if (nodeName.startsWith("user-")) {
                        //Remove custom attributes at the beginning of user-
                        nodeName = nodeName.split("-").last();
                        userProperty << QString("%1|%2").arg(nodeName).arg(nodeValue);
                    } else {
                        QVariant value = QVariant(nodeValue);
                        //In order to be compatible with Qt4, rgba s of color values need to be removed separately because Qt4 does not support hexadecimal string transparency
                        //6422a3a9 This format, in turn argb with transparency, requires special handling
                        if (nodeValue.startsWith("#") && nodeValue.length() == 9) {
                            bool ok;
                            int alpha = nodeValue.mid(1, 2).toInt(&ok, 16);
                            int red = nodeValue.mid(3, 2).toInt(&ok, 16);
                            int green = nodeValue.mid(5, 2).toInt(&ok, 16);
                            int blue = nodeValue.mid(7, 2).toInt(&ok, 16);
                            value = QColor(red, green, blue, alpha);
                        }

                        propertys.append(qMakePair(nodeName, value));
                    }
                }
            }

            //qDebug() << name << x << y << width << height;

            //Instantiate controls based on different control types
            int countWidget = listWidgets.count();
            int countProperty = propertys.count();
            for (int i = 0; i < countWidget; i++) {
                QString className = listWidgets.at(i)->name();
                if (name == className) {
                    //Generate corresponding control
                    QWidget *widget = createWidget(i);
                    //Set properties of custom controls one by one
                    for (int j = 0; j < countProperty; j++) {
                        QPair<QString, QVariant> property = propertys.at(j);
                        QString name = property.first;
                        QVariant value = property.second;
                        widget->setProperty(name.toStdString().c_str(), value);
                    }

                    //Set control coordinates and width and height
                    widget->setGeometry(x, y, width, height);
                    //Instantiate the checked form to follow the control
                    newSelect(widget, userProperty);
                    break;
                }
            }

            //Move to Next Node
            node = node.nextSibling();
            element = node.toElement();
        }
    }
}

void frmConfigXml::saveFile(const QString &fileName)
{
    //Do not continue if control list does not exist
    if (ui->listWidget->count() == 0) {
        return;
    }

    QFile file(fileName);
    if (!file.open(QFile::WriteOnly | QFile::Text | QFile::Truncate)) {
        return;
    }

    //Output file as stream
    QTextStream stream(&file);

    //Building xml data
    QStringList list;

    //Add Fixed Header Data
    list << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
    //Add the canvas main tag, save the width and background pictures, and add other attributes yourself
    list << QString("<canvas width=\"%1\" height=\"%2\" image=\"%3\">")
         .arg(ui->centralwidget->width()).arg(ui->centralwidget->height()).arg("bg.jpg");

    //Find all controls from the container and save all properties of the class based on the class name of the control
    QList<QWidget *> widgets = ui->centralwidget->findChildren<QWidget *>();
    foreach (QWidget *widget, widgets) {
        const QMetaObject *metaObject = widget->metaObject();
        QString className = metaObject->className();

        //There is no need to export if the parent of the current control is not the main form, and some controls have child controls that do not need to be exported
        if (widget->parent() != ui->centralwidget || className == "SelectWidget") {
            continue;
        }

        //Store custom control properties one by one
        //MetaObject->propertyOffset() indicates that the property of the current control begins indexing, and 0 begins with the property of the parent class
        QStringList values;
        int index = metaObject->propertyOffset();
        int count = metaObject->propertyCount();
        for (int i = index; i < count; i++) {
            QMetaProperty property = metaObject->property(i);
            QString nodeName = property.name();
            QVariant variant = property.read(widget);
            QString typeName = variant.typeName();
            QString nodeValue = variant.toString();

            //If it is a color value take out the transparency together, the color value toString defaults to opacity in Qt4
            if (typeName == "QColor") {
                QColor color = variant.value<QColor>();
                if (color.alpha() < 255) {
                    //Qt4 does not support strings in HexArgb format and needs to be removed one by one to stitch together
                    //nodeValue = color.name(QColor::HexArgb);
                    QString alpha = QString("%1").arg(color.alpha(), 2, 16, QChar('0'));
                    QString red = QString("%1").arg(color.red(), 2, 16, QChar('0'));
                    QString green = QString("%1").arg(color.green(), 2, 16, QChar('0'));
                    QString blue = QString("%1").arg(color.blue(), 2, 16, QChar('0'));
                    nodeValue = QString("#%1%2%3%4").arg(alpha).arg(red).arg(green).arg(blue);
                }
            }

            //Enumeration values need to be written as strings for special handling or stored in profile data as int
            if (property.isEnumType()) {
                QMetaEnum enumValue = property.enumerator();
                nodeValue = enumValue.valueToKey(nodeValue.toInt());
            }

            values << QString("%1=\"%2\"").arg(nodeName).arg(nodeValue);
            //qDebug() << nodeName << nodeValue << variant;
        }

        //Find the index corresponding to the current control
        index = -1;
        count = listSelect.count();
        for (int i = 0; i < count; i++) {
            if (listSelect.at(i)->getWidget() == widget) {
                index = i;
                break;
            }
        }

        //You can list all user attributes in the following way and then take their values. This program is already stored in listUserProperty
        //qDebug() << widget->dynamicPropertyNames();

        //Store control user properties individually
        QStringList userProperty = listUserProperty.at(index);
        count = userProperty.count();
        for (int i = 0; i < count; i++) {
            QStringList list = userProperty.at(i).split("|");
            values << QString("user-%1=\"%2\"").arg(list.at(0)).arg(list.at(1));
        }

        //Add properties of controls on the interface one by one
        QString geometry = QString("x=\"%1\" y=\"%2\" width=\"%3\" height=\"%4\"").arg(widget->x()).arg(widget->y()).arg(widget->width()).arg(widget->height());
        QString str = QString("\t<%1 %2 %3/>").arg(className).arg(geometry).arg(values.join(" "));
        list << str;
    }

    //Add Fixed Tail Data
    list << "</canvas>";

    //write file
    QString data = list.join("\n");
    stream << data;
    file.close();
}

Topics: network Excel Database xml