abclinuxu.cz AbcLinuxu.cz itbiz.cz ITBiz.cz HDmag.cz HDmag.cz abcprace.cz AbcPráce.cz
Inzerujte na AbcPráce.cz od 950 Kč
Rozšířené hledání
×
    včera 18:22 | Nová verze

    Byla vydána verze 0.2.0 v Rustu napsaného frameworku Pingora pro vytváření rychlých, spolehlivých a programovatelných síťových systémů. Společnost Cloudflare jej letos v únoru uvolnila pod licencí Apache 2.0.

    Ladislav Hagara | Komentářů: 0
    10.5. 19:11 | Nová verze

    Open source RDP (Remote Desktop Protocol) server xrdp (Wikipedie) byl vydán ve verzi 0.10.0. Z novinek je vypíchnuta podpora GFX (Graphic Pipeline Extension). Nová větev řeší také několik bezpečnostních chyb.

    Ladislav Hagara | Komentářů: 7
    10.5. 04:11 | Nová verze

    Rocky Linux byl vydán v nové stabilní verzi 9.4. Přehled novinek v poznámkách k vydání.

    Ladislav Hagara | Komentářů: 0
    9.5. 22:22 | Bezpečnostní upozornění

    Dellu byla odcizena databáze zákazníků (jméno, adresa, seznam zakoupených produktů) [Customer Care, Bleeping Computer].

    Ladislav Hagara | Komentářů: 17
    9.5. 21:11 | Zajímavý článek

    V lednu byl otevřen editor kódů Zed od autorů editoru Atom a Tree-sitter. Tenkrát běžel pouze na macOS. Byl napevno svázán s Metalem. Situace se ale postupně mění. V aktuálním příspěvku Kdy Zed na Linuxu? na blogu Zedu vývojáři popisují aktuální stav. Blíží se alfa verze.

    Ladislav Hagara | Komentářů: 32
    9.5. 14:33 | Pozvánky

    O víkendu 11. a 12. května lze navštívit Maker Faire Prague, festival plný workshopů, interaktivních činností a především nadšených a zvídavých lidí.

    Ladislav Hagara | Komentářů: 0
    8.5. 21:55 | Nová verze

    Byl vydán Fedora Asahi Remix 40, tj. linuxová distribuce pro Apple Silicon vycházející z Fedora Linuxu 40.

    Ladislav Hagara | Komentářů: 20
    8.5. 20:22 | IT novinky

    Představena byla služba Raspberry Pi Connect usnadňující vzdálený grafický přístup k vašim Raspberry Pi z webového prohlížeče. Odkudkoli. Zdarma. Zatím v beta verzi. Detaily v dokumentaci.

    Ladislav Hagara | Komentářů: 7
    8.5. 12:55 | Nová verze

    Byla vydána verze R14.1.2 desktopového prostředí Trinity Desktop Environment (TDE, fork KDE 3.5). Přehled novinek v poznámkách k vydání, podrobnosti v seznamu změn.

    JZD | Komentářů: 0
    7.5. 18:55 | IT novinky

    Dnešním dnem lze již také v Česku nakupovat na Google Store (telefony a sluchátka Google Pixel).

    Ladislav Hagara | Komentářů: 10
    Podle hypotézy Mrtvý Internet mj. tvoří většinu online interakcí boti.
     (64%)
     (7%)
     (13%)
     (16%)
    Celkem 161 hlasů
     Komentářů: 11, poslední 10.5. 18:00
    Rozcestník

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

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

    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-DK, Relational pipes

    Řešení dotazu:


    Odpovědi

    29.9.2018 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.2018 11:36 xkucf03 | skóre: 49 | 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-DK, Relational pipes
    30.9.2018 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.2018 19:26 xkucf03 | skóre: 49 | 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-DK, Relational pipes
    30.9.2018 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.2018 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.

    We will destroys the Christian's legion ... and the cross, will be inverted
    xkucf03 avatar 30.9.2018 14:26 xkucf03 | skóre: 49 | 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-DK, Relational pipes
    vlastikroot avatar 30.9.2018 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.
    We will destroys the Christian's legion ... and the cross, will be inverted
    xkucf03 avatar 30.9.2018 19:01 xkucf03 | skóre: 49 | 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-DK, Relational pipes
    30.9.2018 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.2018 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.2018 09:36 MadCatX | skóre: 28 | 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.2018 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.2018 17:37 MadCatX | skóre: 28 | 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.2018 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.2018 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).
    We will destroys the Christian's legion ... and the cross, will be inverted
    5.10.2018 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.
    xkucf03 avatar 5.10.2018 21:40 xkucf03 | skóre: 49 | 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-DK, Relational pipes
    11.10.2018 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.