Evgenii Legotckoi
Evgenii LegotckoiMay 15, 2016, 7:11 a.m.

Qt/C++ - Lesson 048. QThread — How to work with threads using moveToThread

In a previous article we are only a little touch to working with threads , and in the version that is more for the customization of thread, although it can be used to perform outside work not directly related to the thread. That is an option with inheritance from QThread and override the method run().

Now we will create ExampleObject class whose objects will be transferred to the individual threads via moveToThread() method and implemented in most of these flows. A useful work will make the slot method run() , which will be defined in this class. What is important, object class will inherit from QObject .

In order to work in the run() method can be performed in cycles, using a while loop, which will operate using variable bool m_running. For the convenience of working with this variable we define it as Q_PROPERTY . But at the end of the work will emit finished() signal.


Work algorithm

  1. Create an QThread object and the ExampleObject class object;
  2. Connect the signal QThread::started() method to ExampleObject::run() ;
  3. Connect the signal ExampleObject::finished() to slot QThread::terminate() , so that on completion of useful work to complete the flow;
  4. Set m_running variable to true, to allow the work cycle, or else this method once completed;
  5. Run stream using the method start() ;
  6. When it is necessary to complete useful work object, set the variable in m_running to false. Running run() method and the flux in which he lives the object will be completed automatically and correctly.

Project structure and appearance of the application

  • ThreadLessonTwo.pro - the profile of the project;
  • exampleobject.h - header of the object file to be transmitted in a thread;
  • exampleobject.cpp - file source object that will be passed in the thread;
  • mainwindow.h - header file of the main application window;
  • mainwindow.cpp - file source code of the main application window;
  • mainwindow.ui - file forms the main application window;
  • main.cpp - the file source code to the main function.

Attached are two objects to be determined, and the two streams. With the help of the application interface, we will show some information in the output qDebug() , and will start and stop the thread.

exampleobject.h

To begin, consider the contents of the object header file. In this file, three properties announced Q_PROPERTY :

  1. running - it is a variable, by means of which will be implemented in the control cycle a run() method and, accordingly, will affect the completion of the useful work of the object.
  2. message - a string that will be sent to output in qDebug() from the main application window
  3. message_2 - this is the second line, which will also be displayed in qDebug() , but will also be used for transmission of the second stream.
#ifndef EXAMPLEOBJECT_H
#define EXAMPLEOBJECT_H

#include <QObject>

class ExampleObject : public QObject
{
    Q_OBJECT
    // A property that controls the work of thread
    Q_PROPERTY(bool running READ running WRITE setRunning NOTIFY runningChanged)
    // The first message in the object
    Q_PROPERTY(QString message READ message WRITE setMessage NOTIFY messageChanged)
    // The second message that will be transmitted via a signal/slot to the second object
    Q_PROPERTY(QString message_2 READ message_2 WRITE setMessage_2 NOTIFY message_2Changed)

    bool m_running;
    QString m_message;
    QString m_message_2;
    int count;  

public:
    explicit ExampleObject(QObject *parent = 0);
    bool running() const;
    QString message() const;
    QString message_2() const;

signals:
    void finished();    // The signal, in which we will complete the thread after the end of the run method
    void runningChanged(bool running);
    void messageChanged(QString message);
    void message_2Changed(QString message_2);
    void sendMessage(QString message);

public slots:
    void run(); // Method with payload which can be carried out in a loop
    void setRunning(bool running);
    void setMessage(QString message);
    void setMessage_2(QString message_2);
};

#endif // EXAMPLEOBJECT_H

exampleobject.cpp

All our interest is reduced to the method run() , in which the while loop will increment the counter count and the information will be displayed with the message m_message , m_message_2 and this counter as long as the variable m_running not be exposed to a value of false. At the exit of the loop at the end of execution run() method will be released finished() signal, in which the flow is completed, which will be the object.

#include "exampleobject.h"
#include <QDebug>

ExampleObject::ExampleObject(QObject *parent) :
    QObject(parent),
    m_message(""),
    m_message_2("")
{

}

bool ExampleObject::running() const
{
    return m_running;
}

QString ExampleObject::message() const
{
    return m_message;
}

QString ExampleObject::message_2() const
{
    return m_message_2;
}

