abclinuxu.cz AbcLinuxu.cz itbiz.cz ITBiz.cz HDmag.cz HDmag.cz abcprace.cz AbcPráce.cz
AbcLinuxu hledá autory!
Inzerujte na AbcPráce.cz od 950 Kč
Rozšířené hledání
×
dnes 01:32 | Nová verze
Letos bylo v komunitě Mageia hodně změn. Po volbě nových vedoucích přišla velká aktualizace a krátce na to udržovací verze 6.1. 7.12., dle plánu, vyšla Mageia s číslem 7 v její první beta verzi. Chyby můžete hlásit v bugzille. Chyby v českých překladech pak na fóru české komunity.
Joelp | Komentářů: 0
dnes 00:11 | Zajímavý projekt
Kvůli rychlejšímu vývojovému cyklu byla přemístěna Cinelerra-gg. Cinelerra-gg je fork Cinelerry-hv. Některé rozdíly forků popisuje sám hlavní vývojář William Morrow (aka GoodGuy). Není zde popsán i fork Lumiera, zřejmě kvůli zatím nepoužitelnému stavu. … více »
D81 | Komentářů: 0
včera 19:11 | Nová verze

Do aplikace pro instant messaging Telegram (Wikipedie) lze nově nahrát češtinu. Více v příspěvku na blogu Telegramu.

Ladislav Hagara | Komentářů: 3
včera 10:55 | Nová verze

Jean-Baptiste Kempf, prezident neziskové organizace VideoLAN stojící za svobodným multiplatformním multimediálním přehrávačem a frameworkem VLC, oznámil v příspěvku na svém blogu vydání první oficiální verze 0.1.0 v říjnu představeného dekodéru svobodného videoformátu AV1 (AOMedia Video 1) s názvem dav1d (Dav1d is an AV1 Decoder). Jedná se o alternativu k referenčnímu dekodéru libaom. Kódový název dav1da verze 0.1.0 je Gazelle.

Ladislav Hagara | Komentářů: 2
včera 10:22 | Nová verze

Po více než dvou letech od vydání verze 11.0 byla vydána nová major verze 12.0 svobodného unixového operačního systému FreeBSD. Podrobný přehled novinek v poznámkách k vydání.

Ladislav Hagara | Komentářů: 4
11.12. 19:55 | Nová verze

Byla vydána verze 3.11 živé linuxové distribuce Tails (The Amnesic Incognito Live System), jež klade důraz na ochranu soukromí uživatelů a anonymitu. Přehled změn v příslušném seznamu. Řešena je řada bezpečnostních chyb.

Ladislav Hagara | Komentářů: 0
11.12. 15:22 | Nová verze

Byl vydán Mozilla Firefox 64.0. Přehled novinek v poznámkách k vydání a na stránce věnované vývojářům. Nejnovější verze tohoto webového prohlížeče přináší například ovládání více panelů, nebo správce úloh, který lze otevřít v nabídce Firefoxu > Více > Správce úloh, nebo napsáním about:performance do adresního řádku.

Ladislav Hagara | Komentářů: 8
11.12. 13:00 | Zajímavý článek Ladislav Hagara | Komentářů: 0
10.12. 22:33 | Nová verze

Po 3 měsících vývoje od vydání verze 14 byla vydána nová stabilní verze 15 open source systému Nextcloud, forku ownCloudu, umožňujícího provoz vlastního cloudového úložiště. Přehled novinek i s náhledy v příspěvku na blogu. Pro vyzkoušení Nextcloudu je k dispozici demo.

Ladislav Hagara | Komentářů: 6
10.12. 18:00 | IT novinky

Počítačová hra Doom slaví 25 let. Společností id Software ji vydala 10. prosince 1993. Zahrát si ji lze například na Internet Archive.

Ladislav Hagara | Komentářů: 17
Chystáte se přejít na Wayland na „desktopu“?
 (25%)
 (6%)
 (12%)
 (30%)
 (27%)
Celkem 111 hlasů
 Komentářů: 14, poslední 10.12. 12:19
