Portál AbcLinuxu, 2. května 2025 05:48
sem si začala psát mqtt widget pro plasma/kde5 desktopový prostředí protože žádnej hotovej mqtt widget tam neměli :O :/ eště to neni hotový ale ten stou rsm čtečkou sem taky strká nehotový :D ;D
momentálně to neni eště uplně vodladěný muže to udělat moc moc špatný věci jako zničit počítač smazat disk nebo popoužití widgetu když si vnoci špatně přikrijete nohy tak vás muže přijít stalman kousnout dopaty :O :O
Update: konfigurační soubor se nemenuje 'config.xml' ale 'main.xml' :D
Plazma widgety na plochu kterým voni řikaj 'plasmoidy' se pišou hlavně v nějakejch divnejch qml skriptech který umožňujou používat javascript a c++ s knihovnama qt (tech qt takže to asi jako neni uplně svobodný :O :O vono qt nějak měnilo ty svý licence takže nikdo nasvěte neví který nástroje/knihovny sou svobodný který zdarma který zapeníze a který se nesměj používat vubec :O :O)
Tendleten čudlikovej widget má c++ backend kterej se do widgetu/plazmoidu dá strčit jako takovej jakože plugin. Na dělaní MQTT to používá cčkovou knihovnu paho vod eclipse hele. Maj tudletu knihovnu pro spousty jinejch jazyků i pro c++ ale ta vobyč cčková má v debianu bullseye rovnou balíček tak sem vzala tu ikdyž to asi jako neni správný c++kový řešení nějaký dělání s vobyč ukazatelama na funkce strukturama nebo polema/ukazatelama misto nějakejch těch jejich std::kontejnerů
jestli vás někoho jako napadlo proč sem nepoužila *.js knihovičku kterou tam eclipse taky má nóó tak vona boužel v qml nefunguje :O :/ qml umí nějaký jednoduchý websockety ale neštimuje to dosebe s touhletou knihovnou. Další možnost bylo vzit websockety a povidat si nima s nějakým vodděleným server skriptem jak to jako dělá třeba panon widget hele ale toje takový divný ne?? :O :O nóó takže sem to teda jakoby namastila v c++
Jak si jako udělat c++kovej plugin a registrovat qt/c++ třídu do qml sem vobšlehla tady u nějakýho pana kotelníka hele :D
Dohromady sou tam zatim jenom tři c++ třídy nastrkaný ve složce plugin. Singleton/jedináček toho našeho klienta pro dělání mqtt povidání v 'KlientProMQTT.h', třída co dělá jenom že přečte *.txt/*.json a strčí nám ho do qml (qml samo vosobě asi jako neumí číst soubory nadisku :O :O) v 'NacitadloSouboru.h' a pak 'PlasmoidPlugin.h' která dělá že nám ty dvě veci zaregistruje jako datový typy/oběty/elementy a mužem to pak v qml naimportovat jako knihovnu
Plazmoidy/kde widgety se musej skládat z nějaký složšky do který se dá soubor 'metadata.desktop' a do něj se napišou věci jako jak se ten widget/plazmoid bude menovat kde má skovanej hlavní skriptík 'main.qml' jakou má ikonku a takový věci. vtý složšce s 'metadata.desktop' si vyrobíme další složšku kterou pomenujem 'contents' a vní eště dvě další složsky, 'config' a 'ui'. Ve složšce 'config' si v souboru config.xml main.xml si podle návodu hele nadefinujem proměný jaký si widget/plazmoid umí pamatovat a v souboru config.qml umístění *.qml skriptíky jednotlivejch tabů v nastavování widgetu.
Ve složšce 'ui' musíme mit někde skovanej 'main.qml' skript kterej je hlavní součástka widgetu do kterýho si pak mužem importovat další voběkty/qml knihovny který mužou bejt skovaný taky v nějakejch svejch dalších složškách. plazmový čudliky maj všecky jednotlivý druhy čudliků v jedný složšce kde má každej druch svou 'třídu'. Je tam taková jakože společná třída 'Cudlik.qml' ze který vostatní čudliky 'děděj'.
Zatim to umí jenom sedum druhů čudliků:
složšková/adresářová struktura plazmovejch čudliků je takovádle zatim
. ├── CMakeLists.txt ├── package │ ├── contents │ │ ├── config │ │ │ ├── config.qml │ │ │ └── main.xml │ │ └── ui │ │ ├── cudliky │ │ │ ├── Cudlik.qml │ │ │ ├── Graf.qml │ │ │ ├── Kruh.qml │ │ │ ├── Moznosti.qml │ │ │ ├── Napis.qml │ │ │ ├── Prepinadlo.qml │ │ │ ├── Soupatko.qml │ │ │ └── Tlacitko.qml │ │ ├── main.qml │ │ └── NastavovaniCudliku.qml │ └── metadata.desktop └── plugin ├── CMakeLists.txt ├── KlientProMQTT.cpp ├── KlientProMQTT.h ├── NacitadloSouboru.cpp ├── NacitadloSouboru.h ├── PlasmoidPlugin.cpp ├── PlasmoidPlugin.h └── qmldir
má to různý závislosti. pro něco na nějakým novějším debianu s kde založeným doinstalujem všecko důležitý příkazem
sudo apt install cmake extra-cmake-modules libpaho-mqtt-dev qtdeclarative5-dev libkf5plasma-dev libssl-dev libcjson-dev
samotnej widget se kompiluje a instaluje tak že se vleze do tý složšky stim prvním CMakeLists.txt souborem a udělá se
cmake . make sudo make install
nóó a pak se to voběvuje mezi nainstalovanejma widgetama a mužem si to strčit naplochu ;D
pokuď si vtom uděláte nějaký změny kdyžuž máte widget puštěnej a přeinstalujete si ho tak pak jakoby musíte zavolat
plasmashell --replace
jinak tam zustane ten starej nezmeněnej
Konfiguruje se to načtením json kterým se naštelujou přihlašovací údaje barvičky a tak podobně
každej čudlik tam musí mit vyplněný atributy druh, název, xovou a ipsilonovou souřadnici v gridu/mřížšce, topic.
nepoviný atributy sou booly jestli muzePublikovat a muzePrijimat, QoS/kvalita služby, retain, jeCelociselny (má pochopytelně smysl jakoby jenom u čudliků co dělaj s číslama :D ;D), šířka a výška v počtu vobsazenejch chlívečků v tý mřížšce do který se budou čudliky strkat, jsonElement pro jakože hóódně primitivní parsování přijímanejch mqtt zpráviček v json formátu a další který mužete vidět v ukázkovým configu :D ;D
jestli chcete mit víc různejch widgetů protože vám nestačí jeden si musíte pohlídat aby měl jakoby každej jiný id!!!!!! :O :O jinak se budou navzájem vodpojovat :O :O
{ "broker": { "adresa": "tcp://123.456.123.456:1883", "id": "widgetNaPlose", "uzivatel": "greta", "heslo": "1234" }, "grid": { "sirka": 4, "vyska": 9 }, "barvy": { "pozadi": "#901a4314", "text": "#efe7bc", "aktivni": "#fad02c", "kontura": "#333652" }, "cudliky": [ { "druh": "Prepinadlo", "nazev": "Čudlík", "onHodnota": 1, "offHodnota": 0, "muzePublikovat": true, "muzePrijimat": true, "x": 0, "y": 0, "topic": "nejakej_topic/blablabla1", "retain": true, "QoS": 2 }, { "druh": "Prepinadlo", "nazev": "Druhej", "onHodnota": 1, "offHodnota": 0, "muzePublikovat": true, "muzePrijimat": true, "x": 1, "y": 0, "topic": "nejakej_topic/blablabla2", "retain": true, "QoS": 2 }, { "druh": "Prepinadlo", "nazev": "Třetí", "onHodnota": 1, "offHodnota": 0, "muzePublikovat": true, "muzePrijimat": true, "x": 2, "y": 0, "topic": "nejakej_topic/blablabla3", "retain": true, "QoS": 2 }, { "druh": "Prepinadlo", "nazev": "Čtvrtý", "onHodnota": 1, "offHodnota": 0, "muzePublikovat": true, "muzePrijimat": true, "x": 3, "y": 0, "topic": "nejakej_topic/blablabla4", "retain": true, "QoS": 2 }, { "druh": "Kruh", "nazev": "CO2 v atmosféře", "minHodnota": 123, "maxHodnota": 987, "muzePublikovat": true, "muzePrijimat": true, "x": 1, "y": 1, "sirka": 2, "vyska": 2, "topic": "nejakej_topic/x", "QoS": 0 }, { "druh": "Soupatko", "nazev": "Víkon vysavačů", "minHodnota": 0, "maxHodnota": 1000, "znakyZaHodnotou": "TW", "muzePublikovat": true, "muzePrijimat": true, "x": 0, "y": 1, "sirka": 1, "vyska": 1, "topic": "nejakej_topic/blah1", "QoS": 0 }, { "druh": "Soupatko", "nazev": "Víkon konvic", "minHodnota": 0, "maxHodnota": 1000, "znakyZaHodnotou": "TW", "muzePublikovat": true, "muzePrijimat": true, "x": 0, "y": 2, "topic": "nejakej_topic/blah2", "QoS": 0 }, { "druh": "Soupatko", "nazev": "Promořování", "minHodnota": 1234, "maxHodnota": 4321, "znakyZaHodnotou": " ☠", "muzePublikovat": true, "muzePrijimat": true, "x": 3, "y": 1, "topic": "nejakej_topic/promor", "QoS": 0 }, { "druh": "Kruh", "nazev": "Promořování", "minHodnota": 0, "maxHodnota": 4321, "znakyZaHodnotou": " ☠", "muzePublikovat": false, "muzePrijimat": true, "x": 3, "y": 2, "topic": "nejakej_topic/promor", "QoS": 0 }, { "druh": "Graf", "nazev": "Vysázenejch stromků vlese", "muzePublikovat": false, "muzePrijimat": true, "x": 0, "y": 4, "sirka": 4, "vyska": 2, "topic": "nejakej_topic/x", "jeCelociselny": false, "QoS": 0 }, { "druh": "Graf", "nazev": "Tloušťka ledovcový vrstvy", "muzePublikovat": false, "muzePrijimat": true, "x": 0, "y": 7, "sirka": 2, "vyska": 2, "topic": "nejakej_topic/x", "jeCelociselny": false, "QoS": 0 }, { "druh": "Napis", "nazev": "Kobaltová raketa zaměřená na:", "muzePublikovat": false, "muzePrijimat": true, "maOkraj": false, "x": 2, "y": 6, "sirka":2, "vyska": 2, "topic": "nejakej_topic/raketa" }, { "druh": "Moznosti", "nazev": "vybrat cíl", "muzePublikovat": true, "muzePrijimat": true, "x": 2, "y": 8, "topic": "nejakej_topic/raketa", "seznamMoznosti": ["praha", "brno", "brusel"] }, { "druh": "Tlacitko", "nazev": "Odpálení kobaltový rakety", "napis": "Odpálit!!!!", "coPosila": "bum!!!!!", "muzePublikovat": true, "muzePrijimat": false, "x": 3, "y": 8, "topic": "nejakej_topic/raketa" } ] }
https://zren.github.io/kde/docs/widget/
https://develop.kde.org/docs/plasma/widget/
https://techbase.kde.org/Development/Tutorials/Plasma5/QML2/GettingStarted
https://doc.qt.io/qt-5/qtquick-qmlmodule.html
https://api.kde.org/frameworks-api/frameworks-apidocs/frameworks/kquickcharts/html/index.html
https://develop.kde.org/docs/plasma/widget/configuration/
https://github.com/kotelnik/plasma-applet-weather-widget
tady dole a taky tady hele
plugin/CMakeLists.txt
set(plasmoidplugin_SRCS PlasmoidPlugin.cpp KlientProMQTT.cpp NacitadloSouboru.cpp ) set(THREADS_PREFER_PTHREAD_FLAG ON) find_package(Threads REQUIRED) find_package(OpenSSL REQUIRED) add_library(plasmoidplugin SHARED ${plasmoidplugin_SRCS}) target_compile_features(plasmoidplugin PRIVATE cxx_std_20) target_compile_options(plasmoidplugin PRIVATE -fexceptions) target_include_directories(plasmoidplugin PRIVATE ${OPENSSL_INCLUDE_DIR}) target_link_libraries(plasmoidplugin Qt5::Quick KF5::Plasma paho-mqtt3as cjson ${OPENSSL_LIBRARIES} Threads::Threads) install(TARGETS plasmoidplugin DESTINATION ${QML_INSTALL_DIR}/org/kde/private/mqttCudliky) install(FILES qmldir DESTINATION ${QML_INSTALL_DIR}/org/kde/private/mqttCudliky)
plugin/KlientProMQTT.h
#ifndef KLIENT_PRO_MQTT_H #define KLIENT_PRO_MQTT_H #include <MQTTAsync.h> #include <cjson/cJSON.h> #include <string.h> #include <unistd.h> #include <string> #include <map> #include <qt5/QtCore/QDebug> #include <qt5/QtCore/QObject> #include <qt5/QtCore/QThread> #include <qt5/QtCore/QString> #include <qt5/QtQml/QJSEngine> #include <qt5/QtQml/QQmlEngine> #define CEKACI_CAS 10000L #define VYCHOZI_QOS 0 //jakej je jako stav připojení enum StavPripojeni { CHYBA, NEPRIPOJENEJ, PRIPOJENEJ, ZAPSANEJ }; // podědíme z class KlientProMQTT : public QThread { // qt oběkty to tady ve třídě musej mit :O :O Q_OBJECT public: // jeto singleton/jedináček takže mužem mit jenom jednu instanci třídy vyrobenou // skovanou v nějaký statický proměný třídy // konstrukor je private a misto něj se volá nějaká statická metoda co vrací tu instanci společnou // singleton budem pak registrovat ve třídě 'PlasmoidPlugin' jako qml typ funckí 'qmlRegisterSingletonType' // ata chce aby metoda getInstance hamala tydlety ukazatele ;D static KlientProMQTT * ziskatInstanci(QQmlEngine * engine, QJSEngine * scriptEngine); //void poslat(const QString & topic, const QString & zprava, unsigned int qos, bool retained); //StavPripojeni getStav() const {return stav;} // destruktor co dělá že uklidí ~KlientProMQTT(); signals: // signály který qt oběkt bude posílat // v qml nato sou napojený chytací metody // definujem jakoby jenom hlavičky metod žádný tělíčka. všecko co se do tý signal metody narve // se pak strčí do argumentu na stejný pozici vtěch qml/js funckí napojenejch // v c++/qt zdrojáčku se pak před nima musí psát 'Q_EMIT' abyto fungovalo :O :O // signály co dělaj že předaj klientem chycenou zprávu do qml frontendu void zpravaDoFrontendu(const QString & topic, const QString & obsahZpravy); void zpravaJsonDoFrontendu(const QString & topic, const QString & obsahZpravy); // signály který mqtt klient posílá když se vodpojí/připojí/něco se rozbije void odpojenej(); void pripojenej(); void chyba(QString textChyby = "chyba :D"); public Q_SLOTS: // funkce co připojujou a vodpojujou k/vod mqtt brokera // funkce připojit při úspěchu vrací true a při asi závažnějších chybách blije vyjímky bool pripojit(); void odpojit(); // nastaví atributy m_adresa m_id a tydlety všecky který potřebujem k připojování k brokeroj a vyrobí podle nich // struktury m_connOpts, m_discOpts...... který budem nastrkávat do funckí z knihovničky MQTTAsync.h void nastavit(const QString & adresa, const QString & id, const QString & uzivatel, const QString & heslo); // nastaví seznam topiců + jejich qos/kvalitu služby z qml/javascriptovejch polí strčenejch sem // každej topic by tam měl bejt jenom jednou napsanej // nemužem použít QMap nebo nějakej jinej víc chytřejší vobal protože naněco takovýho neumí qml převíst :O :/ // převody mezi qml a qt/c++ maj popsaný tady hele https://doc.qt.io/qt-5/qtqml-cppintegration-data.html void nastavitSeznamTopicu(const std::vector<QString> & topicy, const std::vector<int> & qos); // pošlem z klienta zprávu // žere to jakoby ty qstringy ale ty sou převáděný na vobyč str::stringy uvnitř takže byse dotoho němeli strkat // žádný víc speciální znaky protože byje to asi jako nemuselo převíst :O :O // mqtt topicy by asi jako taky měli bejt složený jenom s vobyč ascii znáčků si myslim :O :O void poslat(const QString & topic, const QString & obsahZpravy, unsigned int qos = VYCHOZI_QOS, bool retained = false); // odpojí klienta nastaví adresu id etc + topicy/qos a zkusí znova udělat připojení void reset(const QString & adresa, const QString & id, const QString & uzivatel, const QString & heslo, const std::vector<QString> & topicy, const std::vector<int> & qos); // metoda na přešmiknutí vlákna // zavolá requestInterruption() a počká na ukončení void stop(); // metoda co řiká jestli je mqtt klient připojenej bool jePripojenej() const {return (bool)MQTTAsync_isConnected(m_klient);} // jestli vlákno běží bool bezi() const {return isRunning();} private: // tajenj privátní konstruktor takže muže bejt zavolanej jenom z nějaký jiný metody týdletý třídy // singleton se nám vyrábí jenom v metodě 'ziskatInstanci' explicit KlientProMQTT(QObject * parent = nullptr); // tady je skovaná ta jediná instance týdletý třídy static KlientProMQTT * instance; // metoda na zapsání k topicu // před zapsáním nejdřiv musíme bejt připojený k brokeroj bezchybně void subscribe(); // vypuštění vlákna // jenom se tam furt hlídá jestli je přiojenej/nepřipojenej // jedinej důvod proč misto vobyčejnýho qoběktu s callbackama je použitý vlákno jeto že když klient klient jednou ztratí spojení // a připojí se znova tak už to pak nechtělo dělat 'onConnect' callback takže nebylo možný z c++/qt říct qml/gui že neni připojenej // artelnativní řešení by asi jako mohlo bejt mit v main.qml nějakej časovač co by furt koukal jestli je klinet připojenej :O :O // možná tam jakoby maj voni nějakou chybku nevim :O :O void run() override; StavPripojeni stav; // přihlašovací údaje/data pro mqtt brokera std::string m_adresa; std::string m_id; std::string m_uzivatel; std::string m_heslo; // ke kolika topiců se chcem zapsat a seznam jejich názvů + QoS(kvalita služby) // mqtt vobyč cčková knihova chce vobyč starý cčkový pole/ukazate navstup size_t m_pocetTopicu; char ** m_nazvyTopicu; int * m_QoSsTopicu; // mqttasync.h knihovnou vyrobenej klient // s nim pracujou všecky ty funkce ztý knihovy MQTTAsync m_klient; // různý struktury nastavovací při připojování/vodpojování m_klienta // strkaj se do nich různý parametry + věčinou dvojice ukazatelů na funcke který sou pak // zavolaný jako callbacky potom cose to povede/nepovede udělat // připojovací MQTTAsync_connectOptions m_connOpts; // vodpojovací MQTTAsync_disconnectOptions m_discOpts; //na posílání zpráv MQTTAsync_responseOptions m_sendRespOpts; // subscribovací/na zapisování do topiců MQTTAsync_responseOptions m_subscribeRespOpts; // a tady sou ty různý callbacky jako statický metody // vobyčejný metody voběktu by to neumělo zhamat protože ty maj v c++ prej // jako takovej první neviditelnej argument ukazatel na sám sebe voběkt nebo takovýho něco // context je vodkaz na mqtt klienta připojenýho kterýho si musíme přetypovávat z void když ho třeba potřebujem // callback při vodpojení úspěch/neůspěch static void onDisconnect(void * context, MQTTAsync_successData * odpoved); static void onDisconnectFail(void * context, MQTTAsync_failureData * odpoved); // callbacky pro zapisování k topicům static void onSubscribe(void * context, MQTTAsync_successData * odpoved); static void onSubscribeFail(void * context, MQTTAsync_failureData * odpoved); // připojování // onConnect callbacky nejsou volaný při automatickým znovupřipojení klienta static void onConnect(void * context, MQTTAsync_successData * odpoved); static void onConnectFail(void * context, MQTTAsync_failureData * odpoved); // callbacky pro posílání zpráviček klientem static void onSend(void * context, MQTTAsync_successData * odpoved); static void onSendFail(void * context, MQTTAsync_failureData * odpoved); // při doručení zprávy static void onDeliver(void * context, MQTTAsync_token token); // ztráta spojení static void onConnLost(void * context, char * duvod); // při přijetí zprávy // pozor je tam hulvátstyle kontrola jestli je zpráva string/řetězec ve formátu json že // se kouká na první znak jestli toje složená závorka '{' static int onMessage(void * context, char * topicNazevStr, int topicStrZnaku, MQTTAsync_message * zprava); }; #endif
plugin/KlientProMQTT.cpp
#include "KlientProMQTT.h" using namespace std; KlientProMQTT * KlientProMQTT::instance = nullptr; KlientProMQTT * KlientProMQTT::ziskatInstanci(QQmlEngine * engine, QJSEngine * scriptEngine) { Q_UNUSED(engine); Q_UNUSED(scriptEngine); if(KlientProMQTT::instance == nullptr) { KlientProMQTT::instance = new KlientProMQTT(); } return KlientProMQTT::instance; } // konstrukotr skoro nic nedělá // všecko bude nastavovaný jinejma metodama KlientProMQTT::KlientProMQTT(QObject * parent) : QThread(parent), stav(NEPRIPOJENEJ), m_nazvyTopicu(nullptr), m_QoSsTopicu(nullptr) { } // metoda stop poprosí vlákno vo to aby se jakože přešmiklo a počká void KlientProMQTT::stop() { requestInterruption(); wait(); } // run se zkusí připojit a pak furt vesmyčce kouká jestli neni znova připojenej void KlientProMQTT::run() { if(!pripojit()) { qDebug()<<"nepovedlo se pripojit klienta"; return; } while(!isInterruptionRequested()) { usleep(CEKACI_CAS); if(stav == NEPRIPOJENEJ && jePripojenej()) { stav = PRIPOJENEJ; Q_EMIT pripojenej(); } } // když bylo vlákno přešmiknutý tak zkusíme vodpojit klienta // jestli je připojenej si metoda vodpojit hlídá sama odpojit(); // qt::debugovací výstup qDebug()<<"mqtt client konci!!!!!!!!!"; } // nastavování údajů na připojování k brokeroj void KlientProMQTT::nastavit(const QString & adresa, const QString & id, const QString & uzivatel, const QString & heslo) { // musíme převíst qstringy na vobyč std::stringy // divný/neascii znkay se asi jako ztratěj tak pozor jako :O :O m_adresa = adresa.toStdString(); m_id = id.toStdString(); m_uzivatel = uzivatel.toStdString(); m_heslo = heslo.toStdString(); // inicianiluzujeme si ty struktury pro připojování/vodpojování/etc... výhozíma hodnotama m_connOpts = MQTTAsync_connectOptions_initializer; m_discOpts = MQTTAsync_disconnectOptions_initializer; m_sendRespOpts = MQTTAsync_responseOptions_initializer; m_subscribeRespOpts = MQTTAsync_responseOptions_initializer; // noa ty výhozí hodnoty jakože teťko přepišem nějakejma svejma kde potřebujem změnit // maximální možnej čas v sekundách kterej muže utýct vod posledního komunikování klienta s mqtt brokerem // když upline tak se pošle jakoby takovej 'ping' aby si navzájem řekli že sou voba eště naživu :O :O m_connOpts.keepAliveInterval = 20; // když máme zapnutý automatický znovupřipojování tak neni prej dobrý mit zapnutý promazávání session cache m_connOpts.cleansession = 0; //callbacky m_connOpts.onSuccess = KlientProMQTT::onConnect; m_connOpts.onFailure = KlientProMQTT::onConnectFail; // uživatel a heslo m_connOpts.username = m_uzivatel.c_str(); m_connOpts.password = m_heslo.c_str(); // zapnem automatický znovupřipojování a nastavíme znovupřipojovací interval ve kterým to jakoby tamto // znovupřipojení zkouší třeba vod jedný do tří vteřin m_connOpts.automaticReconnect = 1; m_connOpts.minRetryInterval = 1; m_connOpts.maxRetryInterval = 3; // u vostatních struktur callbacky m_subscribeRespOpts.onSuccess = KlientProMQTT::onSubscribe; m_subscribeRespOpts.onFailure = KlientProMQTT::onSubscribeFail; m_sendRespOpts.onSuccess = KlientProMQTT::onSend; m_sendRespOpts.onFailure = KlientProMQTT::onSendFail; m_discOpts.onSuccess = KlientProMQTT::onDisconnect; m_discOpts.onFailure = KlientProMQTT::onDisconnectFail; // všecky tydlety struktury maj v sobě skovanej context/m_klienta // ho tam šoupnem až si ho v metodě připojit() vyrobíme ;D } // převedem vectory topiců/qos na ty ukazatelový pole. nejde to udělat nějak víc líp????? :O :O // předpokádaj se stejně dlouhatánský pole aže se topicy nebudou vopakovat (to se hlídá v main.qml ) // neví někdo jestli de nějak v c++20 udělat todleto hele https://www.cplusplus.com/forum/general/228918/ :O :O :O :O void KlientProMQTT::nastavitSeznamTopicu(const std::vector<QString> & topicy, const std::vector<int> & qos) { if(topicy.size() != qos.size()) throw runtime_error("neco je v qml moc spatne protoze do metody \'nastavitSeznamTopicu\' sou strceny vektory o ruzny dylce :O :O"); if(topicy.size() == 0) qDebug()<<"do metody \'nastavitSeznamTopicu\' se strkaj vectory o nulovy dylce :O :O"; if(m_nazvyTopicu != nullptr) delete [] m_nazvyTopicu; if(m_QoSsTopicu != nullptr) delete [] m_QoSsTopicu; m_pocetTopicu = topicy.size(); m_nazvyTopicu = new char * [m_pocetTopicu]; m_QoSsTopicu = new int [m_pocetTopicu]; for(size_t i = 0; i < m_pocetTopicu; i++) { m_nazvyTopicu[i] = strdup(topicy[i].toStdString().c_str()); m_QoSsTopicu[i] = qos[i]; qDebug()<<"klient se bude chtit pripojovat k topicu \'"+QString(m_nazvyTopicu[i])+"\' s QoS == "<<m_QoSsTopicu[i]; } } bool KlientProMQTT::pripojit() { // návratovej chybvoej kód když se něco nepovede // mužem si pak dycky najít v hlavičkovým souboru 'MQTTAsync.h' tý paho knihovničky cože // to jako znamená tadleta chyba int rc; // vyrobíme m_klienta if ((rc = MQTTAsync_create(&m_klient, m_adresa.c_str(), m_id.c_str(), MQTTCLIENT_PERSISTENCE_NONE, nullptr))!= MQTTASYNC_SUCCESS) { string err = "nepodarilo se vyrobit MQTTAsync klienta!!!!!!!!!!!!\nnavratovej chybovej kod: "; err += to_string(rc); throw runtime_error(err); } if ((rc = MQTTAsync_setCallbacks(m_klient, m_klient, onConnLost, onMessage, onDeliver)) != MQTTASYNC_SUCCESS) { string err = "nepodarilo se klientoj nastavit callbacky!!!!!!!!!!!!\nnavratovej chybovej kod: "; err += to_string(rc); throw runtime_error(err); } // vyrobenýho klienta jakoby nastrkáme do těch nastavovacích struktur m_connOpts.context = m_klient; m_subscribeRespOpts.context = m_klient; m_sendRespOpts.context = m_klient; m_discOpts.context = m_klient; if((rc = MQTTAsync_connect(m_klient, &m_connOpts)) != MQTTASYNC_SUCCESS) { QString err = "nepodarilo se pripojit klienta!!!!!!!!!!!! navratovej chybovej kod: " + QString::number(rc); qDebug()<<err; Q_EMIT chyba("nepodarilo se pripojit klienta!!!!\nmrkni na prihlasovaci udaje"); return false; } while(stav == NEPRIPOJENEJ) usleep(CEKACI_CAS); if(stav == CHYBA) { QString err = "nepodarilo se subscribnout klienta!!!!!!"; qDebug()<<err; Q_EMIT chyba(err); return false; } return true; } void KlientProMQTT::odpojit() { // když neni připojenej tak ho nemužem začít vodpojovat // mqttasync.h nato nadává if(!jePripojenej()) return; int rc; if ((rc = MQTTAsync_disconnect(m_klient, &m_discOpts)) != MQTTASYNC_SUCCESS) { string err = "nepodarilo se zacit vodpojovani klienta!!!!!!!!!!!!\nnavratovej chybovej kod: " + to_string(rc); throw runtime_error(err); } // budem čekat než to jakoby nějak dopadne :D while (stav == PRIPOJENEJ && stav != CHYBA) usleep(CEKACI_CAS); // když to skončí chybou tak máme nějakej problémek kterej tady asi jako nevyřešíme if(stav == CHYBA) { string err = "nepodarilo se vodpojit klienta!!!!!!!!!!!!"; throw runtime_error(err); } } void KlientProMQTT::subscribe() { if( stav != PRIPOJENEJ) { string err = "klient musi bejt pred subscribnutim pripojenej!!!!"; throw runtime_error(err); } int rc; if ((rc = MQTTAsync_subscribeMany(m_klient, m_pocetTopicu, m_nazvyTopicu, m_QoSsTopicu, &m_subscribeRespOpts)) != MQTTASYNC_SUCCESS) { string err = "nepodarilo se zacit subscribovat klienta!!!!!!!!!!!!\nnavratovej chybovej kod: " + to_string(rc); throw runtime_error(err); } } void KlientProMQTT::poslat(const QString & topic, const QString & zprava, unsigned int qos, bool retained) { // když neni klient připojenej nic se nebude posílat // posílání zpráviček z vodpojenýho klienta má jakoby zabránit zamikání čudliků v gui dycky když je klient vodpojenej if(!jePripojenej()) { qDebug()<<"klient neni pripojenej takze se nic nebude posilat"; return; } string zpravaStdstr = zprava.toStdString(); MQTTAsync_message msg = MQTTAsync_message_initializer; msg.payload = (void *)zpravaStdstr.c_str(); msg.payloadlen = zpravaStdstr.length(); msg.qos = qos; msg.retained = int(retained); int rc; if ((rc = MQTTAsync_sendMessage(m_klient, topic.toStdString().c_str(), &msg, &m_sendRespOpts)) != MQTTASYNC_SUCCESS) { string err = "nepodarilo se poslat str zpravu!!!!!!!!!!!!\nnavratovej chybovej kod: " + to_string(rc); throw runtime_error(err); } } // vodpojíme nastavíme a zkusíme znova připojit void KlientProMQTT::reset(const QString & adresa, const QString & id, const QString & uzivatel, const QString & heslo, const std::vector<QString> & topicy, const std::vector<int> & qos) { odpojit(); if(m_klient != nullptr) MQTTAsync_destroy(&m_klient); nastavit(adresa, id, uzivatel, heslo); nastavitSeznamTopicu(topicy, qos); stav = NEPRIPOJENEJ; pripojit(); } //callbacky void KlientProMQTT::onDisconnect(void * context, MQTTAsync_successData * odpoved) { (void)context; (void)odpoved; instance->stav = NEPRIPOJENEJ; Q_EMIT instance->odpojenej(); qDebug()<<"uspesne odpojeni"<<Qt::endl; } void KlientProMQTT::onDisconnectFail(void * context, MQTTAsync_failureData * odpoved) { (void)context; instance->stav = CHYBA; Q_EMIT instance->chyba("neuspesne vodpojeni"); qDebug()<<"neuspesne vodpojeni "<<Qt::endl<<"chybovy kod: "<<odpoved->code<<Qt::endl; } void KlientProMQTT::onSubscribe(void * context, MQTTAsync_successData * odpoved) { (void)context; (void)odpoved; instance->stav = ZAPSANEJ; qDebug()<<"uspesne zapsani k topicu"<<Qt::endl; } void KlientProMQTT::onSubscribeFail(void * context, MQTTAsync_failureData * odpoved) { (void)context; instance->stav = CHYBA; Q_EMIT instance->chyba("neuspesne zpasani k topicu"); qDebug()<<"neuspesne zpasani k topicu"<<Qt::endl<<"chybovy kod: "<<odpoved->code<<Qt::endl; } void KlientProMQTT::onConnect(void * context, MQTTAsync_successData * odpoved) { (void)context; (void)odpoved; qDebug()<<"uspesne pripojeno"<<Qt::endl; instance->stav = PRIPOJENEJ; Q_EMIT instance->pripojenej(); instance->subscribe(); } void KlientProMQTT::onConnectFail(void * context, MQTTAsync_failureData * odpoved) { (void)context; qDebug()<<"neslo pripojit"<<Qt::endl<<"chybovy kod: "<<odpoved->code<<Qt::endl; instance->stav = CHYBA; Q_EMIT instance->chyba("neslo pripojit"); } void KlientProMQTT::onSend(void * context, MQTTAsync_successData * odpoved) { (void)context; (void)odpoved; qDebug()<<"poslalo se"<<Qt::endl; } void KlientProMQTT::onSendFail(void * context, MQTTAsync_failureData * odpoved) { (void)context; qDebug()<<"nepodarilo se odeslat ZPRAVU"<<Qt::endl<<"chybovy kod: "<<odpoved->code<<Qt::endl; qDebug()<<"klient se tedko pokusi vodpojit"; Q_EMIT instance->chyba("neslo poslat zpravu"); instance->odpojit(); } void KlientProMQTT::onDeliver(void * context, MQTTAsync_token token) { (void)context; qDebug()<<"doruceni zpravy potvrzeno"<<Qt::endl<<"token: "<<token<<Qt::endl<<Qt::endl; } void KlientProMQTT::onConnLost(void * context, char * duvod) { (void)context; instance->stav = NEPRIPOJENEJ; Q_EMIT instance->odpojenej(); qDebug()<<"spojeni ztraceno"; if(duvod != nullptr) qDebug()<<"duvod odpojeni: "<<duvod; } //vrací jedničku když zprávu správně přijmem jinak nám ji to zkusí znova strčit int KlientProMQTT::onMessage(void * context, char * topicNazevStr, int topicStrZnaku, MQTTAsync_message * zprava) { (void)context; (void)topicStrZnaku; if(zprava->payloadlen > 0) { qDebug()<<"prijata zprava do topicu \'" + QString(topicNazevStr) +"\' s vobsahem:"; qDebug()<<QString::fromLocal8Bit((char *)zprava->payload); QString topicQstr = QString::fromLocal8Bit(topicNazevStr), zpravaQstr = QString::fromLocal8Bit((char *)zprava->payload); if( ((char *)zprava->payload)[0] == '{' ) { cJSON * jsonZprava = cJSON_Parse((char*)zprava->payload); if (jsonZprava != nullptr) { //zprava asi jako bude ve formatu json cJSON_Delete(jsonZprava); Q_EMIT instance->zpravaJsonDoFrontendu(topicQstr, zpravaQstr); MQTTAsync_freeMessage(&zprava); MQTTAsync_free(topicNazevStr); return 1; } } Q_EMIT instance->zpravaDoFrontendu(topicQstr, zpravaQstr); } else qDebug()<<"prijata prazdna zprava :O :O"; MQTTAsync_freeMessage(&zprava); MQTTAsync_free(topicNazevStr); return 1; } // destruktor co dělá že uklidí KlientProMQTT::~KlientProMQTT() { if(jePripojenej()) odpojit(); if(m_klient != nullptr) MQTTAsync_destroy(&m_klient); delete [] this->m_nazvyTopicu; delete [] this->m_QoSsTopicu; }
plugin/NacitadloSouboru.h
#ifndef NACITADLO_SOUBORU_H #define NACITADLO_SOUBORU_H #include <qt5/QtCore/QObject> #include <qt5/QtCore/QUrl> #include <qt5/QtCore/QFile> #include <qt5/QtCore/QDebug> class NacitadloSouboru : public QObject { Q_OBJECT public: explicit NacitadloSouboru(QObject * parent = nullptr) : QObject(parent){} ~NacitadloSouboru(){} Q_SLOT QString nacistSoubor(const QUrl & cesta); }; #endif
plugin/NacitadloSouboru.cpp
#include "NacitadloSouboru.h" #include <stdio.h> #include <stdlib.h> QString NacitadloSouboru::nacistSoubor(const QUrl & cesta) { QFile soubor(cesta.toLocalFile()); if(!soubor.open(QFile::ReadOnly | QFile::Text)) return "neslo otevrit!!!!!!!!!!"; else return soubor.readAll(); }
plugin/PlasmoidPlugin.h
#ifndef PLASMOIDPLUGIN_H #define PLASMOIDPLUGIN_H #include <QQmlExtensionPlugin> class QQmlEngine; class PlasmoidPlugin : public QQmlExtensionPlugin { Q_OBJECT Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface") public: void registerTypes(const char *uri) override; }; #endif // PLASMOIDPLUGIN_H
plugin/PlasmoidPlugin.cpp
#include "PlasmoidPlugin.h" #include "KlientProMQTT.h" #include "NacitadloSouboru.h" #include <QtQml> #include <QDebug> void PlasmoidPlugin::registerTypes(const char *uri) { Q_ASSERT(uri == QLatin1String("org.kde.private.mqttCudliky")); qmlRegisterSingletonType<KlientProMQTT>(uri, 1, 0, "Klient", KlientProMQTT::ziskatInstanci); qmlRegisterType<NacitadloSouboru>(uri, 1, 0, "Nacitadlo"); }
plugin/qmldir
module org.kde.private.mqttCudliky plugin plasmoidplugin
package/metadata.desktop
[Desktop Entry] Name=mqttCudliky Comment=Nejvíc nejkrásnější mqtt widget Type=Service X-KDE-ParentApp= X-KDE-PluginInfo-Author=gréta X-KDE-PluginInfo-Email=gretulililinka@protonmail.com X-KDE-PluginInfo-License=nevim jak to vomezuje licence qt :D X-KDE-PluginInfo-Name=org.kde.mqttCudliky X-KDE-PluginInfo-Version=1.0 X-KDE-PluginInfo-Website=www.replace.com X-KDE-ServiceTypes=Plasma/Applet X-Plasma-NotificationArea=true X-Plasma-API=declarativeappletscript X-Plasma-MainScript=ui/main.qml X-Plasma-RemoteLocation= X-KDE-PluginInfo-Category=Environment and Weather Icon=preferences-system-network-sharing
package/contents/config/config.qml
//tady sou definovaný taby v konfiguraci widgetu naploše import QtQuick 2.0 import org.kde.plasma.configuration 2.0 as PlasmaConfig PlasmaConfig.ConfigModel { PlasmaConfig.ConfigCategory { name: i18n('Nastavování čudliků') icon: 'preferences-desktop-plasma' source: 'NastavovaniCudliku.qml' } }
package/contents/config/main.xml
<?xml version="1.0" encoding="UTF-8"?> <kcfg xmlns="http://www.kde.org/standards/kcfg/1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0 http://www.kde.org/standards/kcfg/1.0/kcfg.xsd"> <kcfgfile name=""/> <group name="General"> <entry name="adresa" type="String"> <default></default> </entry> <entry name="id" type="String"> <default></default> </entry> <entry name="uzivatel" type="String"> <default></default> </entry> <entry name="heslo" type="String"> <default></default> </entry> </group> <group name="Cudliky"> <entry name="konfiguracniJson" type="String"> <default> </default> </entry> </group> </kcfg>
package/contents/ui/main.qml
import QtQuick 2.9 import QtQuick.Window 2.2 import QtQuick.Controls 2.5 import QtQuick.Layouts 1.11 //import org.kde.quickcharts 1.0 as Charts import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.plasmoid 2.0 import org.kde.plasma.components 3.0 as PlasmaComponents // naimportujem si všecky voběkty ze složšky 'cudliky' import "./cudliky" // taky si naimportujem náš c++/qt plugin import org.kde.private.mqttCudliky 1.0 //chceme takovej ten velkej widget naplochu Item { // podporujem jenom velkej widget strčenej naplochu Plasmoid.preferredRepresentation: Plasmoid.fullRepresentation Plasmoid.fullRepresentation: Item { id: main // minimální šířka/vejška widgetu pole počtu čudliků Layout.minimumWidth: (velikostMezer + 64) * main.sloupcu * PlasmaCore.Units.devicePixelRatio Layout.minimumHeight: (velikostMezer + 64) * main.radku * PlasmaCore.Units.devicePixelRatio // počet sloupců a řádků grid/mřížškovýho layoutu/rozložení čudliků // čudliky se budou strkat dotoho elmentu grid property alias radku: grid.rows property alias sloupcu: grid.columns // velikost mezer mezi čudlikama v pixelech property int velikostMezer: 2 // různý pole který držej referenci na voběkty čudliků // uplně všecky čudliky property var vseckyCudliky: [] // čudliky podle jednotlivejch topiců // key/klíč pole sou názvy topiců property var cudlikyPodleTopicu: [] // taky čudliky podle topiců ale takový co dělaj že chtěj psaníčko v *.json formátu :D ;D property var jsonCudlikyPodleTopicu: [] // seznam jenom těch čudliků co uměj publikovat // potřebujem znát nato abysme je mohli dycky všecky zamknout když mqtt klient ztratí spojení s brokerem property var publikovaciCudliky: [] // 'sada' všech topiců s nejvíc nejvěčím QoS jako hodnotou // potřebujem ji mit jako vobyč array/pole abysme ji mohli strčit do qt/c++ // v seznamTopicu by měli bejt jenom stringy a v qos zase samý int // ( de ňák v js hezky rozlousknout pole na dvě různý žeby v jednom byly keys a vdruhým values?????? nějakej takovej vopak zip :O :O ) property var seznamTopicu: [] property var seznamTopicuQoS: [] // výchozí barvy když nejsou definovaný v konfigu property color barvaTextu: 'black' property color barvaAktivni: 'red' property color barvaKontury: 'green' property color barvaPozadi: 'white' // načtem ůdaje o brokeroj/přihlašovací ůdaje z konfigu // mužou bejt i undefined/nedefinovaný to se pak hlídá dál aby se nezačalo připojovat property string adresa: plasmoid.configuration.adresa property string id: plasmoid.configuration.id property string uzivatel: plasmoid.configuration.uzivatel property string heslo: plasmoid.configuration.heslo // výchozí konfigurační string pro případ že neni žádnej uloženej v konfigu // normálně by asi jako měla fungovat hodnota strčená mezi <default></default> tagy v // main.xml ale ňák ji to nechtělo načítat :O :/ // možná to neumí zhamat *.json možná dělaj problémy uvozovky možná to nefunguje // jenom v tom testovacím plasmoidviewer vokýnku. nevim :O :/ property string jsonString: '{ "grid": { "sirka": 1, "vyska": 1 }, "barvy": { "pozadi": "#0f808080", "text": "#d3d3d3", "aktivni": "#00ffff", "kontura": "#191919" }, "cudliky": [ { "druh": "Moznosti", "nazev": "čudlík", "muzePublikovat": false, "muzePrijimat": false, "x": 0, "y": 0, "topic": "nejaky_topic", "seznamMoznosti": ["vychozi nastaveni cudliku"] } ] }' // funcke co dělá že veme json a podle něj vyrobí všecky čudliky // nastaví barvičky/velikost mřížšky a takový tydlety // taky nám naplní všecky ty pole čudliků function jsonNaElementy(json) { // nastavení počtu řádků/sloupců mřížšky main.sloupcu = json.grid.sirka; main.radku = json.grid.vyska; // jestli jsou v json barvičky tak nastavíme barvičky :D ;D if(json.hasOwnProperty('barvy')) { barvaAktivni = json.barvy.aktivni; barvaTextu = json.barvy.text; barvaPozadi = json.barvy.pozadi; barvaKontury = json.barvy.kontura; } // noa teďko projdem všecky čudliky v json a zkusíme je dynamicky vyrobit a nastrkat do tý mřížšky for(var i in json.cudliky) { // vezmem si z json další json samotnýho jednoho čudlika var cudlikJson = json.cudliky[i]; // id našeho čudliku vyrobenýho // nějaký id asi musíme vyplnit jinak nemá nějakej víc věčí význam var id = 'cudlik_' + i; // čudlik budem vyrábět funkcí 'createQmlObject' která má navstupu string kterej vobsahuje zdrojáček qml elementu // náš string začnem importem var obektStr = 'import "./cudliky";'; obektStr += cudlikJson.druh + '{'; // jestli muže čudlik publikovat tak taky musí mit možnost posílat publikovací signál dalším qt/qml elementům // bude posílat signál s topicem a nějakou tamtou zprávou if(cudlikJson.muzePublikovat) { obektStr += 'signal zpravaOdCudliku(string topic, string zprava);'; } obektStr += '}'; // vyrobíme si čudlik strčíme ho do gridu a nastavíme mu nějaký id var cudlik = Qt.createQmlObject(obektStr, grid, id); // další atributy čudliku nastavíme jako atributy javascriptovýho voběktu // méno a topic ke kterýmu je připojenej cudlik.nazev = cudlikJson.nazev; cudlik.mqttTopic = cudlikJson.topic; // čudlik bude v mřížšce vyplňovat všecky přidělený chlívečky cudlik.Layout.fillHeight = true; cudlik.Layout.fillWidth = true; // nastavíme pozici v mřížšce/gridu cudlik.Layout.column = cudlikJson.x; cudlik.Layout.row = cudlikJson.y; // jestli má čudlik v json nastavenou nějakou šířku/výšku teda jakože kolik // chlívečků mřížky má našířku/navejšku tak nastavíme if(cudlikJson.hasOwnProperty('sirka')) cudlik.Layout.columnSpan = cudlikJson.sirka; if(cudlikJson.hasOwnProperty('vyska')) cudlik.Layout.rowSpan = cudlikJson.vyska; // vobarvení čudliku // jinak má svý nějaký výchozí barvičky cudlik.barvaAktivni = barvaAktivni; cudlik.barvaTextu = barvaTextu; cudlik.barvaPozadi = barvaPozadi; cudlik.barvaKontury = barvaKontury; // jestli čudlik muže publikovat tak mu nastavíme QoS/kvalitu služby posílací parametr + retain // a taky napojíme jeho signál 'zpravaOdCudliku' na funkci 'zpravaCudliku' if(cudlikJson.muzePublikovat) { cudlik.mqttQos = cudlikJson.hasOwnProperty('QoS') ? cudlikJson.QoS : parseInt(0); cudlik.mqttRetain = cudlikJson.hasOwnProperty('retain') ? cudlikJson.retain : false; cudlik.zpravaOdCudliku.connect(zpravaCudliku); } else { cudlik.muzePublikovat = false; } // jestli máme v json definovanej nějakej atribut 'jsonElement' jakože čudlik // chce vezprávičkách dycky luštit nějakej json a zněj si něco vyzobnout tak nastavíme že parsuje json if(cudlikJson.hasOwnProperty('jsonElement')) { cudlik.parsujeJson = true; cudlik.jsonElement = cudlikJson.jsonElement; } // jestli pracuje/drží datovej typ v celočíselný podobě nebo ne if(cudlikJson.hasOwnProperty('jeCelociselny')) { cudlik.jeCelociselny = cudlikJson.jeCelociselny; } // ruzný specifický atributy pro různý druhy čudliků switch(cudlikJson.druh) { case 'Prepinadlo': if(cudlikJson.hasOwnProperty('zpravaPriOn')) cudlik.zpravaPriOn = cudlikJson.zpravaPriOn; if(cudlikJson.hasOwnProperty('zpravaPriOff')) cudlik.zpravaPriOff = cudlikJson.zpravaPriOff; if(cudlikJson.hasOwnProperty('onText')) cudlik.onText = cudlikJson.onText; if(cudlikJson.hasOwnProperty('offText')) cudlik.offText = cudlikJson.offText; // aktualizace přepínadla protože vokamžitě nemá barvičku cudlik.aktualizovatCudlik(); break; case 'Soupatko': case 'Kruh': if(cudlikJson.hasOwnProperty('minHodnota')) cudlik.hodnotaMin = cudlikJson.minHodnota; if(cudlikJson.hasOwnProperty('maxHodnota')) cudlik.hodnotaMax = cudlikJson.maxHodnota; if(cudlikJson.hasOwnProperty('znakyZaHodnotou')) cudlik.znakyZaHodnotou = cudlikJson.znakyZaHodnotou; console.log(cudlik.hodnotaMin); console.log(cudlikJson.hodnotaMin); if(cudlikJson.druh == 'Kruh') cudlik.aktualizovatPrumerTakyStred(); break; case 'Graf': if(cudlikJson.hasOwnProperty('pocetCarMrizky')) cudlik.pocetCarMrizky = cudlikJson.pocetCarMrizky; break; case 'Tlacitko': if(cudlikJson.hasOwnProperty('coPosila')) cudlik.coPosila = cudlikJson.coPosila; if(cudlikJson.hasOwnProperty('napis')) cudlik.napis = cudlikJson.napis; break; case 'Moznosti': if(cudlikJson.hasOwnProperty('seznamMoznosti')) cudlik.seznamMoznosti = cudlikJson.seznamMoznosti; break; case 'Napis': if(cudlikJson.hasOwnProperty('maOkraj')) cudlik.maOkraj = cudlikJson.maOkraj; break; default: console.error('neznamej druch cudliku v json souboru :O :O'); break; } // přijímací čudliky si rozškatulkujem do polí(slovníků) kde klíčem je topic danýho čudliku dycky // nebudem si pak muset přestrkávat zprávu vod mqtt klienta do všech čudliků najednou ale // jenom do čudliků danýho topicu // máme dvě takový pole polí přijímacích čudliků jedno vobyčejný a druhý pro json if(cudlikJson.muzePrijimat) { if(cudlik.parsujeJson) { if(cudlikJson.topic in jsonCudlikyPodleTopicu) jsonCudlikyPodleTopicu[cudlikJson.topic].push(cudlik); else // jestli neni tak přidáme nový pole jsonCudlikyPodleTopicu[cudlikJson.topic] = [cudlik]; } else { if(cudlikJson.topic in cudlikyPodleTopicu) cudlikyPodleTopicu[cudlikJson.topic].push(cudlik); else cudlikyPodleTopicu[cudlikJson.topic] = [cudlik]; } } else { cudlik.muzePrijimat = false; } // strčíme čudlik do pole který má referenci na všecky čudliky vseckyCudliky.push(cudlik); // jestli čudlik muže publikovat tak taky skováme do pole publikovacích čudliků if(cudlik.muzePublikovat) publikovaciCudliky.push(cudlik); // nakonec skusíme přidat QoS/topic do tý pomyslný 'sady' ze dvou polí // topic přidáváme jenom když eště v poli neni. když je tak kouknem jestli je přidávaný qos věčí než to uložený // a jestli jako jo tak přepišem if(seznamTopicu.includes(cudlikJson.topic)) { var index = seznamTopicu.indexOf(cudlikJson.topic); if(seznamTopicuQoS[index] < cudlikJson.QoS) seznamTopicuQoS[index] = cudlikJson.QoS; } else { // jestli topic eště v 'sadě' neni tak prostě jako přidáme seznamTopicu.push(cudlikJson.topic); seznamTopicuQoS.push(cudlikJson.QoS); } } } // funkce co vodstraní všecky čudliky function odstranitCudliky() { vseckyCudliky.forEach(cudlik => {cudlik.destroy();}); vseckyCudliky = []; publikovaciCudliky = []; cudlikyPodleTopicu = []; jsonCudlikyPodleTopicu = []; // seznam topiců promažem taky seznamTopicu = []; seznamTopicuQoS = []; } // funcke co podle topicu strčí nějakou zprávu všem čudlikům function strcitZpravuCudlikum(topic, obsah) { console.log('zprava cudlikum topic: ' + topic); cudlikyPodleTopicu[topic].forEach(cudlik => {cudlik.prijmout(obsah);}); } // tosamý co 'strcitZpravuCudlikum' ale s json function strcitJsonZpravuCudlikum(topic, obsah) { var json = null; try { json = JSON.parse(obsah); } catch(e) { console.error('chyba pri parsovani json zpravy: ' + e.name + ', ' + e.message); } console.log('json zprava cudlikum topic: ' + topic); jsonCudlikyPodleTopicu[topic].forEach(cudlik => {cudlik.prijmoutJson(json);}); } // zpráva vod čudliku // klient ji zkusí poslat (stav připojení si hlídá metoda 'poslat') function zpravaCudliku(topic, obsah) { console.log('posilam str: '+obsah); Klient.poslat(topic, obsah); } // callbacky na vodpojení/připojení/chybu klienta // vypnem posílací čudliky function odpojeniKlienta() { malejTextDole.text = 'odpojeno (posílací čudliky teďko nefungujou)'; publikovaciCudliky.forEach(cudlik => {cudlik.muzePublikovat = false;}); } // zapnem posílací čudliky function pripojeniKlienta() { malejTextDole.text = 'připojeno'; publikovaciCudliky.forEach(cudlik => {cudlik.muzePublikovat = true;}); } function chybaKlienta(popis) { if(popis === undefined) malejTextDole.text = 'chyba :D'; else malejTextDole.text = popis; } // funkce co se zavolá jakmile máme qml voběkt kompletní Component.onCompleted: { // napojení signálů z mqtt klienta na vodpovídající funkce tady Klient.zpravaDoFrontendu.connect(strcitZpravuCudlikum); Klient.zpravaJsonDoFrontendu.connect(strcitJsonZpravuCudlikum); Klient.odpojenej.connect(odpojeniKlienta); Klient.pripojenej.connect(pripojeniKlienta); Klient.chyba.connect(chybaKlienta); // jestli máme v kongiguraci uloženej string json souboru tak ho vemem jinak použijem ten výchozí var konfiguracniJsonStr = plasmoid.configuration.konfiguracniJson || jsonString; // zkisíme json naparsovat var json = null; try { json = JSON.parse(konfiguracniJsonStr); } catch(e) { console.error('chyba pri parsovani konfiguracniho *.json: ' + e.name + ', ' + e.message); malejTextDole.text = 'nešlo naparsovat json :O :O'; } if(json != null) { // jestli máme validní json tak podle něj mužem vyrobit čudliky :D ;D jsonNaElementy(json); // nastavíme seznam Klient.nastavitSeznamTopicu(seznamTopicu, seznamTopicuQoS); // jestli máme nastavenou brokera/přihlašovací ůdaje a klient neni puštěnej tak ho nastavíme a zapnem if(adresa && id && uzivatel && heslo) { if(!Klient.bezi()) { Klient.nastavit(adresa, id, uzivatel, heslo); Klient.start(); } else { // sem by jsme se jako vubec neměli dostat :D console.log('mqtt klient uz bezi :O :O'); } } else { var txt = 'mqtt klient se nespustil protoze neni nakonfigurovanej'; console.log(txt); malejTextDole.text = txt; } } } // když odstraníme widget tak taky jako musíme vipnout mqtt klienta ;D Component.onDestruction: { Klient.stop(); } //regování na změnu konfigurace Plasmoid.onUserConfiguringChanged: { var konfiguracniJsonStr = plasmoid.configuration.konfiguracniJson || jsonString; var json = null; try { json = JSON.parse(konfiguracniJsonStr); } catch(e) { console.error('chyba pri parsovani konfiguracniho *.json: ' + e.name + ', ' + e.message); malejTextDole.text = 'nešlo naparsovat json :O :O'; } if(json != null) { odstranitCudliky(); jsonNaElementy(json); Klient.nastavitSeznamTopicu(seznamTopicu, seznamTopicuQoS); if(adresa && id && uzivatel && heslo) { // jestli klient už běží tak mu resetnem // to znamená že ho vodpojíme přepišem parametry a připojíme znova // vlákno se nepřetrhne :D if(Klient.bezi()) { Klient.reset(adresa, id, uzivatel, heslo, seznamTopicu, seznamTopicuQoS); } else { // jinak normálně pustíme Klient.nastavit(adresa, id, uzivatel, heslo); Klient.start(); } } else { // jestli je konfigurace špatná/nějakej údaj chybí tak se klient nezačne připojovat var txt = 'mqtt klient se nespustil protoze neni nakonfigurovanej'; console.log(txt); malejTextDole.text = txt; } } } /* * TODO * todleto se nikdy nezavovlalo :O :O * kolibáč by si tam naně měl pořádně došlápnout aby to tam dali dopořádku si myslim :O :O * * Plasmoid.onFormFactorChanged: { console.log("zmena rozliseni"); } Plasmoid.onAvailableScreenRegionChanged: { console.log("zmena!!!!!!!!!!"); } Plasmoid.onLocationChanged: { console.log("zmena222!!!!!!!!!!"); } */ // grid/mřížka do který se nastrkaj jednotlivý čudliky GridLayout { id: grid width: parent.width height: parent.height - malejTextDole.height rowSpacing: velikostMezer columnSpacing: velikostMezer } // malej stavovej textík dole u spodního vokraje widgetu // řiká cose právě děje a jestli je widget připojenej/nepřipojenej a takový věci Text { y: grid.height id: malejTextDole text: 'právě teďko zapnutý' color: main.barvaTextu } } }
package/contents/ui/NastavovaniCudliku.qml
import QtQuick 2.0 import QtQuick.Controls 2.5 import QtQuick.Dialogs 1.0 import QtQuick.Layouts 1.12 import org.kde.kirigami 2.4 as Kirigami // podle ukázky tady hele https://develop.kde.org/docs/plasma/widget/configuration/ // naimportujem si svuj plugin kuli třídě 'NačítadloSouboru' kterou potřebujem kuli načtení textovýho souboru // samotný qml nic takovýho jako přečíst nějakej *.txt asi neumí :O :O import org.kde.private.mqttCudliky 1.0 Kirigami.FormLayout { id: page Kirigami.FormData.label: i18n('Nastavování') // vyrobení načítadla Nacitadlo { id: nacitadlo } // proměný musej mit ten prefix 'cfg_' aby si to jakože spárovalo s vodpovídajícíma hodnotama v souboru main.xml property alias cfg_adresa: konfiguraceAdresa.text property alias cfg_id: konfiguraceId.text property alias cfg_uzivatel: konfiguraceUzivatel.text property alias cfg_heslo: konfiguraceHeslo.text property alias cfg_konfiguracniJson: configTxt.text TextField { id: konfiguraceAdresa Kirigami.FormData.label: i18n('adresa:') placeholderText: i18n('adresa s druhem protokolu a portem nakonci. třeba něco jako tcp://127.0.0.1:1883') } TextField { id: konfiguraceId Kirigami.FormData.label: i18n('id klienta') placeholderText: i18n('id klienta') } TextField { id: konfiguraceUzivatel Kirigami.FormData.label: i18n('uživatel') placeholderText: i18n('uživatel') } TextField { id: konfiguraceHeslo Kirigami.FormData.label: i18n('heslo') placeholderText: i18n('heslo') } ColumnLayout { Kirigami.FormData.label: i18n('načíst z konfiguračního *.json (umí to načíst i tamto nastavování brokera takže seto nemusí vyplňovat rukama)') TextArea { id: configTxt placeholderText: i18n('tady by měl bejt konfurační json čudliků ukázanej') Layout.fillHeight: true Layout.fillWidth: true } Button { text: i18n('Načíst *.json ze souboru') icon.name: 'folder-symbolic' onClicked: { fileDialogLoader.active = true } // načítadlo souborů // soubor dostanem jako proměnou fileUrl toje místo uložení toho souboru někde v systemu // asi nám neumí dát přímo jeho vobsah nevim :O :O Loader { id: fileDialogLoader active: false sourceComponent: FileDialog { id: fileDialog //folder: shortcuts.music // chcem json ale umožníme taky vybrat všecky soubory nameFilters: [i18n('json(%1)', '*.json'),i18n('všecky soubory(%1)', '*'),] onAccepted: { // načtem načítadlem vobsah souboru do js proměný var obsah = nacitadlo.nacistSoubor(fileUrl); // měl byto bejt jakože validní json nóó tak si ho zkusíme tady nejdřiv naparsovat a // když nám to nebude/skončí vyjímkou tak tam asi jako je nějaká chyba var json = null; try { json = JSON.parse(obsah); } catch(e) { configTxt.text = 'chyba pri parsovani konfiguracniho *.json: ' + e.name + ', ' + e.message; console.error(configTxt.text); } if(json != null) { // jestli máme validní json tak ho strčíme do configTxt textovýho pole aby se nám jakože pak uložil do nastavení widgetu configTxt.text = obsah; // jestli má json soubor element pomenovanej 'broker' tak podle něj nastavíme připojení a nemusíme to vyplňovat ručně furt if(json.hasOwnProperty('broker')) { var brokerJson = json.broker; if(brokerJson.hasOwnProperty('adresa')) konfiguraceAdresa.text = brokerJson.adresa; if(brokerJson.hasOwnProperty('id')) konfiguraceId.text = brokerJson.id; if(brokerJson.hasOwnProperty('uzivatel')) konfiguraceUzivatel.text = brokerJson.uzivatel; if(brokerJson.hasOwnProperty('heslo')) konfiguraceHeslo.text = brokerJson.heslo; } } fileDialogLoader.active = false; } onRejected: { fileDialogLoader.active = false; } Component.onCompleted: open() } } } } }
package/contents/ui/cudliky/Cudlik.qml
import QtQml 2.2 import QtQuick 2.9 import QtQuick.Layouts 1.11 // oběkt ze kterýho 'děděj' všecky vostatní čudliky Rectangle { //nějaká minimalní velikost aby se nám to jakože celý nescvrklo uplně Layout.minimumWidth: 64 Layout.minimumHeight: 64 property string nazev: 'Cudlik' // výchozí barvy čudliku kdyby třeba jako nebyly přenastavený po vyrobení property color barvaTextu: 'black' property color barvaAktivni: 'red' property color barvaKontury: 'green' property color barvaPozadi: 'white' // tloušťka vokraje a zakulacení vokrajů property real velikostOkraje: 5 property real zakulaceni: 5 // k jakýmu je připojenej topicu s jakým qos/kvalitou služby a jestli je zapnutej retain flag property string mqttTopic: '' property int mqttQos: 0 property bool mqttRetain: false // jestli muže publikovat a přijímat zprávy property bool muzePublikovat: true property bool muzePrijimat: true // alias/reference kterou umožňujem potomkům lízt k titulku týdletý třídy property alias titulek: titulek // alias kterým se nastavuje viditelnost titulku property alias maTitulek: titulek.visible // jestli jakoby parsuje json a jestli jo tak jakej element hledá // žádný složitý parsování prostě koukne na element // potřebuje někdo nějaký víc složitější parsování??????? :O :O :O :O // sou různý mrňavý knihovničky nato hele třeba https://www.w3resource.com/JSON/JSONPath-with-JavaScript.php property bool parsujeJson: false property string jsonElement: "" // jestli je double/int property bool jeCelociselny: true // jestli je vubec jako číselnej property bool jeCiselny: true color: Qt.rgba(0,0,0,0) // funkce do který se budou strkat přijatý zprávy // nevim jak to udělat jako abstraktní metodu :D function prijmout(zprava){console.log("zavolano cudlik.prijmout rodicovsky tridy :O :O");} // funkce co zkusí vyparsovat klíč z json zprávičky aten pak strčit do normální funkce přijmout // potomci Čudliku todleto teda pak nemusej řešit function prijmoutJson(zpravaJson) { if(parsujeJson) { if(zpravaJson.hasOwnProperty(jsonElement)) { prijmout(zpravaJson[jsonElement]); } else { console.error('v json zprave neni tendlecten klic/element!!!!!!!!!!!!!!!'); } } else { console.error('tendle cudlik nechce json parsovat!!!!!!!!!!!!!!'); } } // titulek čudliku kterej se věčinou zobrazuje nad tim elementem Text { id: titulek text: "Cudlik" color: parent.barvaTextu // font.pointSize: 16 // velikost fontu se vybírá sama podle width/height velikosti Text voběktu font.pointSize: 256 minimumPointSize: 8 fontSizeMode: Text.Fit width: parent.width height: parent.height/5 visible: true } }
package/contents/ui/cudliky/Graf.qml
import QtQuick 2.9 import QtQuick.Controls 2.5 import org.kde.quickcharts 1.0 as Charts import org.kde.quickcharts.controls 1.0 as ChartsControls // čudlik s kcharts grafem // api maj trošičku popsaný tady https://api.kde.org/frameworks-api/frameworks-apidocs/frameworks/kquickcharts/html/index.html // ale blbě se k tomu hledaj nějaký víc složitější ukázkový příklady :O :/ Cudlik { id: grafRoot nazev: "graf" titulek.text: nazev property real zobrazovanaPromena: 0 property var zdrojDat: Charts.SingleValueSource { value: zobrazovanaPromena } property int pocetCarMrizky: 3 function prijmout(zprava) { grafRoot.zobrazovanaPromena = jeCelociselny ? parseInt(zprava) : parseFloat(zprava); grafRoot.zdrojDat.dataChanged(); } Rectangle { width: parent.width height: titulek.visible ? parent.height - titulek.height : parent.height y: titulek.visible ? titulek.height : 0 color: barvaPozadi radius: zakulaceni Charts.LineChart { id: grafPlot antialiasing: true anchors.verticalCenter: parent.verticalCenter x: yAxisLabels.width + velikostOkraje + 10 width: parent.width - velikostOkraje*2 - yAxisLabels.width -10 height: parent.height - velikostOkraje*2 colorSource: Charts.SingleValueSource { value: barvaAktivni } nameSource: Charts.SingleValueSource { value: nazev } valueSources: Charts.HistoryProxySource { source: grafRoot.zdrojDat maximumHistory: 100 } lineWidth: 2 fillOpacity: 0.5 smooth:false direction:Charts.XYChart.ZeroAtEnd Charts.GridLines { id: vertikalni anchors.fill: parent chart: parent direction: Charts.GridLines.Vertical; minor.count: pocetCarMrizky minor.lineWidth: 1 minor.color: barvaTextu // major čáry se mi nepodařilo uplně vypnout // když se třeba jako do major.count strčí 0 tak si je to asi generuje podle .frequency atributu :O :/ major.count: 1 major.lineWidth: 1 major.color: barvaTextu } Charts.GridLines { id: horizontalni anchors.fill: parent chart: parent direction: Charts.GridLines.Horizontal; minor.count: pocetCarMrizky minor.lineWidth: 1 minor.color: barvaTextu major.count: 1 major.lineWidth: 1 major.color: barvaTextu } Rectangle { id: okrajMrizky anchors.fill: parent color: Qt.rgba(0,0,0,0) border.color: barvaTextu border.width: 1 } } Charts.AxisLabels { id: yAxisLabels anchors.verticalCenter: parent.verticalCenter height: parent.height - velikostOkraje*2 x: 10 direction: Charts.AxisLabels.VerticalBottomTop // nejde to ňák víc líp???? :O :O delegate: Label { color: barvaTextu text: parseFloat(Charts.AxisLabels.label).toFixed(2); } source: Charts.ChartAxisSource { chart: grafPlot axis: Charts.ChartAxisSource.YAxis itemCount: pocetCarMrizky + 2 } } Rectangle { id: okrajGrafu anchors.fill: parent color: Qt.rgba(0,0,0,0) border.color: barvaKontury border.width: velikostOkraje radius: zakulaceni } } }
package/contents/ui/cudliky/Kruh.qml
import QtQml 2.2 import QtQuick 2.9 // takový jakože kolečko // kolečko uměj namalovat i kcharts ale to sem zistila až když sem měla todleto hotový :D Cudlik { id: kruh nazev: "Kruh" // minimální maximální hodnota aktuální hodnota a možnej rosah hodnoty property real hodnotaMin: 0 property real hodnotaMax: 100 property real hodnota: 100 property real rosahHodnot: hodnotaMax - hodnotaMin // jaký znaky se voběvujou za hodnotou uprostřed toho kruhu // výchozí je znak '%' property string znakyZaHodnotou: '%' titulek.text: nazev property real sirkaHlavniCary: 16 property real sirkaVedlejsiCary: velikostOkraje property real offset_cary: 1 // souřadnice středu toho kruhu a jeho poloměr // jeto vymyšlený tak že když je titulek kračí než zdálenost středu kruhu vod levýho/pravýho vokraje tak // se kruh do volnýho místa nafoukne. jinak je menčí než titulek a stčenej vo velikost titulku dolu abyseto jakože nepřekrejvalo // protože byto bylo takový hnusný // x je dycky uprostřed property real stredX: width / 2 property real stredY: height /2 property real polomer: height / 2 // polomer je vlastně jenom poloměr kružnice podle který namalujem nějak tlustej kroužek // noa půlka tloušťky tohodlenctoho kroužku je atribut polomerOffset property real polomerOffset: sirkaHlavniCary/2 + sirkaVedlejsiCary // přepočet aktuální hodnoty na úhel v radiánech property real uhel: (kruh.hodnota - kruh.hodnotaMin) / kruh.rosahHodnot * 2 * Math.PI // posun úhlu aby nula byla na kružnici uplně nahoře (tam kde maj točicí hodiny dvanáctku) property real posunUhlu: -Math.PI / 2 // naparsujem číslo podle toho jestli je float nebo celočíselný function prijmout(zprava) { hodnota = jeCelociselny ? parseInt(zprava) : parseFloat(zprava); } // výpočet poloměru podle toho kde se kružnice zdrcne s titulkem function aktualizovatPrumerTakyStred() { // když nemá titulek tak vemem to menčí z width/šířky a height/vejšky a podle toho uděláme ten poloměr // v polovině velikost - půlka tloušťky toho našeho malovacího kroužku if(!maTitulek) { kruh.polomer = (Math.min(kresliciPlocha.width, kresliciPlocha.height) - sirkaHlavniCary - sirkaVedlejsiCary) / 2 - offset_cary; } else if(titulek.width < stredX) { // když je titulek kratčí a dělá že končí před xovým středem nažeho zobrazovacího kroužku tak skusíme najít tu nejvíc nejvěčí kružnici // kterou tam mužem strčit. tadleta kružnice muže dosáhnout až na pravej dolní růžek titulku jestli ji to dovolej rozměry kreslicí plochy // do rovnice pro kružnici hele https://cs.wikipedia.org/wiki/Kru%C5%BEnice // (bod.x - střed.x)**2 + (bod.y - střed.y)**2 == r**2 // si dosadíme za bod na vobvodu ten pravej dolní růžek titulku // a za střed.y vejšku kreslicí plochy - poloměr, jakože vejšku vod spodního vokraje // luštěnej zoreček třeba takle nějak // (titulek.x - kresliciPlocha.width/2)**2 + (titulek.y - kresliciPlocha.y + r)**2 == r**2 // cejtim žeto de eště trošičku aritmetologicky víc zjednodušit ale sem moc líná nato :D // a tamta absolutní hodnota by tam asi jako vubec neměla bejt :D var c1 = (titulek.width - stredX)**2; var c2 = - titulek.height + kruh.height; var r = Math.abs(-(c1/(2*c2)) - (c2/2)); kruh.polomer = Math.abs((Math.min(kresliciPlocha.width/2, kresliciPlocha.height/2, r) - polomerOffset) - offset_cary); } else { kruh.polomer = (Math.min(kresliciPlocha.width, kresliciPlocha.height - titulek.height) - sirkaHlavniCary - sirkaVedlejsiCary) / 2 - offset_cary; } kruh.stredY = kruh.height - kruh.polomer - polomerOffset; } // ždycky když se změní šířka/vejška widgetu tak přepočitáme ipsilonovou souřadnici středu kroužku + poloměr // fakt byse asi jako hodilo vědět jak se jako dělá callback na změnu rozlišení velikosti widgetu a tak si ty hodnoty přepočítávat :D onWidthChanged: { aktualizovatPrumerTakyStred(); } onHeightChanged: { aktualizovatPrumerTakyStred(); } // překreslíme malovací plochu pokaždý když se změní hodnota onHodnotaChanged: { kresliciPlocha.requestPaint(); // tadleta aktualizace průměrů je tady jenom prozatim než se ňák podaří vymyslet jak to jako // aktualizovat nějak líp :O :O aktualizovatPrumerTakyStred(); } // .. a taky když vyrobíme element Component.onCompleted: kresliciPlocha.requestPaint() //kreslicí plocha na který si budem malovat ty kroužky Canvas { id: kresliciPlocha width: kruh.width height: kruh.height // všecko je víc hežčí s antialiasingem :D ;D antialiasing: true onPaint: { // vezmem 2d kreslicí kontext a vypucujem var ctx = getContext('2d'); //ctx.save(); ctx.clearRect(0, 0, kresliciPlocha.width, kresliciPlocha.height); //nakreslení pozadí ctx.beginPath(); ctx.lineWidth = sirkaHlavniCary; ctx.strokeStyle = kruh.barvaPozadi; ctx.arc(kruh.stredX, kruh.stredY, kruh.polomer, 0, 2*Math.PI); ctx.stroke(); //nakreslení zobrazovací čáry ctx.beginPath(); ctx.lineWidth = sirkaHlavniCary; ctx.strokeStyle = kruh.barvaAktivni; ctx.arc(kruh.stredX, kruh.stredY, kruh.polomer, kruh.posunUhlu, kruh.posunUhlu + kruh.uhel); ctx.stroke(); // a nakreslení vobou dvou krajních vokrajovejch čar ctx.beginPath(); ctx.lineWidth = sirkaVedlejsiCary; ctx.strokeStyle = kruh.barvaKontury ctx.arc(kruh.stredX, kruh.stredY, kruh.polomer - sirkaHlavniCary/2 - offset_cary, 0, 2*Math.PI); ctx.stroke(); ctx.beginPath(); ctx.lineWidth = sirkaVedlejsiCary; ctx.strokeStyle = kruh.barvaKontury ctx.arc(kruh.stredX, kruh.stredY, kruh.polomer + sirkaHlavniCary/2 + offset_cary, 0, 2*Math.PI); ctx.stroke(); //ctx.restore(); } // takovej ten text uprostřed kruhu Text { anchors.horizontalCenter: parent.horizontalCenter y: kruh.stredY - height/2 verticalAlignment: Text.AlignVCenter // jestli neni celočíselnej ukazujem proměnou jenom se dvouma desetinovejma místama text: ( jeCelociselny ? parseInt(kruh.hodnota) : kruh.hodnota.toFixed(2) ) + znakyZaHodnotou color: kruh.barvaTextu width: kruh.polomer*1.2 height: kruh.polomer*1.2 // velikost fontu se vybírá sama podle width/height velikosti Text voběktu font.pointSize: 256 minimumPointSize: 8 fontSizeMode: Text.Fit } // klikací voblast kruhu // je aktivní jenom když čudlik muže publikovat // funguje tak že když se klikne někde na kruhu tak se hodnota nastaví tak aby jakože tomu // kliknutýmu místo proporcionacionálně vodpovídala MouseArea { anchors.fill: parent enabled: muzePublikovat onClicked: { // spočitáme euklidovskou zdálenost var zdalenost = Math.sqrt( (mouse.x - kruh.stredX)**2 + (mouse.y - kruh.stredY)**2 ); // nastavujem hodnotu jenom když se myškou máčkne přímo tamten kruh // věčí/menčí zdálenosti nepočitáme if(zdalenost > kruh.polomer - kruh.polomerOffset && zdalenost < kruh.polomer + kruh.polomerOffset) { // vodečtem vod máčknutý souřadnice střed kruhu takže střed kružnice je teďko jakoby na bode (0,0) var x = mouse.x - kruh.stredX var y = mouse.y - kruh.stredY // pomocí atan2 spočitáme jakej má bod máčknutí uhel v rosahu od mínus pí až plus pí // tendleten uhel pak mužem převíst třeba na hodnotu v rosahu 0.0 až 2.0 atu pak násobit půlkou // maximální možný hodnoty kterou čudlik muže ukazovat/mit kruh.hodnota = (1.0 - (Math.atan2(x, y) / Math.PI)) * kruh.rosahHodnot/2 + kruh.hodnotaMin; // nakonec pošlem zpravaOdCudliku(mqttTopic, jeCelociselny ? parseInt(kruh.hodnota) : kruh.hodnota); } } } } }
package/contents/ui/cudliky/Moznosti.qml
import QtQml 2.2 import QtQuick 2.15 import QtQuick.Controls 1.4 import QtQuick.Controls.Styles 1.4 import QtQuick.Templates 2.4 as QtSablony //jeto vlastně combobox ve kterým je strčený nějaký políčko stringů ze kterýho si mužem vybírat jednu možnost dycky Cudlik { id: moznostiRoot nazev: 'moznosti' titulek.text: nazev property var seznamMoznosti: ['tady','by měl', 'bejt seznam','možností'] function prijmout(zprava) { if(!muzePrijimat) return; // jestli přijatá zpráva vodpovídá některý hodnotě v poli možností tak se nastaví // index právě vybraný možnosti // ( atribut currentText qml voběktu https://doc.qt.io/qt-5/qml-qtquick-controls-combobox-members.html je jenom pro čtení :O :/ ) if(seznamMoznosti.includes(zprava)) vybiradlo.currentIndex = vybiradlo.find(zprava); } ComboBox { id: vybiradlo width: parent.width height: maTitulek ? parent.height - titulek.height : parent.height y: maTitulek ? titulek.height : 0 model: seznamMoznosti // možnost je vybíratelná jenom když de publikovat selectByMouse: muzePublikovat // nahradíme původní barvičky tlačítka svejma nějakejma // neví někdo jak jako přestylovat do dropdownový menu :O :O // tendlecten návod hele https://doc.qt.io/qt-5/qtquickcontrols2-customize.html#customizing-combobox nefunguje :O :/ style: ComboBoxStyle { font.pointSize: 24 textColor: 'black' selectedTextColor: barvaKontury selectionColor: barvaAktivni background: Rectangle { height: control.height width: control.width color : barvaPozadi border.color: barvaKontury border.width: velikostOkraje radius: zakulaceni } // text strčíme doprostřed a nafouknem aby byl co nejvíc nejvěčí label: Text { //verticalAlignment: Text.AlignVCenter anchors.centerIn: vybiradlo text: control.editText color: barvaTextu width: control.width - velikostOkraje*2 height: control.height - velikostOkraje*2 font.pointSize: 256 minimumPointSize: 8 fontSizeMode: Text.Fit } } onActivated: { zpravaOdCudliku(mqttTopic, seznamMoznosti[index]); } } }
package/contents/ui/cudliky/Napis.qml
import QtQml 2.2 import QtQuick 2.15 // takovej jakože hodně moc vobyčejnej čudlik kterej dělá jenom to že ukazuje text nějakej chycenej Cudlik { id: napisRoot nazev: "napis" titulek.text: nazev property alias text: napisElement.text // jestli má nějaký pozadí/vokraj // když ne ukazuje se jenom samotnej textík přímo na widgetu property bool maOkraj: true function prijmout(zprava) { if(!muzePrijimat) return; text = zprava; } Rectangle { id: okraj width: parent.width height: maTitulek ? parent.height - titulek.height : parent.height radius: zakulaceni y: maTitulek ? titulek.height : 0 color: barvaPozadi border.color: barvaKontury border.width: velikostOkraje visible: maOkraj } Text { id: napisElement text: "napis" width: okraj.width - velikostOkraje * 2 height: okraj.height - velikostOkraje * 2 anchors.centerIn: okraj horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter color: barvaTextu font.pointSize: 256 minimumPointSize: 8 fontSizeMode: Text.Fit } }
package/contents/ui/cudliky/Prepinadlo.qml
import QtQml 2.2 import QtQuick 2.15 // mačkátko který funguje jako takovej jakože přepínač // má dva stavy zapnuto vypnuto Cudlik { id: prepinadlo nazev: 'prepinadlo' // jestli je zapnutej nebo vypnutej property bool stav: false // podle jakýho vobsahu chycený zprávy mění svuj stav // todleto taky posílá při zapnutí/vypnutí property string zpravaPriOn: '1' property string zpravaPriOff: '0' // co je na něm jako napsaný při zapnutí/vypnutí property string onText: 'ON' property string offText: 'OFF' // vypnem normální titulek třídy Čudlik // budem si malovat nějakej nezávislej na to mačkátko přímo titulek.visible: false // mačkátko je takovej jakože čtvereček // velikost strany čtverečku bude to víc menčí z dýlky a šířky elementu property real stranaTlacitka: Math.min(width, height) function prijmout(zprava) { if(!muzePrijimat) return; // nastavíme stav podle toho jestli jako string zprávičky vodpovídá // zpravaPriOff nebo zpravaPriOn a aktualizujem zobrazení čudliku if(zprava === zpravaPriOff) { stav = false; aktualizovatCudlik(); } else if(zprava === zpravaPriOn) { stav = true; aktualizovatCudlik(); } } // prostě jako přebarvíme součástky čudliku podle stavu kterej má // a změníme vobsah zobrazovanejch textů function aktualizovatCudlik() { if(stav) { mackatko.color = barvaAktivni; onOffText.color = barvaKontury; onOffTitulek.color = barvaKontury; onOffText.text = onText; } else { mackatko.color = barvaPozadi; onOffText.color = barvaTextu; onOffTitulek.color = barvaTextu; onOffText.text = offText; } } // když se nám změní hodnota proměný stav tak taky aktualizujem zobrazení čudliku onStavChanged: aktualizovatCudlik() Rectangle { id: mackatko radius: zakulaceni width: parent.stranaTlacitka height: parent.stranaTlacitka // vypolohujem doprostředka anchors.horizontalCenter: parent.horizontalCenter anchors.bottom: parent.bottom // barva a tloušťka vokraje rectanglu/čtverečku border.color: barvaKontury border.width: velikostOkraje MouseArea { id: mouseArea anchors.fill: parent enabled: muzePublikovat onClicked: { prepinadlo.stav = !prepinadlo.stav; zpravaOdCudliku(mqttTopic, prepinadlo.stav ? zpravaPriOn : zpravaPriOff); } } // text uprostřed čudliku kterej popisuje jestli je mačkátko zapnutý/vypnutý Text { id: onOffText anchors.centerIn: mackatko verticalAlignment: Text.AlignVCenter width: parent.width/2 height: parent.height/2 font.pointSize: 256 minimumPointSize: 8 fontSizeMode: Text.Fit } // titulek/název čudliku zobrazovanej přímo na mačkátku Text { id: onOffTitulek text: prepinadlo.nazev anchors.horizontalCenter: parent.horizontalCenter verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignHCenter width: parent.width/2 height: parent.height/2 font.pointSize: 256 minimumPointSize: 8 fontSizeMode: Text.Fit } } }
package/contents/ui/cudliky/Soupatko.qml
import QtQml 2.2 import QtQuick 2.15 import org.kde.plasma.components 3.0 as PlasmaComponents // takový posouvátko // jeto taková jakože vlastní trošičku horší varianta něčeho na // způsob slideru https://doc.qt.io/qt-5/qml-qtquick-controls-slider.html // máme nějakej čtvereček pomenovanej 'šoupací element' a ten mužem tahat po ose x Cudlik { id: soupatkoRoot nazev: 'soupatko' property int hodnotaMin: 0 property int hodnotaMax: 100 property string znakyZaHodnotou: '%' titulek.text: nazev + ' ' + 50 + znakyZaHodnotou function prijmout(zprava) { if(!muzePrijimat) return; var podil = (parseInt(zprava) - hodnotaMin) / (hodnotaMax - hodnotaMin); soupaciElement.x = maxRosah * podil + velikostOkraje; // jestli zobrazuje titulek za název připišem aktuální hodntou a znaky který za hodnotou ukazujem if(maTitulek) { titulek.text = nazev + " " + poziceNaHodnotu() + znakyZaHodnotou; } } // přepočet xový hodnoty šoupacího elementu na hodntou function poziceNaHodnotu() { var podil = (soupaciElement.x - velikostOkraje) / maxRosah; return parseInt((hodnotaMax - hodnotaMin) * podil + hodnotaMin); } // možnej rosah xový souřadnice šoupacího elementu property real maxRosah: okraj.width - soupaciElement.width - velikostOkraje*2 // pozadí šoupátka Rectangle { id: pozadi height: parent.height/4 width: parent.width anchors.verticalCenter: parent.verticalCenter color: barvaPozadi radius: zakulaceni // když pozadí máčken myšičkou tak taky aktualizujem polohu šoupátka + hodnotu čudliku MouseArea { anchors.fill: parent enabled: muzePublikovat onClicked: { var x = mouse.x; if(x < velikostOkraje) x = velikostOkraje; else if(x > maxRosah) x = maxRosah + velikostOkraje; soupaciElement.x = x; zpravaOdCudliku(mqttTopic, poziceNaHodnotu()); } } // něco jako takovej jakože progress bar kterej vyplňuje voblast vod levýho vokraje až po šoupací element Rectangle { id: progress x: 1 y: 1 height: pozadi.height - 2 width: soupaciElement.x + soupaciElement.width/2 -2 color: barvaAktivni radius: zakulaceni } // vokraj šoupátka Rectangle { id: okraj border.color: barvaKontury border.width: velikostOkraje color: Qt.rgba(0,0,0,0); height: pozadi.height width: pozadi.width radius: zakulaceni } // samotnej šoupací element Rectangle { id: soupaciElement width: 32 anchors.verticalCenter: pozadi.verticalCenter height: pozadi.height * 2 radius: zakulaceni color: barvaTextu border.color: barvaKontury border.width: velikostOkraje // ždycky když se změní xová souřadnice tak aktualizujem titulek jestli ho zobrazujem teda onXChanged: { if(maTitulek) titulek.text = nazev + ' ' + poziceNaHodnotu() + znakyZaHodnotou; } MouseArea { anchors.fill: parent enabled: muzePublikovat // uděláme ho dragable/tahatelnej myšičkou drag.target: soupaciElement // tahat pude jenom po ose x drag.axis: Drag.XAxis // vodkaď až kam ho jako pude tahat drag.minimumX: velikostOkraje drag.maximumX: okraj.width - soupaciElement.width - velikostOkraje onReleased: { zpravaOdCudliku(mqttTopic, poziceNaHodnotu()); } } } } }
package/contents/ui/cudliky/Tlacitko.qml
import QtQml 2.2 import QtQuick 2.15 import QtQuick.Controls 1.4 import QtQuick.Controls.Styles 1.4 import QtQuick.Templates 2.4 as QtSablony //jeto vlastně combobox ve kterým je strčený nějaký políčko stringů ze kterýho si mužem vybírat jednu možnost dycky Cudlik { id: tlacitkoRoot nazev: 'Tlacitko' titulek.text: nazev property string napis: nazev property string coPosila: 'tlacitko macknuty' function prijmout(zprava) { if(!muzePrijimat) return; console.log('tlacitko by asi jako nemelo bejt vubec schopny prijimat mqtt zpravy :O :O'); } Button { id: tlacitko width: parent.width height: maTitulek ? parent.height - titulek.height : parent.height y: maTitulek ? titulek.height : 0 // nahradíme původní barvičky svejma nějakejma style: ButtonStyle { background: Rectangle { height: control.height width: control.width color : tlacitko.pressed ? barvaAktivni : barvaPozadi border.color: barvaKontury border.width: velikostOkraje radius: zakulaceni } // text strčíme doprostřed a nafouknem aby byl co nejvíc nejvěčí label: Text { verticalAlignment: Text.AlignVCenter anchors.centerIn: tlacitko text: napis color: tlacitko.pressed ? barvaPozadi : barvaTextu width: control.width - velikostOkraje*2 height: control.height - velikostOkraje*2 font.pointSize: 256 minimumPointSize: 8 fontSizeMode: Text.Fit } } onClicked: { zpravaOdCudliku(mqttTopic, coPosila); } } }
Tiskni
Sdílej:
Současný desktop vypadá opravdu dobře.
unicorni sou nuda si myslim :O ;D 🦄 🦄 🦄 🦄
ovsem v celkove casti procentualniho slozeni vzduchu je CO2 takrka zanedbatelna polozka jak pred 70 lety tak dnesA z hlediska té sledované aktivity? (absorpce odraženého/vyzářeného IR a z toho vyplývajíví vliv na teplotu a obecně klima)
Cili kdyz do litru vody naleju cca 0.3 ml slivovice tak mam hned narust alkoholu ve sklenici vody o 100% coz je desive nicmene nic to neznamena.1) O nekonečně %, 2) Když ti pustím do pokoje 0.03% kyanovodíku tak se ti to taky nebude líbit. A přitom jsou to jenom 0.03%!
To u mě za ty dvě minuty...Teda to je rychlovka. Musim se vsak omluvit ze jsem se nespravne genderove vyjadril a ze jsem misto slecny mel napsat clovek.
přeci ji jako hnedka potom nestrčíš ven zadveře ne :D
Kyanovodik je nejjedovatejsi latka co vubec existuje.Takže to vypadá, že asi nejde tvrdit „je něčeho jenom 0.03%, nemůže to mít žádný vliv“, že?
Zato kdyz si do pokoje privedu slecnu tak za hodinu mi tam vyfuni tolik CO2 ze jeho hladina vzroste o stovky % takze cele to hrani si s tisicinama % CO2 mi zavani ekoterorismem.Ale ono přece nejde o to, že by zvýšení CO2 z 270 na současných 420 nebo za pár let predikovaných 500 ppm nějak přímo škodilo člověku (jak se snažíš naznačit na příkladu s místností) ve smyslu dýchání, ale o to, že CO2 absorbuje infračervené záření a tím způsobuje oteplování. To je strawman, nebo tomu opravdu nerozumíš?
co2 prej teda jako pomáhá maši planetě zachytávat a udržovat teplo. noa díky tomu tady v noci neni taková zima jako třeba na měsíci když tam nesvítí sluníčko. chceš snad ze země mit nevobyvatelnou měsíční pustinu???? :O :O :D ;D
Kyanovodik je nejjedovatejsi latka co vubec existuje.V žádném případě, existuje hromada zejména organických látek (např. botulotoxin), které jsou o mnoho řádů jedovatější.
Zato kdyz si do pokoje privedu slecnu tak za hodinu mi tam vyfuni tolik CO2 ze jeho hladina vzroste o stovky % takze cele to hrani si s tisicinama % CO2 mi zavani ekoterorismem.V tomto okamžiku je už pro dorozumění se v diskuzi zásadní rozlišovat mezi procenty a procentními body, protože pokud jsi v obou případech myslel buď procenta, nebo procentní body, tak jsi napsal naprostý nesmysl.
Estli je ve zduchu neceho "jen nedychatelnyho"Já jsem pro každou srandu, ale tady mi ten vtip nějak uniká. Je to narážka na nějakou hlášku, něco co někdo řekl v televizi, nebo nějaký meme, nebo...?? Nechápu.
To jestli nas prirustek 0.0107% zabije nebo zpusobi nejake globalni oteplovani je vec dalsi.To je právě to co nechápu - kdy'ž je tohle to "další", tak co je to první? Asi mi furt uniká na co to má být narážka...
proto si jako ten co2 rudozelený 'experťy' vybraly protože je to taková duchařina :D ;D tvl blbá vodní pára má víc jak 60% podíl na tom údajným globálním voteplovaní proč se neřeší nějaký vysoušení potoků/rybníků??? toby mělo víc věčí význam než dělání s 0.00000000000000000% něčeho neviditelnýho/nemastnýho/neslanýho. ale tady ňák tak intujitivně cejtíme žeto je jakože uplně pitomej nápad bojovat proti vodě/vodní páře :O ;D
bojovat proti co2 je stejná pitomina akorátže to rudozelený jakoby neviděj nebo vidět nechtěj :O :/
Estli je ve zduchu neceho "jen nedychatelnyho" 0.03 nebo 0.04% tak se nemame ceho obavat a vse kolem je jen ekoterorismus.Ale „ekoteroristi“ přece nebojují proti CO2 kvůli tomu, že zhoršuje lidské dýchání (a v těchto koncentracích opravdu nijak znatelně nezhoršuje), ale kvůli tomu, že otepluje planetu!
škoda žeto co2 moc na dělání globálního tepla nefunguje :O :/ si myslim že kdyby to co2 fakt jako fungovalo tak by bylo samo vo sobě uplně supr zelenou technologií na akumulaci ir záření/tepla a mohli by jsme si zněj udělat takovou jakože malilinkatou dysonovu sféru v atmosféře planety :D ;D
jestli těto jako uklidní tak praha je na seznamu cílů taky :D :D ;D ;D
si mužeš nastavit nějaký svoje barvičky když seti tydlety jakože nelíběj :D ;D
akorátže když jakože přetěžuju metody poděděný z qt nebo tam mam nějakej danej ukazatel na funkci jako datovej typ definovanej vtom mqttasync.h tak stim toho jako moc nejde dělat :O ;D
a jestli toje fujkyfuj nevim v c++ mužou funkce blejt vyjímky a to je asi jako víc zprávnější způsob jak to jako dělat :O :O
ISSN 1214-1267, (c) 1999-2007 Stickfish s.r.o.