// The most important method, which will be carried out "useful" work object
void ExampleObject::run()
{
    count = 0;
    // Variable m_running responsible for the object in the stream.
    // If false, the work is completed
    while (m_running)
    {
        count++;
        emit sendMessage(m_message); 
        qDebug() << m_message << " " << m_message_2 << " " << count;
    }
    emit finished();
}

void ExampleObject::setRunning(bool running)
{
    if (m_running == running)
        return;

    m_running = running;
    emit runningChanged(running);
}

void ExampleObject::setMessage(QString message)
{
    if (m_message == message)
        return;

    m_message = message;
    emit messageChanged(message);
}

void ExampleObject::setMessage_2(QString message_2)
{
    if (m_message_2 == message_2)
        return;

    m_message_2 = message_2;
    emit message_2Changed(message_2);
}

mainwindow.h

The header of the main window of the application declared slots for processing keystrokes to record messages to the objects that will be carried out in the streams, as well as slots for starting and stopping the flow. In addition, we declare two objects ExampleObject class and two objects of QThread class.

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QThread>
#include "exampleobject.h"

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

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

private slots:
    void on_write_1_clicked();  // Slot for writind data from lineEdit_1 to the first object in the first stream
    void on_write_2_clicked();  // Slot for writind data from lineEdit_1 to the second object in the first stream
    void on_start_clicked();   
    void on_stop_clicked();     

private:
    Ui::MainWindow *ui;
    QThread thread_1;       
    QThread thread_2;       
    ExampleObject exampleObject_1;  
    ExampleObject exampleObject_2; 
};

#endif // MAINWINDOW_H

mainwindow.cpp

And now combine all of the previous code in a working application in the source code of the main program window. In the constructor of the class, as has been said in the beginning, you need to connect signals started() of the threads to the slots run() our test objects, and signals the finished(), to the slots terminate() of threads to complete the work streams at completion method run().

Also joined sending message signal exampleObject_1 object to slot installation exampleObject_2 messages. But that information can be transmitted, you need the fifth argument in the connect method to transfer the flag Qt :: DirectConnection, which would establish a direct connection of objects and allows to transmit information through a system of signals and slots .

And for the transfer of objects in the threads necessary to use the method moveToTrhead().

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

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

    connect(&thread_1, &QThread::started, &exampleObject_1, &ExampleObject::run);
    connect(&thread_2, &QThread::started, &exampleObject_2, &ExampleObject::run);

    connect(&exampleObject_1, &ExampleObject::finished, &thread_1, &QThread::terminate);
    connect(&exampleObject_2, &ExampleObject::finished, &thread_2, &QThread::terminate);

    connect(&exampleObject_1, &ExampleObject::sendMessage, &exampleObject_2, &ExampleObject::setMessage_2, Qt::DirectConnection);
    exampleObject_1.moveToThread(&thread_1);    
    exampleObject_2.moveToThread(&thread_2);
}

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

void MainWindow::on_write_1_clicked()
{
    exampleObject_1.setMessage(ui->lineEdit_1->text());
}

void MainWindow::on_write_2_clicked()
{
    exampleObject_2.setMessage(ui->lineEdit_2->text());
}

void MainWindow::on_start_clicked()
{
    exampleObject_1.setRunning(true);
    exampleObject_2.setRunning(true);
    thread_1.start();
    thread_2.start();
}

void MainWindow::on_stop_clicked()
{
    exampleObject_1.setRunning(false);
    exampleObject_2.setRunning(false);
}

Coclusion

As a result, the application can be obtained following the following conclusion qDebug(), which shows the parallel operation of facilities in the streams, as well as the transfer of information from one thread to another.

"thread 1"   ""   11422
"thread 2"   "thread 1"   11446
"thread 1"   ""   11423
"thread 2"   "thread 1"   11447
"thread 1"   ""   11424
"thread 2"   "thread 1"   11448
"thread 1"   ""   11425
"thread 2"   "thread 1"   11449
"thread 1"   ""   11426
"thread 2"   "thread 1"   11450
"thread 1"   ""   11427
"thread 2"   "thread 1"   11451

Download the project can be on the following link: Thread Lesson Two

Video

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.

Do you like it? Share on social networks!

d
  • July 10, 2017, 11:02 a.m.

У меня происходит переполнение счетчика count, появляется ошибка malloc(): memory corruption (fast). Не подскажите, как с этим бороться?

