Изменение текстового поля на форме из потока (2 окна) QML QT C++

376
12 октября 2017, 12:22

Пытаюсь изменить значение текстового поля из другого потока, но почему-то не работает: Есть 2 окна, первое окно:

firstwindow.h:

#include "mythread.h"
#include <QObject>
class FirstWindow : public QObject
{
  Q_OBJECT
  public:
    explicit FirstWindow(QObject *parent = nullptr);
  private:
    MyThread *myThread;
  public slots:
    void start();
};

firstwindow.cpp:

#include "firstwindow.h"
FirstWindow::FirstWindow(QObject *parent) : QObject(parent)
{
}
void FirstWindow::start()
{
   myThread = new MyThread(5); //передача значения в класс MyThread
}

firstwindow.qml:

import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.3
import QtQuick.Window 2.0
import FirstWindowModule 1.0
ApplicationWindow {
  visible: true
  width: 380
  height: 240
  id: firstwindow
FirstWindow {
    id: obj;
}
//создание и вызов второго окна, вызов функции start(), создающей объект класса MyThread
Button { 
   text: "Start"
   onClicked: {
   var component = Qt.createComponent("main.qml");
       console.log("Component Status:", component.status, component.errorString());
       var window = component.createObject(firstwindow);
       window.show()
       obj.start();
   }
 }
}

Второе окно:

mythread.h:

#include "newclass.h"
#include <QObject>
#include <QThread>
class MyThread : public QObject
{
  Q_OBJECT
  Q_PROPERTY(QString firstNumber READ GetFirstNumber WRITE SetFirstNumber NOTIFY firstNumberChanged)
  private:
   QThread *thread;
   NewClass *newClass;
   QString firstNumber;
   int counter;
  private slots:
   void UpdateFirstValue (int i);
  public slots:
    void StartThread(int);
  public:
    explicit MyThread(int value, QObject *parent = nullptr);
    explicit MyThread(QObject *parent = nullptr);
    QString GetFirstNumber();
    void SetFirstNumber(QString);
  signals:
    void firstNumberChanged();
 };

mythread.cpp:

#include "mythread.h"
MyThread::MyThread(QObject *parent)
: QObject(parent)
{
}
MyThread::MyThread(int counter, QObject *parent)
: QObject(parent)
{
  this->counter = counter;
  StartThread(counter); //запуск функции создания и запуска потока
}
QString MyThread::GetFirstNumber()
{
  return firstNumber;
}
void MyThread::SetFirstNumber(QString value)
{
  firstNumber = value;
}
void MyThread::StartThread(int counter)
{
  thread = new QThread;
  newClass = new NewClass(counter);
  newClass->moveToThread(thread);
  connect(newClass, SIGNAL(sendfirstvalue(int)), this, SLOT(UpdateFirstValue(int)));
  connect(thread, SIGNAL(started()), newClass, SLOT(Start()));
  thread->start();
}
void MyThread::UpdateFirstValue (int i)
{
  firstNumber = QString::number(i);
  emit firstNumberChanged();
}

main.qml:

import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.3
import QtQuick.Window 2.0
import NameModule 1.0
ApplicationWindow {
   visible: true
   width: 380
   height: 240
Rectangle {
    id: content
    TypeName {
        id: obj
    }
    ColumnLayout {
        height: parent.height
        anchors.horizontalCenter: content.horizontalCenter
        Rectangle {
            Layout.fillHeight: true
            Text {
                anchors.centerIn: parent
                id: firstNumber
                text: "f = " + obj.firstNumber
                font.bold: true
                onTextChanged: {
                    console.log("firstNumberChanged");
                }
            }
        }
    }
 }
 }

Класс NewClass, функция которого работает в потоке:

newclass.h:

#include <QObject>
class NewClass : public QObject
{
  Q_OBJECT
  public:
    explicit NewClass(int value, QObject *parent = nullptr);
  signals:
    void sendfirstvalue(int);
  private slots:
    void Start();
  private:
    int value;
};

newclass.cpp:

