Qt/C++ - Lesson 006. QSqlQueryModel – Tables in Qt with SQL-query

QSqlQueryModel, qsqlquerymodel example, qt, qt таблицы, query

Using QSqlQueryModel to form tables in Qt is the most hardcore version with the lowest level of abstraction than the use QSqlTableModel or QSqlRelationalTableModel . But with all this, and the most flexible option, which requires a deeper knowledge of the language of SQL-queries. In a previous article , two tables were constructed:

  • The main, which were columns Дата, Время, Имя Хоста, IP-адрес.
  • Table devices , which were columns Имя Хоста and IP-адрес.

In the main table in the columns of the host name and IP-address points ID-devices, which carried out a substitution of data from the device tables. In this paper, the structure of the first table and therefore the table will look different:

  • The main, which were columns Дата, Время, ID устройства.
  • Table devices , which were columns Имя Хоста and IP-адрес.

The Generating of the main table in the Application will be SQL-query and the table will be a column respectively Дата, Время, Имя Хоста and IP-адрес.

Project structure

QSqlQueryModel project structure remains the same as in the previous article .

mainwindow.ui

Tables are also used are the same as in the previous article, we will remind ourselves of their names:

  • tableView
  • tableViewDevice

main.cpp

The file used in the project, being created by default.

#include "mainwindow.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();

    return a.exec();
}

mainwindow.h

This file connects library QSqlQueryModel, while QSqlRelationalTableModel classes, QSqlRelationalDelegate QSqlRelation and will not be used. Also change the method signature initialization models as object QSqlQueryModel no method to install the table name, to which will access the property.

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QSqlQueryModel>

/* Сonnecting the header file for the database */
#include "database.h"

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

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

private:
    Ui::MainWindow  *ui;
    /* The project uses objects to interact with the database 
     * and model representation database table
     * */
    DataBase                    *db;
    QSqlQueryModel              *modelMain;
    QSqlQueryModel              *modelDevice;

private:
    /* Also present are two methods that form the model 
     * and appearance TableView
     * */
    void setupMainModel(const QStringList &headers);
    void setupDeviceModel(const QStringList &headers);
    void createUI();
};

#endif // MAINWINDOW_H

mainwindow.cpp

The main difference between the code in the source file of the code from the previous article is that the methods used to initialize models SQL-query that is performed setQuery() method.

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    this->setWindowTitle("QSqlQueryModel Example");
    /* The first step is to create an object for the database 
     * and initialize the database connection
     * */
    db = new DataBase();
    db->connectToDataBase();

    /* After that produce content database tables content 
     * to be displayed in the tableView and tableViewDevice
     * */
    for(int i = 1; i < 4; i++){
        QVariantList data;
        data.append("Device " + QString::number(i));
        data.append("172.168.13." + QString::number(i));
        db->inserIntoDeviceTable(data);
    }

    for(int i = 0; i < 10; i++){
        QVariantList data;
        QString random = QString::number(qrand() % ((4 + 1) - 1) + 1);
        data.append(QDate::currentDate());
        data.append(QTime::currentTime());
        data.append(random);
        db->inserIntoMainTable(data);
    }

    /* Initialize the model to represent the data indicating 
     * the names of the columns
     * */
    this->setupMainModel(QStringList() << trUtf8("id")
                                       << trUtf8("Дата")
                                       << trUtf8("Время")
                                       << trUtf8("Имя хоста")
                                       << trUtf8("IP адрес")
               );

    this->setupDeviceModel(QStringList() << trUtf8("id")
                                         << trUtf8("Имя хоста")
                                         << trUtf8("IP адрес")
               );

    this->createUI();
}

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

void MainWindow::setupMainModel(const QStringList &headers)
{
    /* Initialization of data representation model
     * */
    modelMain = new QSqlQueryModel(this);

    modelMain->setQuery("SELECT "
                        TABLE ".id, "
                        TABLE "." TABLE_DATE ", "
                        TABLE "." TABLE_TIME ", "
                        DEVICE "." DEVICE_HOSTNAME ", "
                        DEVICE "." DEVICE_IP
                        " FROM " TABLE ", " DEVICE
                        " WHERE " DEVICE ".id = " TABLE "." TABLE_DEVICE_ID
                        " ORDER BY " TABLE "." TABLE_DATE " DESC , " TABLE "." TABLE_TIME " DESC"
                        );

    /* Set the columns names in a table with sorted data
     * */
    for(int i = 0, j = 0; i < modelMain->columnCount(); i++, j++){
        modelMain->setHeaderData(i,Qt::Horizontal,headers[j]);
    }
}