Evgenii Legotckoi
  • July 10, 2017, 11:34 a.m.

А что делали? Повторяете урок или как? Пытались просто скачать проект в конце статьи и запустить?

t
  • Aug. 7, 2017, 5:12 a.m.

Взял Ваш пример для Qt4.8 (не спрашивайте почему). переписал связь сигнал/слот след. образом:

    // Запуск выполнения метода run будет осуществляться по сигналу запуска от соответствующего потока
    QObject::connect(&thread_1,        SIGNAL(started()),
                     &exampleObject_1, SLOT(run()));
    QObject::connect(&thread_2,        SIGNAL(started()),
                     &exampleObject_2, SLOT(run()));
    //connect(&thread_1, &QThread::started, &exampleObject_1, &ExampleObject::run);
    //connect(&thread_2, &QThread::started, &exampleObject_2, &ExampleObject::run);
    // Остановка потока же будет выполняться по сигналу finished от соответствующего объекта в потоке

    QObject::connect(&exampleObject_1,        SIGNAL(finished()),
                     &thread_1, SLOT(terminate()));
    QObject::connect(&exampleObject_2,        SIGNAL(finished()),
                     &thread_2, SLOT(terminate()));
    //connect(&exampleObject_1, &ExampleObject::finished, &thread_1, &QThread::terminate);
    //connect(&exampleObject_2, &ExampleObject::finished, &thread_2, &QThread::terminate);
    // коннект для передачи данных из первого объекта в первом потоке, ко второму объекту во втором потоке
    QObject::connect(&exampleObject_1,        SIGNAL(sendMessage(QString)),
                     &exampleObject_2,        SLOT(setMessage_2(QString)));
    //connect(&exampleObject_1, &ExampleObject::sendMessage,
    //        &exampleObject_2, &ExampleObject::setMessage_2, Qt::DirectConnection);

В итоге получаю

"thread 1" "" 1752

"thread 2" "" 1747

"thread 1" "" 1753

"thread 2" "" 1748

"thread 1" "" 1754

т.е. связь между потоками не работает.


Вижу еще волшебное слово Qt::DirectConnection, пробовал добавить - нет результата

Добавил  еще в pro файл запись CONFIG += no_keywords - тоже не помогло

Что подскажите


PS: я нубер в Qt и плюсах

Evgenii Legotckoi
  • Aug. 7, 2017, 5:30 a.m.

Не вижу всего кода, по этому куску кода невозможно сказать, в чём ошибка. На первый взгляд код написан верно.

P/S/ Если соберетёсь кидать весь код, то создавайте тогда новую тему на форуме. Там есть возможность прикрепить архив с проектом к теме и сообщениям. В комментариях под статьёй не стоит постить большие куски кода.

Форум здесь .
t
  • Aug. 7, 2017, 6:48 a.m.

В остальном код как и Вашем примере

Evgenii Legotckoi
  • Aug. 7, 2017, 8:37 a.m.

Тогда мне больше нечего Вам сказать. Давно уже не работал с Qt 4.8 и приниципиально не работаю с ним сейчас.

t
  • Aug. 15, 2017, 9:49 a.m.

В qt5.6 всё нормально заработало. С 4.8 - нет

Evgenii Legotckoi
  • Aug. 15, 2017, 10:32 a.m.

Нууу... тут уже вопрос к самому Qt4.8. Если честно, идей нет, да и копаться в deprecated коде желания тоже нет.

m
  • Sept. 4, 2017, 10:57 a.m.

В моем коде с двумя потоками и выводом сообщений в textEdit при использовании Qt::DirectConnection падает в "The program has unexpectedly finished" через 10-20 сообщений.  При использовании Qt::QueuedConnection, работает.

Все же как правильно использовать ?
Нашел на другом форуме:
  • Qt::DirectConnection — сигнал обрабатывается сразу вызовом соответствующего метода слота
  • Qt::QueuedConnection — сигнал преобразуется в событие и ставится в общую очередь для обработки
  • Qt::AutoConnection — это автоматический режим, который действует следующим образом: если отсылающий сигнал объект находится в одном потоке с принимающим его объектом, то устанавливается режим Qt::DirectConnection , в противном случае — режим Qt::QueuedConnection . Этот режим ( Qt::AutoConnection ) определен в методе connection() по умолчанию.
Evgenii Legotckoi
  • Sept. 4, 2017, 2:31 p.m.