#include "newclass.h"
#include <windows.h>
#include "QDebug"
NewClass::NewClass(int value, QObject *parent)
: QObject(parent)
{
   this->value = value;
}
void NewClass::Start()
{
  qDebug() << "value" << value;
  for(int i = 0; i < value; i++)
  {
    qDebug() << "i" << i;
    emit sendfirstvalue(i);
    Sleep(1000); // from windows.h
  }
}

Связка классов FirstWindow и MyThread с соответствующими им qml:

qmlRegisterType<FirstWindow>("FirstWindowModule", 1, 0, "FirstWindow");
qmlRegisterType<MyThread>("NameModule", 1, 0, "TypeName");

Суть проблемы: 2 окна, в первом окне (FirstWindow) располагается кнопка, по которой открывается второе окно (MyThread), и сразу запускается вычислительный процесс в новом потоке (функция Start() в классе NewClass. Данная функция в цикле, каждую секунду отсылает целочисленное значение обратно во второе окно MyThread, и данное значение должно отображаться в данном окне. Установлено, что проблема скорее всего в создании этого второго окна, что либо поток запускается раньше, чем показывается окно, либо еще что-то. Спасибо.

UPDATE: заново переписал вопрос и залил проект на https://github.com/sawyerhard/test2, Проблема не решена

Answer 1

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

Тип TextManager сразу наследуется от QThread. В нем останется только переопределить метод run.

main.h

#include <QObject>
#include <QThread>
class TextManager : public QThread
{
    Q_OBJECT
    Q_PROPERTY(QString value READ getValue WRITE setValue NOTIFY valueChanged)
    public:
        TextManager(QObject *parent = 0);
        QString getValue() const;
    private:
        QString value;
        void run();
    signals:
        void valueChanged();
    public slots:
        void setValue(const QString &value);
}; 

main.cpp

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <main.h>
TextManager::TextManager(QObject *parent)
   : QThread(parent), value("init")
{
}
QString TextManager::getValue() const
{
   return value;
}
void TextManager::setValue(const QString &v)
{
   value = v;
}
void TextManager::run()
{
   for(int i = 0; i < 10; i++){
       setValue(QString::number(i));
       emit valueChanged();
       msleep(300);
   }
}

int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    QGuiApplication app(argc, argv);
    qmlRegisterType<TextManager>("PropertyHandler", 1, 0, "TextManager");
    QQmlApplicationEngine engine;
    engine.load(QUrl(QLatin1String("qrc:/main.qml")));
    if (engine.rootObjects().isEmpty())
        return -1;
    return app.exec();
}

main.qml

import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Window 2.0
import PropertyHandler 1.0
ApplicationWindow {
    visible: true
    width: 380
    height: 240
    Component.onCompleted: {
           setX(Screen.width / 2 - width / 2);
           setY(Screen.height / 2 - height / 2);
    }
    TextManager {
        id: txtmgr
    }
    Text {
        id: txtField
        anchors.centerIn: parent
        text: txtmgr.value
    }
    Button {
       text: "Start"
       onClicked: {
          txtmgr.start();
       }
    }
}
Answer 2

После всех ваших дополнений и уточнений к вопросу удалось запустить ваш код. Все исправления помечу комментариями. Странно, что у вас нет ни ошибок компиляции, ни ошибок дебаггера qml. Или вы и о них умолчали.

Заголовочного файла не будет. Все в main.cpp.

main.cpp

#include <QGuiApplication>
#include <QQuickView>
#include <QThread>
#include <QTest>

class NewClass : public QObject
{
   Q_OBJECT
   public:
     explicit NewClass(QObject *parent = nullptr);
   signals:
     void sendfirstvalue(int);
   private slots:
     void Start();
};

class MyThread : public QObject
{
  Q_OBJECT
  Q_PROPERTY(QString firstNumber READ GetFirstNumber WRITE SetFirstNumber NOTIFY firstNumberChanged)
  private:
    QThread *thread;
    NewClass *newClass;
    QString firstNumber;
  private slots:
    void UpdateFirstValue (int i);
  public slots:
  // это основная причина, почему не работало; вы определили его как private, но хотели вызывать из qml
    void mStartThread(); // переименовал StartThread -> mStartThread так как qml хочет вызывать функции, имена которых начинаются с нижнего регистра; работает и с верхним, но ругается
  public:
    explicit MyThread(QObject *parent = nullptr);
    QString GetFirstNumber();
    void SetFirstNumber(QString);
  signals:
    void firstNumberChanged();
};