void MainWindow::setupDeviceModel(const QStringList &headers)
{
    /* Initialization of data representation model
     * */
    modelDevice = new QSqlQueryModel(this);

    modelDevice->setQuery("SELECT "
                        DEVICE ".id, "
                        DEVICE "." DEVICE_HOSTNAME ", "
                        DEVICE "." DEVICE_IP
                        " FROM " DEVICE
                        );
    /* Set the columns names in a table with sorted data
     * */
    for(int i = 0, j = 0; i < modelDevice->columnCount(); i++, j++){
        modelDevice->setHeaderData(i,Qt::Horizontal,headers[j]);
    }
}

void MainWindow::createUI()
{
    ui->tableView->setModel(modelMain);    
    ui->tableView->setColumnHidden(0, true);    
    ui->tableView->setSelectionBehavior(QAbstractItemView::SelectRows);
    ui->tableView->setSelectionMode(QAbstractItemView::SingleSelection);
    ui->tableView->resizeColumnsToContents();
    ui->tableView->setEditTriggers(QAbstractItemView::NoEditTriggers);
    ui->tableView->horizontalHeader()->setStretchLastSection(true);

    ui->tableViewDevice->setModel(modelDevice);     
    ui->tableViewDevice->setColumnHidden(0, true);   
    ui->tableViewDevice->setSelectionBehavior(QAbstractItemView::SelectRows);
    ui->tableViewDevice->setSelectionMode(QAbstractItemView::SingleSelection);
    ui->tableViewDevice->resizeColumnsToContents();
    ui->tableViewDevice->setEditTriggers(QAbstractItemView::NoEditTriggers);
    ui->tableViewDevice->horizontalHeader()->setStretchLastSection(true);
}

To update the data in the table, it is necessary to repeat the SQL-query. This is done by calling query() , which is used lastQuery() method, as shown in the code below:

model->setQuery(modelDevice->query().lastQuery());

database.h

The header file for the class, projecting facade for working with database change only two lines, compared to the previous article . Changes are shown in the code below:

/* Directives of table names, fields, tables, and databases */
#define DATABASE_HOSTNAME   "ExampleDataBase"
#define DATABASE_NAME       "DataBase.db"

#define TABLE                   "MainTable"
#define TABLE_DATE              "Date"
#define TABLE_TIME              "Time"
#define TABLE_DEVICE_ID         "DeviceID"

#define DEVICE                  "DeviceTable"
#define DEVICE_IP               "IP"
#define DEVICE_HOSTNAME         "Hostname"

database.cpp

In this file are subject to change only two methods that work with the main table. Due to the fact that the changed number of columns which are used in this table.

/* Method for creating the main table in the database
 * */
bool DataBase::createMainTable()
{
    /* In this case, a forming raw SQL-query with its subsequent execution.
     * */
    QSqlQuery query;
    if(!query.exec( "CREATE TABLE " TABLE " ("
                            "id INTEGER PRIMARY KEY AUTOINCREMENT, "
                            TABLE_DATE      " DATE            NOT NULL,"
                            TABLE_TIME      " TIME            NOT NULL,"
                            TABLE_DEVICE_ID " INTEGER         NOT NULL"
                        " )"
                    )){
        qDebug() << "DataBase: error of create " << TABLE;
        qDebug() << query.lastError().text();
        return false;
    } else {
        return true;
    }
    return false;
}

/* The method for inserting entries in the main table
 * */
bool DataBase::inserIntoMainTable(const QVariantList &data)
{
    /* SQL Query formed from QVariantList, 
     * which are transmitted in data to be inserted into the table.
     * */
    QSqlQuery query;
    /* At the beginning of the SQL query is generated with the keys, 
     * which are then linked bindValue method for substituting data from QVariantList
     * */
    query.prepare("INSERT INTO " TABLE " ( " TABLE_DATE ", "
                                             TABLE_TIME ", "
                                             TABLE_DEVICE_ID " ) "
                  "VALUES (:Date, :Time, :ID )");
    query.bindValue(":Date",        data[0].toDate());
    query.bindValue(":Time",        data[1].toTime());
    query.bindValue(":ID",          data[2].toInt());

    if(!query.exec()){
        qDebug() << "error insert into " << TABLE;
        qDebug() << query.lastError().text();
        return false;
    } else {
        return true;
    }
    return false;
}