В принципе все варианты возможны, зависит от ситуации.
Наиболее правильным по максимому использовать Qt::AutoConnection. Если что-то по какой-то причине не работает, то разбираться в чём причина и возможно переходить на Qt::DirectConnection , при данном методе слот будет вызываться сразу же, юез постановки в очередь событий. Иногда может потребоваться, чтобы дать приоритет вызову слота. Ведь слоты вызываются в той последовательности, как они был подключены в коде.
Тут уже возникает вопрос в потокобезопасности. Поэтому сложный код следует блокировать мьютексами.Теми же самыми QMutexLocker и QMutex, например,

int complexFunction(int flag)
{
    QMutexLocker locker(&mutex);

    // ToDo something
    return retVal;
}
В данном случае mutex является объектов класса QMutex, который объявлен в заголовке класса. Можете проверить у себя работу вашего класса с использование QMutex и Qt::DirectConnection .
m
  • Sept. 5, 2017, 12:34 a.m.

В том и дело что хотел без мютексов обойтись. т.е. суть проблемы есть старая программа, рабочая и три потока в ней  A-B-(C1...Cn) .

A - поток основной программы;
B - поток для QModbusTcpServer;
С1...Cn - потоки опроса приборов по протоколу xxx [RTU];

Задача передать из потоков собранные данные потоков C1...Cn в один JSON контейнер и отправить в QWebSocketServer.
В QWebSocketServer создал слот (для отладки и в MainWindow) привязал сигналы из C1...Cn,  передаю простой тип QString.

Вопрос , на ваш взгляд потокобезопасно так делать или нет ? Ну и как писал Qt::DirectConnection вызывает глюк, без него работает.
P.S. Qt 5.6.1
Evgenii Legotckoi
  • Sept. 5, 2017, 2:27 a.m.

Я глубоко в дебри многопоточности не зарывался, но если разбираться с нюансами и поднять в памяти то, что мне говорили более опытные коллеги. То можно сделать следующие выводы:

  • Qt::QueuedConnection является потокобезопасным вариантом и информация не портится при передаче данных потому, что все сигналы отправляют информацию в очередь сообщений и информация обрабатывается поочерёдно. Это актуально для передачи информации между несколькими потоками.
  • Qt::DirectConnection используется тогда, когда и отправитель сигнала и получатель находятся в одном потоке, поскольку такая передача информации будет потокобезопасной. Всё равно другой код не начнёт работу с текущим слотом, пока слот не завершится. Но это не значит, что такое соединение нельзя использовать при передачи информации между потоками. Только в данном случае нужно обезопаситься мьютексами. Тем более, что их реализация позволяет это делать в одну строку в методе.
Поэтому у вас всё заработало с Qt::QueuedConnection и сломалось с Qt::DirectConnection, очевидно потоки конкурировали друг с другом и получилось, что была испорчена память.

Поэтому в вашем случае работайте без Qt::DirectConnection , и подумайте над использованием мьютексов. Если начнёт падать и с Qt::QueuedConnection , то стоит подумать над подключением мьютексов.
g
  • June 9, 2018, 7:37 a.m.

Возможно таким же способом передать туда QTcpSocket?

Evgenii Legotckoi
  • June 12, 2018, 2:42 a.m.

Да, это должно работать. Главное при передаче данных в основные потоки защищайте данные от конкурентного доступа мьютексами.

ИВ
  • March 16, 2021, 1:07 p.m.

Не получается сделать connect, получаю гору ошибок. В чем может быть дело ?

Evgenii Legotckoi
  • July 2, 2021, 6:35 a.m.

connect(&thread01, &QThread::started, Server, &myserver::run);

AC
  • Feb. 28, 2023, 4:26 a.m.
  • (edited)

Ввожу 1 в Write 1, ввожу 2 в Write 2, нажимаю Start и сразу Stop. Сначала вижу очень много одинаковых строк, а потом сообщение об ошибке. При этом программа не крашится, но предупреждение настораживает... Почему так? Неужели надо &QThread::terminate заменить на &QThread::quit ? Вот что у меня вышло после. Варнинг перестал сыпаться...
Ubuntu 22, Qt 6.