Rozcestník

Dotaz: Qt/C++, signály, sloty a vlánka

xkucf03 avatar 29.9. 00:32 xkucf03 | skóre: 46 | blog: xkucf03
Qt/C++, signály, sloty a vlánka
Přečteno: 1666×

Píši GUI aplikaci, která má číst standardní vstup a chci aby načtená data průběžně zobrazovala. Používám Qt a C++. Jsou tam potřeba dvě vlákna -- jedno pro GUI a jedno pro čtení vstupu. Z toho vlákna pro čtení nemůžu pracovat s GUI komponentami a plnit do nich hodnoty, to je jasné.

Dočetl jsem se, že nejsnazší způsob, jak tahle dvě vlákna propojit jsou signály a sloty. To jsem udělal a funguje mi to. Akorát mi připadá, že bylo potřeba napsat moc kódu, který nic zajímavého nedělá, jen přeposílá události -- a to je potřeba udělat pro každou metodu. Výsledek:

#pragma once

#include <QObject>

#include <relpipe/reader/typedefs.h>
#include <relpipe/reader/TypeId.h>
#include <relpipe/reader/handlers/RelationalReaderStringHandler.h>
#include <relpipe/reader/handlers/AttributeMetadata.h>

using namespace relpipe::reader;
using namespace relpipe::reader::handlers;

// signal/slot parameters must be declared here and registered with qRegisterMetaType()

Q_DECLARE_METATYPE(string_t)
Q_DECLARE_METATYPE(std::vector<AttributeMetadata>)

class QtRelationalReaderStringHadler : public QObject, public RelationalReaderStringHadler {
	Q_OBJECT
private:
	RelationalReaderStringHadler* target;
public:

	QtRelationalReaderStringHadler(QObject* parent, RelationalReaderStringHadler* target) :
	QObject(parent), target(target) {

		// see Q_DECLARE_METATYPE above
		qRegisterMetaType<string_t>();
		qRegisterMetaType<std::vector < AttributeMetadata >> ();

		QObject::connect(this, &QtRelationalReaderStringHadler::signal_startRelation, this, &QtRelationalReaderStringHadler::slot_startRelation);
		QObject::connect(this, &QtRelationalReaderStringHadler::signal_attribute, this, &QtRelationalReaderStringHadler::slot_attribute);
		QObject::connect(this, &QtRelationalReaderStringHadler::signal_endOfPipe, this, &QtRelationalReaderStringHadler::slot_endOfPipe);
	}

	virtual ~QtRelationalReaderStringHadler() {

	}

	virtual void startRelation(string_t name, std::vector<AttributeMetadata> attributes) override {
		emit signal_startRelation(name, attributes);
	}

	virtual void attribute(const string_t& value) override {
		emit signal_attribute(value);
	};

	virtual void endOfPipe() override {
		emit signal_endOfPipe();
	};

signals:
	void signal_startRelation(string_t name, std::vector<AttributeMetadata> attributes);
	void signal_attribute(const string_t& value);
	void signal_endOfPipe();

private slots:

	void slot_startRelation(string_t name, std::vector<AttributeMetadata> attributes) {
		target->startRelation(name, attributes);
	};

	void slot_attribute(const string_t& value) {
		target->attribute(value);
	};

	void slot_endOfPipe() {
		target->endOfPipe();
	};
};

Jde mi o to, že mám rozhraní RelationalReaderStringHadler a toto rozhraní mi provolává metoda, která parsuje vstup. V CLI aplikaci to můžu napojit napřímo, ale tady jsem mezi to musel vložit ještě tu "proxy" QtRelationalReaderStringHadler, která to prožene přes signály/sloty a tím zajistí, že se data mezi dvěma vlákny předají správně. Napadá vás nějaké elegantnější řešení?

V dokumentaci Qt jsem našel, že to jde napojovat i na std::bind nebo lambdu, což by trošku kódu ušetřilo (mohl bych kód slotů předat do QObject::connect() jako funkci). Nešlo by to napojit nějak automaticky? Protože ta rozhraní jsou v obou vláknech totožná.