//--------------------------------------------------------
// конструкторы MyThread и NewClass, которые вы не указали
//--------------------------------------------------------
MyThread::MyThread(QObject *parent)
    : QObject(parent)
{
}
NewClass::NewClass(QObject *parent)
    : QObject(parent)
{
}
//--------------------------------------------------------

QString MyThread::GetFirstNumber()
{
   return firstNumber;
}
void MyThread::SetFirstNumber(QString value)
{
  firstNumber = value;
}
void MyThread::mStartThread()
{
   thread = new QThread;
   newClass = new NewClass();
   newClass->moveToThread(thread);
   connect(newClass, SIGNAL(sendfirstvalue(int)), this, SLOT(UpdateFirstValue(int)));
   connect(thread, SIGNAL(started()), newClass, SLOT(Start()));
   thread->start();
}
void MyThread::UpdateFirstValue (int i)
{
   firstNumber = QString::number(i);
   emit firstNumberChanged();
}

void NewClass::Start()
{
   for(int i = 0; i < 3; i++)
   {
     emit sendfirstvalue(i);
     QTest::qSleep(1000); // у вас Sleep непонятно откуда - заменил на QTest::qSleep (в *.pro файле надо добавить QT += testlib)
   }
}

// ну main вы просто не приводили; приведу, чтобы было понятно, как можно запустить
int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    QGuiApplication app(argc, argv);
    qmlRegisterType<MyThread>("NameModule", 1, 0, "TypeName");
    QQuickView view;
    view.setResizeMode(QQuickView::SizeRootObjectToView);
    view.setSource(QUrl("qrc:/main.qml"));
    view.show();
    return app.exec();
}
#include "main.moc" // так как нет заголовочного файла и классы определены тут же

main.qml

import QtQuick 2.0
import QtQuick.Layouts 1.1
import NameModule 1.0
import QtQuick.Controls 2.0
Rectangle {
    id: content
    TypeName {
        id: obj
    }
    // добавил кнопку для вызова - проверить же надо как-то
    Button {
        text: "start"
        onClicked: {
            obj.mStartThread();
        }
    }
    ColumnLayout {
        height: parent.height
        anchors.horizontalCenter: content.horizontalCenter
        Rectangle {
            Layout.fillHeight: true
            Text {
                anchors.centerIn: parent
                id: firstNumber
                text: "f = " + obj.firstNumber
                font.bold: true
                onTextChanged: {
                    console.log("firstNumberChanged");
                }
            }
        }
    }
}
Answer 3

Проблема решена) В общем вся проблема была в этих 2х строчках в файле firstwindow.qml:

var window = component.createObject(firstwindow);

и вызов функции

obj.start();    

при котором происходило повторное!!! создание объекта myThread, так как основной объект создавался в строчке выше (так как окно связано с MyThread классом), из-за этого данные, которые хоть и приходили, но не отображались в окне.

Решение: 1. Убираем строчку из firstwindow.qml:

obj.start();

2. Добавляем метод onCompleted в файл main.qml, в котором запускаем выполнение потока:

Component.onCompleted: {
    obj.startThread(5);
}

Всем спасибо)

READ ALSO
Hash Table Неправильный вывод через find

Hash Table Неправильный вывод через find

Где ошибкаХочу узнать какие парные числа, какие нет

234
Тест по технологии CUDA [требует правки]

Тест по технологии CUDA [требует правки]

Укажите вывод программы (“comp”, если программа не скомпилируется и “err”, если произойдет ошибка при исполнении):

215
как подключить BDA device в C++

как подключить BDA device в C++

как подключить PCI Tuner в C++ чтоб потом работать с BDA

230
Как правильно вставить окно в окно в mfc С++?

Как правильно вставить окно в окно в mfc С++?

Всем доброго времени суток

228