Пытаюсь изменить значение текстового поля из другого потока, но почему-то не работает: Есть 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, Проблема не решена
Так как вы не привели свой код полностью, сложно сказать, почему у вас что-то не работает. Поэтому, привожу свой пример, в котором значение текстового поля изменяется отдельным потоком.
Тип 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();
}
}
}
После всех ваших дополнений и уточнений к вопросу удалось запустить ваш код. Все исправления помечу комментариями. Странно, что у вас нет ни ошибок компиляции, ни ошибок дебаггера 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");
}
}
}
}
}
Проблема решена) В общем вся проблема была в этих 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);
}
Всем спасибо)
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Перевод документов на английский язык: Важность и ключевые аспекты
Укажите вывод программы (“comp”, если программа не скомпилируется и “err”, если произойдет ошибка при исполнении):