Případně by se mi líbilo, kdybych mohl v těch metodách místo emit signal_... jen předat někam lambdu s tím, že se má provést v GUI vlákně. (to bych si asi zvládl napsat sám a protáhnout všechna volání přes jeden signál/slot, ale nechci vymýšlet kolo a radši bych se naučil nějaký standardní postup).

Mám rád, když se lidé přou, znamená to, že vědí, co dělají, a že mají směr. Frantovo.cz, SQL-Výuka.cz, Nekuřák.net

Řešení dotazu:


Odpovědi

29.9. 08:26 MadCatX
Rozbalit Rozbalit vše Re: Qt/C++, signály, sloty a vlánka
A proč nemůžeš ty signály vysílat přímo z RelationStringHandleru? Fungovalo by to IMHO stejně a ušetřil by sis psaní toho proxy.
xkucf03 avatar 29.9. 11:36 xkucf03 | skóre: 46 | blog: xkucf03
Rozbalit Rozbalit vše Re: Qt/C++, signály, sloty a vlánka

Ten RelationalReaderStringHadler je rozhraní definované v knihovně, kterou tato aplikace používá. Třída QtRelationalReaderStringHadler je implementace tohoto rozhraní v aplikaci. Stejně tak toto rozhraní implementuje GUI třída:

class RelpipeChartMainWindow : public QMainWindow, public RelationalReaderStringHadler {...

A v ní jsou pak tyto metody (sloužící i jako sloty) stylem:

void RelpipeChartMainWindow::endOfPipe() {
	// TODO: just display a message
	statusBar()->addWidget(new QPushButton("endOfPipe", widget.statusbar));
}

Zpracování se pak spouští ve vlákně na pozadí:

RelpipeChartMainWindow window;
QtRelationalReaderStringHadler handler(&app, &window);
reader->addHandler(&handler);
WorkerThread t(reader); // QThread volající reader->process(); tzn. čtení STDIN a volání metod handleru
t.start();

V CLI aplikaci bych místo reader->addHandler(&handler); udělal reader->addHandler(&window); a poslal události rovnou objektu, který je vypíše (a celé by to běželo v jednom vlákně). V Qt aplikaci jsou vlákna dvě a nemůžu to napojit takhle na přímo, takže jsem to proložil tím QtRelationalReaderStringHadler. Ale rád bych našel jednodušší řešení, protože takhle tam mám 80 řádků kódu jen kvůli tomu, abych řekl že se to má provolat z jednoho vlákna do druhého (a s rostoucím počtem metod by toho kódu bylo ještě víc).

P.S. Ta knihovna, ve které je RelationalReaderStringHadler není (a nemá být) závislá na Qt -- je to čisté C++.

P.P.S. Není nutné, aby GUI třída implementovala RelationalReaderStringHadler -- klidně by se ty metody mohly jmenoval jinak a mít jiné parametry, ale přišlo mi zbytečné vymýšlet nové rozhraní, když obsah je stejný.

Mám rád, když se lidé přou, znamená to, že vědí, co dělají, a že mají směr. Frantovo.cz, SQL-Výuka.cz, Nekuřák.net
30.9. 18:37 MadCatX
Rozbalit Rozbalit vše Re: Qt/C++, signály, sloty a vlánka
Mrknul jsem se na to o něco lépe a přijde mi, že to řešení se signálovacím meziobjektem máš navržené docela zvláštně. Z toho proxy objektu signáluješ sám sobě, což mi nedává žádný smysl. Mechanismus signálů a slotů v Qt sice umožňuje bezpečně signalizovat mezi více vlákny ale kód, jak ho máš napsaný teď sice funguje správně ale asi z jiného důvodu, než si myslíš.

QObject::connect má volitený pátý parametr, který řídí, zda se má příslušný slot zavolat okamžitě jako funkce z vlákna odesílatele nebo zda se má na event queue vlákna příjemce vložit informace, že se má ten slot zavolat. Ten parametr má výchozí hodnotu Qt::AutoConnection. To znamená, že se při zpracovávání signálu handler podívá, zda příjemce náleží stejnému vláknu, ze kterého byl zavolán signál a podle toho se zachová. Proto ti to tvoje docela kostrbaté řešení funguje. Kdybys ale třeba ten proxy objekt vytvořil z jiného než hlavního vlákna, rozbilo by se to.

Pokud nechceš, aby samotná business logika závisela na Qtčkách, budeš si sice pořád muset pomoct nějakým proxy objektem, který se ale dá napsat daleko jednodušeji. Napadá mě třeba toto:

Mějme nějakou business funkci, která klidně může běžet v samostatném vlákně. Ta podle situace generuje nějaký progress report:
void businessFunc()
{
  /* ... do work ... */
  
  switch (report) {
  case 0:
    outputProxy->startRelation();
    break;
  case 1:
    outputProxy->attribute();
    break;
  case 2:
    outputProxy->endOfPipe();
    break;
  }
}
Dále mějme jednoduchý interface OutputProxy:
class OutputProxy {
public:
  virtual void startRelation() = 0;
  virtual void attribute() = 0;
  virtual void endOfPipe() = 0;
};
Ten interface budou implementovat konkrétní proxy třídy určené k prodrátování dat z business logiky ke konkrétnímu výstupu. Nejjednodušeji třeba takto:
class QtOutputProxy : public OutputProxy, public QObject {
  Q_OBJECT
  
public:
  QtOutputProxy(MyUIWidget *target, QObject *parent = nullptr) :
    QObject(parent)
  {
    connect(this, &QtOutputProxy::sig_startRelation, target, &MyUIWidget::onStartRelation);
    connect(this, &QtOutputProxy::sig_attribute, target, &MyUIWidget::onAttribute);
    connect(this, &QtOutputProxy::sig_endOfPipe, target, &MyUIWidget::onEndOfPipe);
  }
  
