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 06:00 | Komunita

Projekt Linux Counter (Wayback Machine) definitivně skončil. Linux Counter vznikl v roce 1993 a uživatelé Linuxu mohli sebe a své počítače do projektu registrovat. V roce 2011 byl Linux Counter převeden na novou doménu a kompletně přepsán. Nejenom na AbcLinuxu se ještě před deseti lety řešily žebříčky zemí dle počtu uživatelů Linuxu.

Ladislav Hagara | Komentářů: 0
dnes 01:33 | Nová verze

Společnost Purism informuje, že vývojové desky Librem 5 jsou již odesílány přispěvatelům. Telefony Librem 5 s PureOS respektující bezpečnost, svobodu a soukromí uživatelů by měly být odesílány v dubnu. Aktuálně je lze předobjednat za 599 dolarů. Po 7. lednu za 699 dolarů.

Ladislav Hagara | Komentářů: 0
dnes 00:11 | Nová verze

Byla vydána nová major verze 6.0 multiplatformního virtualizačního nástroje Oracle VM VirtualBox. Přehled novinek v Changelogu. Nově lze například virtuální počítač exportovat do infrastruktury Oracle Cloud.

Ladislav Hagara | Komentářů: 0
včera 17:11 | IT novinky

Společnost Wave Computing představila iniciativu MIPS Open. Architektura MIPS bude k dispozici jako open source.

Ladislav Hagara | Komentářů: 0
včera 16:11 | Nová verze

Pro vývojáře v programovacím jazyce Python: Krátce po vydání multiplatformního frameworku Qt ve verzi 5.12 bylo vydáno také Qt for Python 5.12. Knihovna pro vědecké výpočty v Pythonu SciPy (Wikipedie) byla po šesti měsících vývoje vydána v nové verzi 1.2.0.

Ladislav Hagara | Komentářů: 0
17.12. 22:33 | Zajímavý projekt

Na Humble Bundle byla spuštěna akce Humble Book Bundle: Hacking for the Holidays by No Starch Press. Za 1 dolar a více lze koupit 5 elektronických knih, za 8 dolarů a více lze koupit 10 elektronických knih a za 15 dolarů a více lze koupit 15 elektronických knih věnovaných počítačové bezpečnosti, penetračnímu testování, forenzní analýze nebo kryptografii od nakladatelství No Starch Press. Nákupem lze podpořit organizaci Electronic Frontier Foundation (EFF).

Ladislav Hagara | Komentářů: 0
17.12. 20:11 | Nová verze

Po devíti měsících od vydání verze 5.4 byla vydána verze 5.5 svobodného multiplatformního softwaru pro konverzi a zpracování digitálních fotografií primárně ve formátů RAW RawTherapee (Wikipedie). Nová verze RawTherapee je k dispozici také jako balíček ve formátu AppImage. Stačí jej stáhnout, nastavit právo ke spuštění a spustit.

Ladislav Hagara | Komentářů: 0
17.12. 15:44 | Upozornění

Národní úřad pro kybernetickou a informační bezpečnost (NÚKIB) vydal varování (pdf) před používáním softwaru i hardwaru společností Huawei Technologies Co., Ltd., a ZTE Corporation. Používání těchto prostředků představuje bezpečnostní hrozbu.

Ladislav Hagara | Komentářů: 51
17.12. 13:33 | Pozvánky

Spolek OpenAlt zve příznivce otevřených řešení a přístupu na 159. brněnský sraz, který proběhne v pátek 21. prosince od 18:00 v restauraci Na blbým místě na adrese Pellicova 5b.

Ladislav Hagara | Komentářů: 1
17.12. 12:55 | Bezpečnostní upozornění

V relačním databázovém systému SQLite (Wikipedie) byla nalezena bezpečnostní chyba pojmenovaná Magellan. Chyba se týká také webových prohlížečů postavených na Chromiu podporujících WebSQL API (POC). Bezpečnostní chyba byla opravena v SQLite 3.26.0 a v Chromiu 71.0.3578.80.

Ladislav Hagara | Komentářů: 1
Chystáte se přejít na Wayland na „desktopu“?
 (25%)
 (9%)
 (11%)
 (31%)
 (25%)
Celkem 151 hlasů
 Komentářů: 19, poslední 14.12. 18:37
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: 1785×

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.