Result

The visual result you should not be different from what you received in the previous lesson. It will box with two tables.

We recommend hosting TIMEWEB
We recommend hosting TIMEWEB
Stable hosting, on which the social network EVILEG is located. For projects on Django we recommend VDS hosting.
Support the author Donate

Какаим образом можно вставить в базу даннных сразу несколько строк? Ситуация такая - на COM-порт приходят данные, непрерывно и поступают в парсер. Там, после обработки записываются в QVariantList (как в примере и далее в БД), но из-за того, что данные идут непрерывно, база данных постоянно занята и обратиться к ней невозможно, пока не отключен порт. Какаим образом в QVariantList можно записать сразу несколько строк, а потом все их отправить в БД?

Тут скорее нужно правильно написать сырой запрос и использовать QSqlQuery

Например, если есть несколько инсертов

INSERT INTO MyTable VALUES ("John", 123, "Lloyds Office");
INSERT INTO MyTable VALUES ("Jane", 124, "Lloyds Office");
INSERT INTO MyTable VALUES ("Billy", 125, "London Office");
INSERT INTO MyTable VALUES ("Miranda", 126, "Bristol Office");

То нужно написать соответсвующий запрос

INSERT INTO table1 (First, Last)
VALUES
    ('Fred', 'Smith'),
    ('John', 'Smith'),
    ('Michael', 'Smith'),
    ('Robert', 'Smith');

Не уверен, что здесь поможет bind, скорее придётся подготовить аналогичную строку с использованием QVariantList и подать её в QSqlQuery.

Как можно динамически отображать данные в таблице? На COM-порт непрерывно приходят данные, я их принимаю сохраняю в БД, а после остановка приема/передачи данные отображаются в таблице. В таблице, которая нас интересует нужно отображать iD с которых приходят данные и количество пакетов, которое от них пришло. Пытаюсь сделать такvoid

MainWindow::newCountID(QByteArray refreshValueArr)

{
    QString id11;//Строки значений ID1, ID2 и пришедших значений
    int can1count, can2count;//Значения строк Count of Messages

    int can = refreshValueArr[0];//Значение CAN
    int sizeArr = refreshValueArr.size();

    sizeArr--;

    QByteArray idnew = refreshValueArr.remove(0, 1);//Массив пришедшего ID
    QString strId    = idnew.toHex().toUpper();     //Строка пришедшего ID

    qDebug()<<"can"<<can;
    qDebug()<<"strId****************"<<strId;

    int j = 0, i = 0;

    qDebug()<<"modelMainCAN1->rowCount()"<<modelMainCAN1->rowCount();
    if(modelMainCAN1->rowCount() > 0)//Если значения в строки уже записаны
    {
        for(i; i < modelMainCAN1->rowCount(); i++, qDebug()<<"---i---"<<i)
        {
               id11 = ui->tableViewCAN1->model()->data(//Получаем значения ячеек столбца ID
                           ui->tableViewCAN1->model()->index(i, 0)).value<QString>();
               qDebug()<<"id11------------------- "<<id11;
                if(id11 == strId)//Если строка ID такая же как уже существующая
                {
                    if(i > 0)
                    {
                        can1count = ui->tableViewCAN1->model()->data(//Получаем значения ячеек столбца Message                                                                                                                                                 
                                             ui->tableViewCAN1->model()->index(i, 2)).value<int>();//of count (то, что нужно 
                                                                                                                                          //поменять)

                            can1count++;//Меняем

                            ui->tableViewCAN1->model()->data(//Изменяем значения ячеек столбца Message of count
                            ui->tableViewCAN1->model()->index(i, 2)).setValue(can1count);//Вставляем
                            //Но не вставляется, во всяком случае на виджете не отображается, но сама форма 
                            //начинает тормозить
                        }
                        else//если такого значения нет (первый запуск) его нужно добавить
                        {
                            modelMainCAN1->insertRow(modelMainCAN1->rowCount(QModelIndex()));
                        }
                    }break;//Выходим из цикла
                }
            }


            else//если записей в таблице пока нет (но это второстепенно)
            {
                ui->tableViewCAN1->setModel(modelMainCAN1);
                //modelMainCAN1->insertRow();
            }

        
}