  void startRelation() override
  {
    emit sig_startRelation();
  }
  
  void attribute() override
  {
    emit sig_attribute();
  }
  
  void endOfPipe() override
  {
    emit sig_endOfPipe();
  }
  
signals:
  void sig_startRelation();
  void sig_attribute();
  void sig_endOfPipe();
};
Pokud bys měl jiný typ výstupu, který by přesně nekopíroval způsob, jakým business logika reportuje, stačí ti upravit implementaci té proxy třídy; např. takhle:
class QtOutputProxyV2 : public OutputProxy, public QObject {
  Q_OBJECT
  
public:
  QtOutputProxyV2(MyUIWidgetV2 *target, QObject *parent = nullptr) :
    QObject(parent)
  {
    connect(this, &QtOutputProxyV2::updateDisplay, target, &MyUIWidgetV2::onUpdateDisplay);
  }
  
  void startRelation() override
  {
    emit updateDisplay("Relation started...");
  }
  
  void attribute() override
  {
    emit updateDisplay("Displaying attribute");
  }
  
  void endOfPipe() override
  {
    emit updateDisplay("End of pipe");
  }
  
signals:
  void updateDisplay(const QString &text);
};
xkucf03 avatar 30.9. 19:26 xkucf03 | skóre: 46 | blog: xkucf03
Rozbalit Rozbalit vše Re: Qt/C++, signály, sloty a vlánka
To znamená, že se při zpracovávání signálu handler podívá, zda příjemce náleží stejnému vláknu, ze kterého byl zavolán signál a podle toho se zachová.

Koukám na to a je mi divné, že to vůbec funguje, žádné chyby ani varování... Signály i sloty jsou v QtRelationalReaderStringHadlerconnect() dělám taky v této třídě (ovšem ještě v hlavním vlákně). Pak se ve vlákně na pozadí pustí ten reader->process(), který provolává QtRelationalReaderStringHadler ovšem jako normální metody -- a až uvnitř těchto metod to jde přes signál/slot (ale v rámci toho samého vlákna) a ze slotů se provolává target->startRelation(name, attributes) ovšem už zase jako normální metoda. A to ten target patří do jiného vlákna. Asi si s tím budu muset pohrát v debuggeru, protože takhle mi přijde, že to nemůže fungovat (ale na první pohled to funguje).

V tom void attribute(const string_t& value) se předává pouze reference a handler si má tu hodnotu přečíst/zkopírovat, ale nepatří mu -- jakmile skončí tahle metoda, tak ten, kdo ji volal, tu hodnotu smaže (resp. skončí rozsah její platnosti). Tady by mi ASan nadával, že používám již uvolněnou paměť, což se neděje. To nasvědčuje tomu, že ty signály/sloty jsou v mém případě synchronní (protože jsou v rámci jednoho vlákna/objektu). A celé to asi funguje jen díky tomu, že Qt se (náhodou) nerozbije, i když manipuluji s jeho GUI komponentami z jiného vlákna, než bych měl.

Předělám to zatím tím prvním způsobem -- tzn. přesunu sloty do RelpipeChartMainWindow -- tím by to mělo být asynchronní -- a zároveň změním jejich signatury, aby se pracovalo s kopií a ne referencí. BTW: když je to asynchronní, je zaručené pořadí? Protože na tom tady záleží -- ty atributy se plní do tabulky a řádek/sloupec se odvozuje od pořadí, v jakém ta hodnota přišla.

Mám rád, když se lidé přou, znamená to, že vědí, co dělají, a že mají směr. Frantovo.cz, SQL-Výuka.cz, Nekuřák.net
30.9. 21:00 MadCatX
Rozbalit Rozbalit vše Re: Qt/C++, signály, sloty a vlánka
To znamená, že se při zpracovávání signálu handler podívá, zda příjemce náleží stejnému vláknu, ze kterého byl zavolán signál a podle toho se zachová.

Koukám na to a je mi divné, že to vůbec funguje, žádné chyby ani varování... Signály i sloty jsou v QtRelationalReaderStringHadlerconnect() dělám taky v této třídě (ovšem ještě v hlavním vlákně). Pak se ve vlákně na pozadí pustí ten reader->process(), který provolává QtRelationalReaderStringHadler ovšem jako normální metody -- a až uvnitř těchto metod to jde přes signál/slot (ale v rámci toho samého vlákna) a ze slotů se provolává target->startRelation(name, attributes) ovšem už zase jako normální metoda. A to ten target patří do jiného vlákna. Asi si s tím budu muset pohrát v debuggeru, protože takhle mi přijde, že to nemůže fungovat (ale na první pohled to funguje).

Tu metodu, která jenom emituje příslušný signál ale voláš z worker vlákna. QObject::activate se tedy volá z worker vlákna. Pokud jsi instanci QtRelationalReaderStringHandleru vytvořil z hlavního vlákna, Qt to vidí a místo přímého zavolání slotu provedou queued zavolání z hlavního vlákna. Kdybys ten QtRelationalReaderStringHandler vytvořil z worker vlákna, už by to nechodilo.

V tom void attribute(const string_t& value) se předává pouze reference a handler si má tu hodnotu přečíst/zkopírovat, ale nepatří mu -- jakmile skončí tahle metoda, tak ten, kdo ji volal, tu hodnotu smaže (resp. skončí rozsah její platnosti). Tady by mi ASan nadával, že používám již uvolněnou paměť, což se neděje. To nasvědčuje tomu, že ty signály/sloty jsou v mém případě synchronní (protože jsou v rámci jednoho vlákna/objektu). A celé to asi funguje jen díky tomu, že Qt se (náhodou) nerozbije, i když manipuluji s jeho GUI komponentami z jiného vlákna, než bych měl.

Teď bych nerad kecal, ale pokud se provádí queued zavolání slotu, všechny jeho parametry ze zkopírují, takže původní objekty klidně mohou být zničeny. Proto taky musí mít vše, co zaregistruješ pomocí makra Q_DECLARE_METATYPE implicitiní defaultní konstruktor i copy konstruktor. Pokus o manipulaci s GUI objekty odjinud než z hlavního vlákna by velmi pravděpodobně aspoň vypsal varování do konzole.

Předělám to zatím tím prvním způsobem -- tzn. přesunu sloty do RelpipeChartMainWindow -- tím by to mělo být asynchronní -- a zároveň změním jejich signatury, aby se pracovalo s kopií a ne referencí. BTW: když je to asynchronní, je zaručené pořadí? Protože na tom tady záleží -- ty atributy se plní do tabulky a řádek/sloupec se odvozuje od pořadí, v jakém ta hodnota přišla.

Já bych ty argumenty klidně předával referencí, IMHO si tím ušetříš dvě zbytečná volání copy konstruktoru. Můžeš si to ověřit tím, že v copy konstruktoru necháš něco vypsat. Pořadí vykonání slotů by i při queued aktivaci zachováno být mělo.
vlastikroot avatar 30.9. 10:59 vlastikroot | skóre: 24 | blog: vlastikovo | Milevsko
Rozbalit Rozbalit vše Re: Qt/C++, signály, sloty a vlánka

Kdyz napises emit signal_endOfPipe(); tak je to uplne obycejny volani funkce (ten emit nic neni). Tu funkci ti vygeneruje MOC, jen se podiv do nejakeho moc_*.cpp souboru, tam uvidis jak presne to funguje.

Me dost pomohl k pochopeni tenhle clanek. Runtime se da delat vsechno mozny, napr. si z QMetaObject vytahnout seznam signalu/slotu a volat je pak pres int/string ID a list variant argumentu (ja mam takhle reseny remote method invocation). Otazka je, jestli pro tvy reseni neni lepsi to proste napsat rucne, nez si pridelavat zbytecny runtime overhead.

Sg1-game | We will destroys the Christian's legion ... and the cross, will be inverted | IP 80.188.182.6
xkucf03 avatar 30.9. 14:26 xkucf03 | skóre: 46 | blog: xkucf03
Rozbalit Rozbalit vše Re: Qt/C++, signály, sloty a vlánka
Příloha:
Kdyz napises emit signal_endOfPipe(); tak je to uplne obycejny volani funkce (ten emit nic neni).

Podle toho, co jsem četl v dokumentaci a na SO, takt to takhle funguje jen, když děláš emit ve stejném vlákně ve kterém to daný slot přijímá.

Navíc tam píší, že mezi více vlákny je to volání asynchronní, takže se vůbec divím, že mi to tak pěkně funguje a ASan mi nehlásí žádné chyby :-D

Asi přes ty signály/sloty budu všude předávat radši kopii hodnoty než referenci...

Mám rád, když se lidé přou, znamená to, že vědí, co dělají, a že mají směr. Frantovo.cz, SQL-Výuka.cz, Nekuřák.net
vlastikroot avatar 30.9. 18:52 vlastikroot | skóre: 24 | blog: vlastikovo | Milevsko
Rozbalit Rozbalit vše Re: Qt/C++, signály, sloty a vlánka
Ja narazel na tohle
# define Q_EMIT
#ifndef QT_NO_EMIT
# define emit
#endif
v src/corelib/kernel/qobjectdefs.h. Emit opravdu nic nedela, je to prazdny define.
Sg1-game | We will destroys the Christian's legion ... and the cross, will be inverted | IP 80.188.182.6
xkucf03 avatar 30.9. 19:01 xkucf03 | skóre: 46 | blog: xkucf03
Rozbalit Rozbalit vše Re: Qt/C++, signály, sloty a vlánka
Aha a má se tam tedy psát? K čemu to je?
Mám rád, když se lidé přou, znamená to, že vědí, co dělají, a že mají směr. Frantovo.cz, SQL-Výuka.cz, Nekuřák.net
30.9. 19:06 MadCatX
Rozbalit Rozbalit vše Re: Qt/C++, signály, sloty a vlánka
V principu nikam, je to pouze poznámka pro programátora, že volaná funkce není běžná funkce ale něco, co se bude zpracovávat signal/slot mechanismem.
2.10. 12:55 luky
Rozbalit Rozbalit vše QFuture
Nebylo by jednodussi pouzit QFutureWatcher a nechat si vysledky posilat do jednoho vlakna pres signal resultReadyAt?
4.10. 09:36 MadCatX | skóre: 20 | blog: dev_urandom
Rozbalit Rozbalit vše Re: QFuture
To si nemyslím. K signalizaci nějaké události mezi dvěma komponentami v Qtčkách slouží právě signály a sloty, které jsou de-facto veřejným API a jsou zvlášť vymyšlené tak, aby byly thread-safe. Přehazovat si mezi komponentami nějaký wrapper okolo future mi přijde trochu čuňácké. Další možností je třeba sypat výsledky to nějaké fronty stylem producent-konzument. To by ale znamenalo napsat si synchronizační mechanismus ručně, zatímco se signály a sloty to bude "prostě fungovat".
4.10. 12:15 luky
Rozbalit Rozbalit vše Re: QFuture
Proc? Cteci funkci spustim pres QtConcurrent::run a vracenej QFuture pouziju na update gui a zpracovani vysledku. Tradicni postup na asynchroni veci v QT...
4.10. 17:37 MadCatX | skóre: 20 | blog: dev_urandom
Rozbalit Rozbalit vše Re: QFuture
Future, aspoň jak ji chápu a používám já, je užitečná v situaci, kdy někde odpálím nějakou asynchronně běžící funkci a výsledek volání si přes future vyzvednu, když ho potřebuji. Tazatel má ale plnohodnotné worker vlákno, které během své činnosti produkuje nějaká data průběžně a proto mi zde použití future nepřijde šikovné. Ne, že by to nešlo ale ve výsledku to IMHO nebude o nic jednodušší než původně navrhované signály a sloty.
5.10. 11:07 luky
Rozbalit Rozbalit vše Re: QFuture
QFuture se hodi i na prubezne produkovana data, treba QFutureWatcher::progressValueChanged se muze bindnout do QProgressBar::setValue a rovnou ukazovat prubeh. Data jde vyzvedavat postupne pres QFutureWatcher::resultsReadyAt.
vlastikroot avatar 5.10. 19:56 vlastikroot | skóre: 24 | blog: vlastikovo | Milevsko
Rozbalit Rozbalit vše Re: QFuture
IMHO je QFuture prasarna na cokoliv co ma bezet stale a zpracovavat zpravy. Je to urceny na dej, ktery se ma nekdy dokoncit. Napr. request nad protokolem implementovanym temi zpravami (vyssi vrstva).
Sg1-game | We will destroys the Christian's legion ... and the cross, will be inverted | IP 80.188.182.6
5.10. 20:48 luky
Rozbalit Rozbalit vše Re: QFuture
Ja jsem zadani pochopil tak, ze se nacita vstup a postupne se zobrazuje. Kdyz je nacteno vse, program se neukonci, ale dal bezi.
Řešení 1× (xkucf03 (tazatel))
xkucf03 avatar 5.10. 21:40 xkucf03 | skóre: 46 | blog: xkucf03
Rozbalit Rozbalit vše Re: QFuture

Ano, má to číst postupně. Šlo mi o to, aby se uživateli zobrazily první řádky prvních tabulek, hned jak přijdou jejich data, a uživatel s nimi mohl pracovat, i když se ještě průběžně načítají další.

Pouštěl jsem si to přes:

(for x in `seq 5`; do sleep 1; relpipe-in-fstab; done) | pv --quiet --rate-limit 50 | relpipe-out-chart.qt

a tam je hezky vidět, jak data postupně naskakují v jednotlivých buňkách a dá se s tím GUI současně pracovat.

Nakonec jsem to vyřešil tím, že jsem proxy zjednodušil:

class QtRelationalReaderStringHadler : public QObject, public RelationalReaderStringHadler {