"1" "" 212463
"2" "1" 253535
"1" "" 212464
"1" "" 212465
"2" "1" 253536
"2" "1" 253537
Qt has caught an exception thrown from an event handler. Throwing
exceptions from an event handler is not supported in Qt.
You must not let any exception whatsoever propagate through Qt code.
Qt has caught an exception thrown from an event handler. Throwing
exceptions from an event handler is not supported in Qt.
You must not let any exception whatsoever propagate through Qt code.

ThreadLessonTwo problem

Evgenii Legotckoi
  • March 13, 2023, 8:08 a.m.

Вообще, правильнее использоать quit, поскольку terminate - это более жёсткий вариант, на тот случай, если поток схватил deadlock.
Так что в итоге вы правильно всё исправили. Статья старая, на тот момент я делал больше глупых ошибок :-)

AC
  • June 16, 2023, 9:55 a.m.

После того, как было сделано

exampleObject_1.moveToThread(&thread_1);

Объект exampleObject_1 стал жить в своей ните, а не в той же, где живет MainWindow

Почему же в методе MainWindow::on_write_1_clicked()
Можно обращаться к методам exampleObject_1?
Разве могут взаимодействовать объекты из разных нитей как-то, кроме как через сигнал-слоты?

AC
  • June 17, 2023, 10:03 a.m.

В продолжение моего предыдыщуего поста.

Похоже, нельзя обращаться к полям и методам объекта А из объекта Б, если они в разных нитях живут. Поэтому вместо

exampleObject_1->setRunning(false);

Следует писать

thread_1->requestInterruption();

А вместо

    while (m_running)
    {
        count++;
        emit sendMessage(m_message); // Высылаем данные, которые будут передаваться в другой поток
        qDebug() << m_message << " " << m_message_2 << " " << count;
    }
    emit finished();

Стоит писать

    while(1)
    {
        if(QThread::currentThread()->isInterruptionRequested()){
            goto exit_LABEL;
        }
        count++;
        emit sendMessage(m_message); // Высылаем данные, которые будут передаваться в другой поток
        qDebug() << m_message << " " << m_message_2 << " " << count;
    }
exit_LABEL:;
    emit finished();

И на всякий случай выскажусь по поводу конструктора ExampleObject::ExampleObject(). Сейчас он пустой, но нужно проговорить. В этом конструкторе ни в коем случае нельзя выделять память из кучи (делать new), подробности тута см "NEVER allocate heap objects (using new)".

Evgenii Legotckoi
  • June 19, 2023, 3:56 a.m.

Там всё равно есть ссылочная информация на этот объект, просто... да... всё может посыпаться в более сложной логике. Пример очень старый, может быть неактуален или в чём-то неправилен в настоящее время.

AC
  • Aug. 20, 2023, 5:52 a.m.

"Запуск выполнения метода run будет осуществляться по сигналу запуска от соответствующего потока"
Мой душнила шепчет, что корректнее было бы написать
"Запуск выполнения метода run будет осуществляться по сигналу запуска от соответствующего объекта (thread_1 или thread_2) QThread."
Почему это важно? Может показаться, что существуют какие-то статические сигналы, которые способен emit непосредственно класс QThread. По аналогии со статическими методами...
Верно?

Evgenii Legotckoi
  • Aug. 20, 2023, 9:53 a.m.

Ну да, так конечно можно и до столба докопаться, но с другой стороны точность не бывает лишней в некоторых вопросах :)

AC
  • Aug. 24, 2023, 8:49 a.m.

Вот тут можно посмотреть аналогичный современный проект (правда с одной дополнительной нитью).

AC
  • Sept. 5, 2023, 3:37 a.m.

Я поясню свой вопрос. Выше я писал
"Почему же в методе MainWindow::on_write_1_clicked()
Можно обращаться к методам exampleObject_1?
Разве могут взаимодействовать объекты из разных нитей как-то, кроме как через сигнал-слоты?"

Вот чего я боюс:
"Ещё совсем уж вскользь упомяну, что реализация SMP в процессорах Интел совершенно всепрощающа и гарантирует программисту, что все процессоры видят память одинаково, хотя и имеют раздельные кеши. То есть обеспечивают так называемую когерентность кешей процессоров в многопроцессорной системе.
Это не везде так. Есть процессоры, в которых надо предпринимать специальные усилия для того, чтобы данные гарантированно были синхронизированы между процессорами. Или явно выключать кеширование для страниц памяти, в которых лежат данные, доступные нескольким процессорам. Это уже тема для совершенно отдельного разговора, здесь я лишь упомянул для полноты."