Это запись метода которая работает параллельно с БД, данные из парсера поступают в БД и в наш метод одновременно

Может вам из того метода кидать сигнал о получении новых данных и делать апдейт таблицы по тому сигналу, чтобы была новая выборка из БД для обновления таблицы?

Сигнал кидайте в конце этого метода, который Вы привели.

Не подскажете как обновить данные в ячейке? Допустим есть строка, состоящая из 2-х столбцов если данные в первом столбце совпадают с пришедшими к значению во втором столбце просто прибавляется 1. Сигнал послал, данные принял, сравнил с данными в столбце №1, а вот значения в ячейке таблицы обновить не получается.

Постоянно обращаться к БД для обновления таблицы не получится, т.к. данные идут непрерывно.

Я так полагаю, что Вам нужно тогда перпроектировать модель данных приложения.

Базу данных используйте только как хранилище, а при динамическом изменении данных, которое у вас имеется используйте модель данных, наследованную от QAbstractItemModel. Там используйте вектор со структурами строки ваших данных. При получении данных, вставляйте запись в БД и в модель данных.

Единственный коннект модели к базе данных будет через сырую выборку SQL-запросом всех необходимых данных, и новое заполнения вектора данных в модели.

Тогда модель данных не будет постоянно обращаться к БД.

При вставке новый строк обычно используют методы beginInsertRows, endInsertRows и т.д, тогда модель будет уведомлять представление об изменениях и Table View будет автоматически перерисовываться. Также метод setData нужно использовать для обновления ячеек в таблице.

Просто то, что вам требуется, это более кастомная модель данных, а не так, которая по видимому у вас используется. То есть QSqlQueryModel здесь уже не очень-то подойдёт.

Тем более, что это ReadOnly модель. Она в своём базовом функционале не позволяет записывать или обновлять данные через себя.


ReadOnly - теперь ясно. Спасибо, ща буду переписывать.

Из разряда "а вдруг кому-нибудь пригодится"

Для динамического обновления таблицы по мере поступления в нее данных я воспользовался контейнером QMap, т.к. у меня есть ID - они выполняют роль ключей и обновляющиеся значения (количество сообщений, которые пришли на этот ID).

Сначала пишем новый класс унаследованный от QAbstractTableMode l:

.h

#include <QMap>
#include <QDebug>
#include <QThread>
#include <QAbstractTableModel>

class MapModel : public QAbstractTableModel
{
    Q_OBJECT

public:
    enum MapRole
    {
        KeyRole,
        ValueRole
    };
    explicit MapModel(QObject *parent = nullptr);
    int rowCount(const QModelIndex& parent = QModelIndex()) const;
    int columnCount(const QModelIndex& parent = QModelIndex()) const;
    QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
    QVariant headerData(int section, Qt::Orientation orientation, int role) const;
    inline void setMap(QMap<QString, int>* map){map6 = map;}

private:
    QMap<QString, int>* map;
};

.cpp

#include "mapmodel.h"

MapModel::MapModel(QObject *parent) : QAbstractTableModel(parent)
{
    map = nullptr;
}

int MapModel::rowCount(const QModelIndex& parent) const
{
    if(map)
        return map6->count();
    return 0;
}

int MapModel::columnCount(const QModelIndex& parent) const
{
    return 3;
}

QVariant MapModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    if (role != Qt::DisplayRole)
           return QVariant();

    if (orientation == Qt::Horizontal)
    {
        switch (section)
        {
            case 0:
                return QString("ID_Device");

            case 1:
                return QString("Number of Messages");

            default:
                return QVariant();
        }
    }
    return QVariant();
}

QVariant MapModel::data(const QModelIndex& index, int role) const
{
    if(!map)
        return QVariant();
    if(index.row() < 0 || index.row() >= map6->count() || role != Qt::DisplayRole)
    {
        return QVariant();
    }

    if(index.column() == 0)
    {
        return map->keys().at(index.row());
    }
    if(index.column() == 1)
    {
        return map->values().at(index.row());
    }
    return QVariant();
}