	Q_OBJECT
public:
	QtRelationalReaderStringHadler(QObject* parent) : QObject(parent) {
	}

	virtual ~QtRelationalReaderStringHadler() {
	}

	virtual void startRelation(string_t name, std::vector<AttributeMetadata> attributes) override {
		emit startRelationReceived(name, attributes);
	}

	virtual void attribute(const string_t& value) override {
		emit attributeReceived(value);
	};

	virtual void endOfPipe() override {
		emit endOfPipeReceived();
	};

signals:
	void startRelationReceived(const string_t name, std::vector<AttributeMetadata> attributes);
	void attributeReceived(const string_t value);
	void endOfPipeReceived();
};

Napojení signálů a slotů dělám v hlavním vlákně (původně to bylo v té proxy):

QObject::connect(&handler, &QtRelationalReaderStringHadler::startRelationReceived, &window, &RelpipeChartMainWindow::startRelation, Qt::ConnectionType::QueuedConnection);
QObject::connect(&handler, &QtRelationalReaderStringHadler::attributeReceived, &window, &RelpipeChartMainWindow::attribute, Qt::ConnectionType::QueuedConnection);
QObject::connect(&handler, &QtRelationalReaderStringHadler::endOfPipeReceived, &window, &RelpipeChartMainWindow::endOfPipe, Qt::ConnectionType::QueuedConnection);

A v GUI třídě implementuji sloty:

void RelpipeChartMainWindow::startRelation(const string_t name, std::vector<AttributeMetadata> attributes) {
	setStatusMessage(L"Reading relation: " + name);
	attributeCounter = 0;
	QSplitter* splitter = new QSplitter(Qt::Orientation::Vertical, tabs);

	currentTable = new QTableWidget(0, attributes.size(), splitter);
	QStringList headers;
	for (AttributeMetadata a : attributes) headers << QString::fromWCharArray(a.getAttributeName().c_str());
	currentTable->setHorizontalHeaderLabels(headers);
	currentTable->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeMode::ResizeToContents);