Ответ Евгения "Там всё равно есть ссылочная информация на этот объект" как-то не очень успокаивает... Есть ссылка на область (какой-то) памяти, с чего бы вдруг одной и той же для разных-то нитей?..

Evgenii Legotckoi
  • Sept. 6, 2023, 7:18 a.m.

Разве могут взаимодействовать объекты из разных нитей как-то, кроме как через сигнал-слоты?"

Могут. Выполняя оператор new , Вы выделяете под объект память в куче (heap), и если вы передаёте указатель на этот объект в 10 потоков, то код во всех 10-ти потоках может работать с этим указателем и модифицировать объект. В этом случае важным является защищать процесс записи мьютексами, чтобы не возникло состояниее гонки и данные не накрылись медным тазом. А сигнал слотовое соединение - это механизм потокобезопасной передачи данных, но это абсолютно не означает, что через это соединение нельзя передать указатель на объект в другом потоке, и не означает, что не нужно записываемые данные защищать мьютексами.

А касательно той статьи на Хабре, то это системное программирование на микроконтроллерах с использованием чистого Си, судя по коду, может я и ошибаюсь, но меня берут сомнения, что там примеры на С++. Да и автор в комментариях упоминает о Atmega 128.
Поэтому и рассуждение на тему организации памяти в процессоре исходя из статьи для систем реального времени на микроконтроллерах хоть и применимо для десктоп приложений, для которых в первую очередь разрабатывался Qt, тем не менее всё же выглядит высасыванием проблемы из пальца. Хотя бы потому, что в таком случае нужно уже говорить относительно конкретной модели процессора, на котором происходит разработка. В противном случае разговор является беспредметным.

Comments

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

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

  • Result:50points,
  • Rating points-4
m

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

  • Result:80points,
  • Rating points4
m

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

  • Result:20points,
  • Rating points-10
Last comments
ИМ
Игорь МаксимовNov. 22, 2024, 11:51 a.m.
Django - Tutorial 017. Customize the login page to Django Добрый вечер Евгений! Я сделал себе авторизацию аналогичную вашей, все работает, кроме возврата к предидущей странице. Редеректит всегда на главную, хотя в логах сервера вижу запросы на правильн…
Evgenii Legotckoi
Evgenii LegotckoiOct. 31, 2024, 2:37 p.m.
Django - Lesson 064. How to write a Python Markdown extension Добрый день. Да, можно. Либо через такие же плагины, либо с постобработкой через python библиотеку Beautiful Soup
A
ALO1ZEOct. 19, 2024, 8:19 a.m.
Fb3 file reader on Qt Creator Подскажите как это запустить? Я не шарю в программировании и кодинге. Скачал и установаил Qt, но куча ошибок выдается и не запустить. А очень надо fb3 переконвертировать в html
ИМ
Игорь МаксимовOct. 5, 2024, 7:51 a.m.
Django - Lesson 064. How to write a Python Markdown extension Приветствую Евгений! У меня вопрос. Можно ли вставлять свои классы в разметку редактора markdown? Допустим имея стандартную разметку: <ul> <li></li> <li></l…
d
dblas5July 5, 2024, 11:02 a.m.
QML - Lesson 016. SQLite database and the working with it in QML Qt Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
Now discuss on the forum
m
moogoNov. 22, 2024, 7:17 a.m.
Mosquito Spray System Effective Mosquito Systems for Backyard | Eco-Friendly Misting Control Device & Repellent Spray - Moogo ; Upgrade your backyard with our mosquito-repellent device! Our misters conce…
Evgenii Legotckoi
Evgenii LegotckoiJune 24, 2024, 3:11 p.m.
добавить qlineseries в функции Я тут. Работы оень много. Отправил его в бан.
t
tonypeachey1Nov. 15, 2024, 6:04 a.m.
google domain [url=https://google.com/]domain[/url] domain [http://www.example.com link title]
NSProject
NSProjectJune 4, 2022, 3:49 a.m.
Всё ещё разбираюсь с кешем. В следствии прочтения данной статьи. Я принял для себя решение сделать кеширование свойств менеджера модели LikeDislike. И так как установка evileg_core для меня не была возможна, ибо он писался…

Follow us in social networks