В методе, в котором идет обработка пришедших данных, в нужном месте прописываем какой-нибудь сигнал, который принимает необходимые нам данные, в моем случае это QByteArray

signalToMain(QByteArray arr);

Коннектим наш сигнал со слотом:

connect(parserClass, SIGNAL(signalToModelMain(QByteArray)), this, SLOT(slotFromParser(QByteArray)));

Наш слот

void MainWindow::slotFromParser(QByteArray array)
{
    static QMap<QString, int>map1;//Маппер для таблицы 

    QString id11;//Строки значений ID1

    QString strId    = array.toHex().toUpper();     //Строка пришедшего ID

    qDebug()<<"strId****************"<<strId;

    int  rows = 1;//Начальное значение QMap впервые пришедших ключа и значения

    if(map1.contains(strId))//Если контейнер содержит строку(ключ) аналогичную пришедшей
    {
        foreach(QString key, map1.keys()) //Я знаю что foreach лучше не использовать, "горбатого могила"))
        {
            int val = map1.value(key);//Получаем значение соответствующее ключу
            qDebug()<<"val" << val;

            if(strId == key)//Если пришедшая строка равна ключу на определенной позиции
            {
                ++val;//Инкрементируем счетчик
                qDebug()<<"val++1" << val;
                map1.insert(strId, val);//Вставляем в контейнер полученные значения
                break;
            }
        }
     }
     else
     {
         map1.insert(strId, rows);//Вставляем начальное значение
     }
     qDebug()<<"map1------ "<<map1;
     mapMod->setMap(&map1);//Создаем модель
     ui->tableView->setModel(mapMod);//Вставляем модель в таблицу
     ui->tableView->reset();//Перезагружаем таблицу
}

, где mapMod:

mainwindow.h

MapModel   *mapMod;

mainwindow.cpp

mapMod = new MapModel();
#include "mapmodel.h"

MapModel::MapModel(QObject *parent) : QAbstractTableModel(parent)
{
    map6 = nullptr;
}

int MapModel::rowCount(const QModelIndex& parent) const
{
    if(map6)
        return map6->count();
    return 0;
}

int MapModel::columnCount(const QModelIndex& parent) const
{
    return 3;
}

QVariant MapModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    if (role != Qt::DisplayRole)
           return QVariant();

    if (orientation == Qt::Horizontal)
    {
        switch (section)
        {
            case 0:
                return QString("ID_Device");

            case 1:
                return QString("Number of Messages");

            default:
                return QVariant();
        }
    }
    return QVariant();
}

QVariant MapModel::data(const QModelIndex& index, int role) const
{
    if(!map6)
        return QVariant();
    if(index.row() < 0 || index.row() >= map6->count() || role != Qt::DisplayRole)
    {
        return QVariant();
    }

    if(index.column() == 0)
    {
        return map6->keys().at(index.row());
    }
    if(index.column() == 1)
    {
        return map6->values().at(index.row());
    }
    return QVariant(); 

} не получается подредактировать

private:
    QMap<QString, int>* map6;



В Qt 5.11. при попытке вставить в БД запись выдает ошибку

QSqlQuery::prepare: database not open

QSqlDatabasePrivate::database: requested database does not belong to the calling thread.
как с этим бороться?




Попробуйте передать инстанс базы данных в конструктор QSqlQuery

QSqlQuery q(db);

Не получается

bool DataBase::insertDataIntoDB(QVariantList data)
{
    QSqlQuery query(db);
    QString   str;
    qDebug()<<"InsertInsertInsertInsertInsert"<<QThread::currentThreadId();;

    int count = data.count();
    int N = count/5;
    qDebug()<<"N"<<N;

    str = "INSERT INTO DataBase    ("
                       "DTTable,    "
                       "CANTable,   "
                       "ID_Device,  "
                       "DlcCan,     "
                       "Message    )"
          "VALUES (?, ?, ?, ?, ?)";
    QSqlDatabase::database().transaction();
    for(int i = 0, j = 0; i < N; i++)
    {
        query.prepare(str);
        query.addBindValue(data[j]);
        query.addBindValue(data[++j]);
        query.addBindValue(data[++j]);
        query.addBindValue(data[++j]);
        query.addBindValue(data[++j]);
        query.exec();
        ++j;
    }
    query.exec();
    //qDebug()<<data;

    QSqlDatabase::database().commit();

    if(!query.exec())
    {
        qDebug()<<"Error insert into DataBase";
        qDebug()<<query.lastError().text();
        return false;
    }
    else
    {
        qDebug()<<"Insert compleated";
        return true;
    }
}