	// TODO: chart
	splitter->addWidget(new QPushButton("here will be the chart", splitter));
	splitter->addWidget(currentTable);
	int index = tabs->addTab(splitter, QString::fromWCharArray(name.c_str()));
	if (tabs->count() == 2) tabs->setCurrentIndex(index); // switch to the first relation (first tab is Options tab)
	tabs->setTabIcon(index, QIcon::fromTheme("application-vnd.oasis.opendocument.spreadsheet"));
}

void RelpipeChartMainWindow::attribute(const string_t value) {
	// TODO: draw chart
	integer_t column = attributeCounter % currentTable->columnCount();
	integer_t row = attributeCounter / currentTable->columnCount();
	if (row >= currentTable->rowCount()) currentTable->insertRow(currentTable->rowCount());
	currentTable->setItem(row, column, new QTableWidgetItem(QString::fromWCharArray(value.c_str())));
	attributeCounter++;
}

void RelpipeChartMainWindow::setStatusMessage(string_t message) {
	status->setText(QString::fromWCharArray(message.c_str()));
}

void RelpipeChartMainWindow::endOfPipe() {
	setStatusMessage(L"Reading successfully finished.");
}

Tu proxy bych nejradši zredukoval ještě víc, ale ty metody dané rozhraním RelationalReaderStringHadler jsou virtuální a nešlo mi je použít jako signály, takže jsou to normální metody a signál se z nich jen emituje, místo aby sama ta metoda byla signálem. Ale to už není tolik kódu navíc, takže jsem s tím už víceméně spokojený.

Nedokáži posoudit, jestli by to přes QFuture bylo jednodušší/lepší...

Mám rád, když se lidé přou, znamená to, že vědí, co dělají, a že mají směr. Frantovo.cz, SQL-Výuka.cz, Nekuřák.net
11.10. 06:24 Jardik
Rozbalit Rozbalit vše Re: QFuture
Nebylo. V podstate to mas, tak jak to v ramci qt muzes nejlepe udelat.

Založit nové vláknoNahoru

Tiskni Sdílej: Linkuj Jaggni to Vybrali.sme.sk Google Del.icio.us Facebook

ISSN 1214-1267   www.czech-server.cz
© 1999-2015 Nitemedia s. r. o. Všechna práva vyhrazena.