[ньюб QT] queued signal - подтверждения выполнения
Они то вроде как специально под разделение UI и всего остального и были сделаны (примеры для этого в справке были).
стандартные QT треды имеют всё необходимое для синхронизации. Но беглый просмотр доков не выявил той функциональности, которая мне нужна. Либо её нужно делать самому и я с радостью приму советы по алгоритму или ссылку на шаблон разработки либо же готовую либу. Либо она уже реализована в QT и тогда я буду благодарен за название класса, чтение доков по которому решило бы мои проблемы
The return value of the member function call is placed in ret. If the invocation is asynchronous, the return value cannot be evaluated. You can pass up to ten arguments (val0, val1, val2, val3, val4, val5, val6, val7, > val8, and val9) to the member function.Так что то, что ты просишь, стандартными средствами, по-видимому, не реализуется — If the invocation is asynchronous, the return value cannot be evaluated..
Если же worker thread достаточно часто проверяет очередь событий (регулярно вызывая QCoreApplication::sendPostedEvents то можно попробовать звать QMetaObject::invokeMethod с флагом Qt::BlockingQueuedConnection — тогда gui thread будет ждать, пока в worker отработает вызываемая функция (или же это она очень долго выполняется? а в этом случае уже можно в ней взводить или опускать флажок "параметр принят", который и будет эдаким возвращаемым значением.
А стандартные QT-треды не подошли что ли?Мне нужно было сложную систему для подтверждения обмена информацией между клиентом (UI) и сервером (вычислительный процесс) с учётом возможных высоких лагов последнего, во время которого необходимо для клиента переводить во временное состояние с возможностью отмены. (Например, мы меняем количество рабочих тредов в QLineEdit, после чего там рисуется "?" и в зависимости от результата негоциаций меняется либо на старое, либо на запрашиваемое новое значение.
Они то вроде как специально под разделение UI и всего остального и были сделаны (примеры для этого в справке были).
Это сложная система, редко нужная, и понятно, что QT подобного не обязана иметь.
Но в чём заключается полная неожиданность для меня - так в том, что QT не поддерживает асинхронные сообщения (signal->slot) в разных тредах. Документация заявляет, что это возможно, даже константу вводит Qt::QueuedConnection. А на самом деле эта штука всё равно блокирует тред.
Для того, чтобы продемонстрировать фичу, я сделал простую программу, которая создаёт два треда. Главный тред посылает во вспомогательный сообщение, после чего завершается, послав сообщение "выходи вон" самому себе. Вспомогательный тред просто ждёт этого сигнала и по получению его ничего делает. Сигнал завершения основного треда провоцирует завершение программы.
Что ожидается: программа завершается независимо от того, что делает вспомогательный тред после получение сигнала главного.
Результат эксперимента: sleep в вспомогательном процессе приостанавливает выполнение основного.
Вывод: Qt::QueuedConnection работает в блокирующем режиме.
пиздец, товарищи
Собственно код и вывод его работы в зависимости от простановки коммента на sleep(3600) - в последнем случая я не дожидаюсь завершения программы
import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *
class PrintThread (QThread):
AsyncSignal = pyqtSignal
StopSignal = pyqtSignal
def __init__(self):
super(PrintThread,self).__init__
self.Helper = HelperThread
self.ConnectAll
def ConnectAll(self):
self.finished.connect(self.Helper.terminate,Qt.QueuedConnection)
self.AsyncSignal.connect(self.Helper.AsyncSlot,Qt.QueuedConnection)
self.StopSignal.connect(self.stopSlot,Qt.QueuedConnection)
def stopSlot (self):
print ("Hit stop")
self.quit
def printSlot(self):
print ("emitting signal")
self.AsyncSignal.emit
print ("done")
self.StopSignal.emit
def run(self):
print ("printer started")
self.Helper.start
self.wait(1)
self.printSlot
self.exec
class HelperThread (QThread):
def __init__ (self):
super(HelperThread,self).__init__
def AsyncSlot(self):
print ("Catch Signal")
#self.sleep(3600)
def run(self):
print ("helper started")
self.exec
def main:
print ("Start testing")
app = QApplication(sys.argv)
th = PrintThread
th.finished.connect(app.quit)
th.start
app.exec_
print ("Test has ended")
if __name__ == '__main__':
main
Start testing
printer started
helper started
emitting signal
done
Catch Signal
Hit stop
Test has ended
Собственно два вопроса:
Start testing
printer started
helper started
emitting signal
done
Catch Signal
1) корректен ли мой эксперимент, не выдал ли я собственные ошибки за косяки QT?
2) что мне теперь делать, если мне нужно именно асинхронные сообщения? Добавить к связки питона и QT JMS?
Вывод: Qt::QueuedConnection работает в блокирующем режиме.пиздец, товарищиНе кипятись так. Тут важно понимать, в какую очередь сообщений попадает тот или иной запрос на выполнение функции.
Сообщение для экземпляра QObject (назовем его A) обрабатывается очередью сообщений потока T, если экземпляр A был создан в этом потоке. Поэтому, если тебе хочется, чтобы функция из экземпляра A отработала в потоке T, то A надо создавать именно в этом потоке.
В твоей же программе HelperThread создается в PrinterThread, поэтому он принадлежит потоку печати, а значит, все предназначенные ему сообщения будут обрабатываться в потоке печати.
Теперь ясно, как поправить твою программу — в конструкторе HelperThread скажи moveToThread(this); после этого сообщения, предназначенные для HelperThread, будут обрабатываться в его очереди сообщений, а не в очереди сообщений его родителя.
#include "qt-threading.h"
Helper::Helper
{
moveToThread(this);
}
void Helper::run
{
printf("helper started\n");
exec;
}
void Helper::asyncSlot
{
printf("catch signal\n");
sleep(3600);
}
Printer::Printer
{
helper = new Helper;
connect(this,SIGNAL(asyncSignalhelper,SLOT(asyncSlotQt::QueuedConnection);
connect(this,SIGNAL(stopSignalthis,SLOT(stopSlotQt::QueuedConnection);
}
void Printer::run
{
printf("printer started\n");
helper->start;
sleep(1);
printSlot;
}
void Printer::printSlot
{
printf("emitting signal\n");
emit asyncSignal;
printf("done\n");
emit stopSignal;
}
void Printer::stopSlot
{
printf("hit stop\n");
quit;
}
int main(int argc,char **argv)
{
QCoreApplication app(argc,argv);
Printer *p = new Printer;
QObject::connect(p,SIGNAL(finished&app,SLOT(quit;
p->start;
int r = app.exec;
printf("test has ended\n");
return r;
}
А вот заголовки:
#ifndef __QT_THREADING_H_INCLUDED__
#define __QT_THREADING_H_INCLUDED__
#include <QtCore>
class Helper:public QThread
{
Q_OBJECT
public:
Helper;
virtual ~Helper{}
protected:
void run;
Q_SLOT void asyncSlot;
};
class Printer:public QThread
{
Q_OBJECT
public:
Printer;
virtual ~Printer {}
protected:
void run;
Q_SLOT void printSlot;
Q_SLOT void stopSlot;
Q_SIGNAL void asyncSignal;
Q_SIGNAL void stopSignal;
private:
Helper *helper;
};
#endif
Не кипятись так. Тут важно понимать, в какую очередь сообщений попадает тот или иной запрос на выполнение функции.Спасибо.
Сообщение для экземпляра QObject (назовем его A) обрабатывается очередью сообщений потока T, если экземпляр A был создан в этом потоке.
В очередной раз забыл какой QT умный и как много в нём побочных эффектов. Недавно вот выловил баг с созданием сигнала через pyqtSignal. Думал что для разных экземпляров класса сигналы нужны разные, прописал создание в конструкторе. А нужно было описывать как статический член класса, оно оказывается умеет прозрачно бандлиться с self. Но в тот раз гугол спас. Чувствую я ждёт меня ещё немало забавного и интересного.
Оффтопик: а что у QT так мало виджетов разных, я думал их будет гораздо больше, чем нашлось в QTDesigner? Может существуют свободные коллекции виджетов, которые можно докачать отдельно?
а что у QT так мало виджетов разных, я думал их будет гораздо больше, чем нашлось в QTDesigner? Может существуют свободные коллекции виджетов, которые можно докачать отдельно?А чего тебе там не хватает? Попробуй посмотреть Qt Labs Blog — там описано немало необычных применений имеющимся элементам интерфейса. С помощью смекалки и листов стилей их можно изменить до неузнаваемости и заставить делать нестандартные вещи — например, список задач в à la Windows Explorer реализуется посредством QTreeView.
То есть, я верю, что дефолтный QT предоставляет достаточно полный набор building blocks, но ведь можно было бы разнообразить палитру более сложными виджетами, пусть и состоящими из простых
Вопрос про коллекцию QT виджетов всё ещё актуален. Допустим я хочу поместить на форму виджет для открытия файла: edit для ввода и связанная с ним кнопка для задания имени файла через диалог. Должен я сам этот виджет конструировать (что конечно весьма просто, пост на форуме дольше написать, но меня интересует "правильная" практика или же можно откуда-то получить расширенную коллекцию виджетов и брать виджет оттуда в несколько щелчков мыши? То есть базовый набор виджетов - это хорошо, но обычно всякий гуи проект обрастает большим количеством собственных чуть более продвинутых виджетов. Может есть такой набор виджетов в общем пользовании?
Насчет "правильной" практики попробую посоветовать реализовать свой элемент управления в духе
class FileSelector : public QWidget, public QDesignerCustomWidgetInterface
{
Q_OBJECT
public:
FileSelector(QWidget *parent = 0) : QWidget(parent)
{
//fileNameEdit делаем не локальной, чтобы можно было потом пользоваться
//А о кнопке нам дальше помнить не надо
fileNameEdit = new QLineEdit;
QPushButton *browse = new QPushButton(tr("Browse...";
QHBoxLayout *h = new QHBoxLayout(this);
h->addWidget(fileNameEdit);
h->addWidget(browse);
connect(browse,SIGNAL(clickedthis,SLOT(browseClicked;
......
}
....
остальной нужный код -- получение имени/содержимого файла, etc.
А дальше смотришь документацию по "Custom Widget Plugin Example", где описано, как зарегистрировать твой элемент интерфейса в Designer'е.
Хотя от меня лично совет — забей на Designer — после изучения доков на Q{HBox,VBox,Grid}Layout интерфейс рисуется и компонуется очень быстро руками. Зато потом проще будет модифицировать интерфейс — я, к примеру, так и не понял в свое время, как в Designer'е редактировать stretch'и и прочие мало-мальски хитрые компоненты форм (хотя это было во время Qt 4.0.0 — может, с тех пор чего и изменилось).
Хотя от меня лично совет — забей на Designer — после изучения доков на Q{HBox,VBox,Grid}Layout интерфейс рисуется и компонуется очень быстро руками. Зато потом проще будет модифицировать интерфейс — я, к примеру, так и не понял в свое время, как в Designer'е редактировать stretch'и и прочие мало-мальски хитрые компоненты форм (хотя это было во время Qt 4.0.0 — может, с тех пор чего и изменилось).уже забил. Двух небольших приложений хватило за глаза.
Только вот ручками не намного приятнее все виджеты на форму класть. Во-первых до хрена тупого кода. Во-вторых, все эти положить то на это происходят в два приёма когда есть связь child <-> parent. Сначала в child нужно указать parent, потом в parent добавить child. Объектный подход доканывает. Хочется иметь возможность в рантайме работать с xml наподобие той, что работает с *.ui. То есть чтобы можно было бы создать добавить сложный виджет на форму одной командой, вроде
self.somebox.add(
"""
<vboxlayout=someLayout>
<widget=someWidget> <Geometry x=0 y=0 width=200 height=100> </widget>
<pushbutton=pbExpand><picture= _resource_addr_/></pushbutton>
<hboxlayout spacing=4>
...
</hboxlayout>
</vboxlayout>
"""
)
при этом вовсе не обязательно было бы называть элементы (пусть существуют анонимно а сразу после выполнения строчки кода по строковым константам генерировались бы необходимые виджеты и добавлялись бы в класс и были доступны уже через объектный интерфейс. Точно так же обратно: из объектного интерфейса получать строчки xml конфига, уже запихнутого в DOM
Хочется иметь возможность в рантайме работать с xml наподобие той, что работает с *.ui. То есть чтобы можно было бы создать добавить сложный виджет на форму одной командой, вродеВо-первых, если будешь делать widget так, как я показал, то и получишь автоматом "добавление сложного виджета" — напишешь у себя в конструкторе
blahblahblah = new FileSelector;
и вперед.
А если хочется xml, то смотри в сторону QFormBuilder — он как раз умеет по xml'ю строить widget. Хотя, я бы не стал им пользоваться, потому как считаю, что XUL — уродец еще тот и лучше уж поиметь не очень красивый конструктор, чем раскидывать элемент интерфейса по куче файлов, да еще и validation для XUL'а иметь только в runtime.
Если хочется какой-то язык, приспособленный непосредственно для рисования интерфейса — посмотри Qt Declarative (пока что его надо отдельно скачивать с сайта Trolltech, но обещают, что будет из коробки в Qt 4.7). Насколько оно готово к использованию, не знаю — сам еще с ним не игрался.
Во-первых, если будешь делать widget так, как я показал, то и получишь автоматом "добавление сложного виджета" — напишешь у себя в конструктореа вот не пофиг, в родительской форме мне этот набор виджетов описывать или выделить в отдельный класс. Я тебе просто говорю, что объектная форма не самая приятная, иногда хочется иметь возможность комбинировать её с деклартивной (чтобы создание лэйаута виджетов напоминало написание html то есть в результате мне хочется получить гибридную систему. Поскольку я использую питон, отсутствие проверок времени компиляции меня не заботит, в питоне их не бывает вообще
объектная форма не самая приятная, иногда хочется иметь возможность комбинировать её с деклартивной (чтобы создание лэйаута виджетов напоминало написание html)Как такое делать, не знаю — попробуй выдумать свой велосипед. Потом порассказывай о результатах.
Оставить комментарий
yroslavasako
Я хочу разнести тред юзвер интерфейса и рабочий тред (чтобы тормоза одного не влияли на работу другого). Для мультитрединга пользуюсь только QT либами. Столкнулся с достаточно общей проблемой, чтобы она уже имела решение.Допустим, в юзверинтерфейсе изменилсь параметры, влияющие на рабочий тред, после чего UI ставит сигнал в очередь. Затем он выставляет в соответствующее поле неопределённой значение (например "?" в QLineEdit) и ждёт, когда от work треда придёт подтверждение, что параметр принят (bool) в зависимости от результата оставляет старый параметр или меняет его новый. Как проще реализовать такую функциональность, учитывая возможность того, что за время обработки work тредом UI мог успеть сгенерировать тот же сигнал повторно, но уже с другими параметрами.
Сам я пришёл к необходимости написать собственный MessageManager, который к сообщениям добавлял бы IDшник, и проверял ответ на которое из них пришёл, поддерживающий две политики работы, согласно которым обрабатывается либо вся очередь одинаковых сообщений (если все сообщения важны и терять их нельзя, например сообщения образуют моноид либо последнее из них (если пользователь успел поменять параметр десять раз, а рабочему треду важен только последний заданный, то есть сообщения исключают друг друга).
Быть может QT предлагает другой удобный механизм, а я просто не рассмотрел его при беглом поиске по докам?