Как-то даже странно, а вы что ли в отдельный поток убрали базу данных? То есть изначально инстанс создаётся в одном потоке, а все QSqlQuery в другом потоке? Они должны находиться в одном потоке так-то.

Comments

Only authorized users can post comments.
Please, Log in or Sign up
Donate

Hello, Dear Users of EVILEG!!!

If the site helped you, then support the development of the site financially, please.

You can do it by following ways:

Thank you, Evgenii Legotckoi

AT
Dec. 10, 2019, 8:06 a.m.
Anastasija Troschenkova

C++ - Test 001. The first program and data types

  • Result:60points,
  • Rating points-1
AT
Dec. 10, 2019, 8:02 a.m.
Anastasija Troschenkova

Qt - Test 001. Signals and slots

  • Result:73points,
  • Rating points1
g
Dec. 10, 2019, 4:51 a.m.
gisLu

C ++ - Test 004. Pointers, Arrays and Loops

  • Result:40points,
  • Rating points-8
Last comments
Dec. 9, 2019, 3:41 a.m.
Evgenij Legotskoj

Эта ошибка invalid use of incomplete type ‘class Ui::AnotherWindow’ обычно говорит о том, что не найдено определение класса или структуры. Типичная проблема - не подключён заголовочны…
NB
Dec. 9, 2019, 3:36 a.m.
Nikolaj Batmanov

Ну, не настолько со мной всё полхо...))) Вроде бы. Я ж кнопки отрисовываю.
Dec. 9, 2019, 3:14 a.m.
Evgenij Legotskoj

Добрый день. У вас ui файлов по ходу нет. UI файлы используются для вёрстки в графическом дизайнере.
NB
Dec. 9, 2019, 3:05 a.m.
Nikolaj Batmanov

Здравствуйте! Полностью скопировал ваш пример к себе, чтобы разобраться. А он не хочет запускаться, дает ошибку: invalid use of incomplete type ‘class Ui::AnotherWindow’ ui(new Ui…
Dec. 8, 2019, 7:23 a.m.
Evgenij Legotskoj

У меня здесь есть одна старая статья с примером векторного редактора. Там есть ответы на ваши вопросы. Поизучайте Qt/C++ - Урок 072. Пример векторного редактора на Qt QGraphicsItem, QG…
Now discuss on the forum
MU
Dec. 11, 2019, 8:27 a.m.
Maciej Urmański

Thank you! Now works, and this is solution. num_embed = Embed.objects.filter(added_by=recipe.added_by).count()
Dec. 11, 2019, 8:12 a.m.
Mihailll

Так работает. Взял этот пример https://api-2d3d-cad.com/face_recognition_with_opencv/ void MainWindow::on_pushButton_4_clicked() //фото определение лица{ // Load Face cascade (.xml…
TD
Dec. 10, 2019, 4:14 a.m.
Timur Dosov

Спасибо, работает. А ещё вопрос: как загрузить страницу с динамической подгрузкой контента по скроллингу? Например - [https://ntvplus.ru/tv/]. Пока делаю через костыль - QApplication::s…
Dec. 9, 2019, 7:16 a.m.
qml_puthon_user

Я сделал простой вывод текста по испусканию сигнала... Чего не хватает программе?) Python: # системные библиотекиimport cv2import numpy as npimport sysimport threading# PyQt б…
SK
Dec. 8, 2019, 4:11 p.m.
Semen Kosandjak

інтерфейс qt, приклад того додаю на малюнку, я натискаю на кнопку і у мене з'являється 2 текст лайну в які я вводжу значення, тобто в 1 цифри,у другому випадку це літери, тобто c++, без графічно…
EVILEG
About
Services
© EVILEG 2015-2019
Recommend hosting TIMEWEB