Portál AbcLinuxu, 1. května 2025 07:11
Tento seriál je zaměřen zejména na programátory v Qt. Jeho cílem je ukázat, proč psát aplikace pro KDE a nejen pro čisté Qt a představit některé základní technologie KDE jako jsou KParts, Akonadi, Strigi nebo KIO.
V rámci toho seriálu budu poředpokládat alespoň základní znalost frameworku Qt. Základy pužívání Qt můžete nastudovat v seriálu o konzolových a grafických aplikacích v Qt. Dodatečné informace a návody k tomu, o čem budeme v tomto seriálu mluvit, jsou k dispozici na KDE TechBase. Podrobnou dokumentaci API najdete na api.kde.org. V rámci seriálu budu používat a odkazovat na stabilní API z KDE 4.5.
Na úplný začátek si ukážeme jak vypadá standardní KDE aplikace, jak používat okno a snadno nadefinovat menu a panely, následně si ukážeme pár zajímavých dialogů, kterými KDE oproti Qt disponuje. Záměrně nebudu zmiňovat základní grafické prvky jako tlačítka, vstupní pole nebo MVC, protože ty jsou téměř identické s těmi, které jsou dostupné v Qt, akorát místo na Q začínají na K.
Než se dostaneme k některým zajímavějším technologiím, ukážeme si, jak snadno a rychle vytvořit jednoduchou KDE aplikaci s menu. Je to totiž trochu jiné, než jak jsme zvyklí z Qt.
Základem každé grafické aplikace je okno. V nabídce jsou dvě třídy, klasické KMainWindow, které se používá téměr stejně jako QMainWindow a třída KXmlGuiWindow. Její výhoda oprotí první zmíněné je ta, že hierarchii menu a panelů můžete definovat v externím XML souboru. Pro příklad, vytvořme si třídu MainWindow odvozenou od KXmlGuiWindow a nadefinujme pro ni jednoduché menu a toolbar:
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <KXmlGuiWindow> class MainWindow : public KXmlGuiWindow { Q_OBJECT public: MainWindow(); ~MainWindow() {}; }; #endif
#include <KAction> #include <KActionCollection> #include <KLocale> #include <KStandardShortcut> #include "mainwindow.h" MainWindow::MainWindow() : KXmlGuiWindow() { // Vytvori menu Soubor -> Ukoncit KStandardAction::quit(this, SLOT(close()), actionCollection()); // Vytvorime KAction pro polozku "Zmena hesla" KAction *action = actionCollection()->addAction("set_passwd"); action->setText(i18n("Změna hesla")); // Vytvorime KAction pro polozku "Otevrit v programu" action = actionCollection()->addAction("open_in"); action->setText(i18n("Otevřít v programu...")); // Vytvorime KAction pro zaskrtavaci polozku "Zobrazovat tip dne" action = actionCollection()->addAction("tip_of_day"); action->setText(i18n("Zobrazovat tip dne")); action->setCheckable(true); // Vytvorime toolbar, pridame dialog pro klavesove zkratky a vytvorime GUI setupGUI(ToolBar|Keys|Create); }
Všimněte si, že nikde není určeno, do jakého menu se mají akce umisťovat. Rodičem všech akcí je KActionCollection, přičemž každé okno v KDE má vlastní takovou kolekci. Ta se postará o rozložení akcí do menu, na panelu atd.
Nyní vytvoříme příslušný XML soubor, ve kterém definujeme rozložení menu a toolbarů.
<?xml version="1.0" encoding="UTf-8"?> <gui name="dialogs" version="1" xmlns="http://www.kde.org/standards/kxmlgui/1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.kde.org/standards/kxmlgui/1.0 http://www.kde.org/standards/kxmlgui/1.0/kxmlgui.xsd"> <MenuBar> <Menu name="dialogs"><text>&Dialogy</text> <Action name="set_passwd"/> <Action name="open_in"/> <Separator/> <Action name="tip_of_day"/> </Menu> </MenuBar> <ToolBar name="mainToolBar"> <text>Main Toolbar</text> <Action name="set_passwd"/> <Action name="open_in"/> </ToolBar> </gui>
Syntaxe je celkem jednoznačná. Všimněte si, že atribut name odpovídá argumentu funkce addAction() v mainwindow.cpp. Tagem <text> se definuje popis položky, který se použije, pokud nenastavíte jiný pomocí metody setText().
Aby to ale nebylo příliš jednoduché, musí se v každé KDE aplikací ve funkci main() nastavit určité parametry. Jedním z nich je i interní název aplikace. Atribut name v tagu gui pak musí tomuto názvu odpovídat. Také název .rc souboru nemůže být libovolný, ale musí být nazevaplikaceui.rc.
#include <KAboutData> #include <KApplication> #include <KCmdLineArgs> #include "mainwindow.h" int main(int argc, char **argv) { // Kazda KDE aplikace musi mit KAboutData KAboutData aboutData("dialogs", // interni nazev aplikace - viz atribut name v <gui> a nazev .rc souboru 0, ki18n("Dialogy"), // plny nazev aplikace (musi byt prekladatelny) "1.0", // verze aplikace ki18n("Ukázka dialogů v KDE"), // strucny popis (musi byt prekladatelny) KAboutData::License_GPL); // licence aboutData.addAuthor(ki18n("Dan Vrátil")); // autor, take musi byt prekladatelny KCmdLineArgs::init( argc, argv, &aboutData ); // prida do aplikace prepinace --help, --help-kde, --help-qt atd KApplication app; MainWindow *mainWindow = new MainWindow(); mainWindow->show(); return app.exec(); }
Podstatné části jsou okomentované, zbytek je stejný jako v Qt aplikacích. KAboutData může obsahovat mnoho dalších informací, včetně seznamu autorů, kreditů, loga, adresi bugtrackeru a mnoha dalších, vizte dokumentaci.
Pokud teď zkusíte kód přeložit, žádné menu ani panel se vám nezobrazí. Je to proto, že KDE hledá .rc soubor v jedné velmi specifické lokalitě, a to v /usr/(local/)share/apps/nazevaplikace/. I před samotným testováním je tedy potřeba projekt nainstalovat. Nebo alespon zkopírovat .rc soubor do příslušného adresáře, například pomocí CMake.
# Nazev projektu, mel by se shodovat s internim oznacenim aplikace project(dialogs) # Najde KDE a nastavi promenne find_package(KDE4 REQUIRED) add_definitions(${QT_DEFINITIONS} ${KDE_DEFINITIONS}) include_directories(${KDE4_INCLUDES}) set(dialogs_SRCS main.cpp mainwindow.cpp ) kde4_add_executable(dialogs ${dialogs_SRCS}) target_link_libraries(dialogs ${KDE4_KDEUI_LIBS}) # Nainstaluje binarku do BIN_INSTALL_DIR, standardne tedy /usr/(local/)bin install(TARGETS dialogs DESTINATION ${BIN_INSTALL_DIR}) # Nainstaluje .rc soubor do /usr/(local/)share/apps/dialogs install(FILES dialogsui.rc DESTINATION ${DATA_INSTALL_DIR}/dialogs)
Po spuštění se zobrazí okno s připraveným menu a toolbarem. Všimněte si, že KDE za nás samo přidalo uniformní nabídky Nápověda a Nastavení s možností konfigurace klávesových zkratek pro jednotlivé akce a konfiguraci panelu. Stejně tak spuštění příkazu dialogs --help zobrazí standardní přepínače pro KDE aplikace. V dialogu O Aplikace se pak zobrazí právě ty informace, které jsme nastavili v KAboutData. Dost funkcí na těch pár řádek kódu, co?
V Qt jsou k dispozici dialogy pro otevírání/ukládání souborů, tisk, nastavení barvy nebo písma. Ty jsou v KDE samozřejmě taky, ale kromě nich existují i dialogy pro zadání nebo změnu hesla, dialog "Otevřít v...", otravný dialog s tipem dne nebo dialog pro hledání a nahrazení. Samozřejmě všechny takové dialogy není problém vytvořit v Qt odvozením od QDialog. To ale vede k tomu, že v každém programu pak dialogy vypadají a ovládájí se jinak. Použítím existujících dialogů se toto chování sjednocuje a je identické s tím, na co jsou uživatelé zvyklí.
Jako první si představíme dialogy pro práci s hesly. Nepodporují žádné šifrování, ukládání nebo autentizaci, jde pouze o dialogy pro zadání údajů. Předchozí aplikaci tedy rozšíříme o dialog pro změnu hesla a oveření hesla při spuštění programu. Heslo nebudeme nijak šifrovat, uložíme ho v plaintextu do globálního konfiguračního souboru aplikace.
... #include <knewpassworddialog.h> // /usr/include/KDE/KNewPasswordDialog neexistuje ... ... void MainWindow::showChangePassword() { // Vytvori dialog KNewPasswordDialog dialog(this); // Zakaze prazdne heslo dialog.setAllowEmptyPasswords(false); // Minimalni delka hesla je 8 dialog.setMinimumPasswordLength(8); // Popiska dialog.setPrompt(i18n("Heslo nebo život!")); // Modalne zobrazi dialog, pri potvrzeni vraci TRUE if (dialog.exec()) setPassword(dialog.password()); } void MainWindow::setPassword(QString newPassword) { // Ziska ukazatel na vychozi konfiguraci aplikace KSharedConfig::Ptr config = KGlobal::config(); config->group("Security").writeEntry("Password", newPassword); }
V konstruktoru je ještě potřeba spojit signál triggered() příslušné akce s vytvořeným slotem, aby to celé fungovalo.
Alternativní možností je použít nemodální dialog, ten se otevírá metodou show(). Při potvrzení se vyvolá signál newPassword(QString newPassword), při odmítnutí se vyvolá rejected().
Reimplementací virtuální metody checkPassword() lze definovat vlastní pravidla pro platnost hesla.
Při spuštění budeme heslo ověřovat dialogem KPasswordDialog. Pokud není žádné heslo nastavené, požádáme o zadání nového.
.. #include <KPasswordDialog> .. .. MainWindow::MainWindow() : KXmlGuiWindow() { KSharedConfig::Ptr config = KGlobal::config(); QString pass = config->group("Security").readEntry("Password", QString()); // Pri prazdnem hesle zobrazi dialog nastaveni noveho hesla, jinak pozada o zadani hesla if (pass.isEmpty()) { showChangePassword(); } else { // Vyvori dialog s checkboxy "Zapamatovat si heslo" a "Prihlasit anonymne" KPasswordDialog dialog(this, KPasswordDialog::ShowKeepPassword | KPasswordDialog::ShowAnonymousLoginCheckBox); dialog.setPrompt(i18n("Zadejte heslo nebo se přihlašte anonymně")); while (dialog.exec() && (dialog.password() != pass)) { // Nastavi dialogu chybovou hlasku "Spatne heslo"; dialog.showErrorMessage(i18n("Znovu a lépe...)", KPasswordDialog::PasswordError); } } ... ... }
Pokud bychom přihlašovací dialog řešili nemodálně, informaci o přijetí dialogu bychom získali signálem gotPassword(QString pass), respektive gotUsernameAndPassword(QString username, QString pass). Přítomnost vstupu pro uživatelské jméno se ovlivňuje příznaky v konstruktoru, kompletní přehled najdete v dokumentaci.
Otravný dialog, který nám při spuštění programu prozradí nějakou zajímavou klávesovou zratku, známe všichni. No někdy to může být užitečné, tak proč bychom si ho do programu nepřidali také! Dialog zobrazíme již v konstruktoru, ihned po autentizaci uživatele. Navíc ho zobrazíme nemodálně, aby se stihl vytvořit zbytek okna, než si dialog přečteme.
... #include... ... MainWindow::MainWindow() : KXmlGuiWindow() { // Autentizace uzivatele ... ... KTipDialog::showTip(this, QString(), false); // Vytvoreni menu ... ... // Vytvorime KAction pro zaskrtavaci polozku "Zobrazovat tip dne" action = actionCollection()->addAction("tip_of_day"); action->setText(i18n("Zobrazovat tip dne")); action->setCheckable(true); // Zaskrtne akci podle nastaveni TipOfDay/RunOnStart action->setChecked(config->group("TipOfDay").readEntry("RunOnStart", true)); connect(action, SIGNAL(toggled(bool)), this, SLOT(toggleShowTipOfDay(bool))); ... ... } ... ... void MainWindow::toggleShowTipOfDay(bool enable) { KTipDialog::setShowOnStart(enable); }
Jednoduché, že? Druhý parametr je cesta k alternativnímu souboru s tipy. Pokud je prázdný, použije se soubor /usr/(local/)share/apps/nazevaplikace/tips. Třetí parametr určuje, jestli jsme dialog vyvolali násilně nebo ne. Pokud nebyl vyvolán násilně, zobrazí se jen tehdy, pokud je zaškrtnuté "Zobrazovat tip dne při spuštění". Při násilném vyvoláni se dialog zobrazí bez ohledu na to, co nastavil uživatel. Uložení nastavení probíhá automaticky.
Formát souboru tips je zdruba následující:
<tip category="Dialogs|app"> <html> <p>Tento program v sobě ukrývá závažnou <strong>bezpečnostní chybu</strong>. Už jste ji objevili?</p> </html> <tip category="Dialogs|app"> <html> <p>Už jste zkusili odmítnout přihlášení?</p> </html> ...
V CMakeLists.txt je ještě potřeba změnit dva řádky, aby se aplikace přeložila a nainstaloval se soubor s tipy:
... ... # Pridejte ${KDE4_KIO_LIBS} (kvuli KFileDialog a KRun) target_link_libraries(dialogs ${KDE4_KDEUI_LIBS} ${KDE4_KIO_LIBS}) ... ... # Pridejte soubor tips do souboru k nainstalovani install(FILES dialogsui.rc tips DESTINATION ${DATA_INSTALL_DIR}/dialogs)
V tomto dialogu má uživatel možnost vybrat aplikaci, ve které chce otevřít nějaký soubor. Přímé použití tohoto dialogu je ale nevhodné, protože dialog sám neumí spustit vybranou aplikaci. Lepším řešením je použít třídu KRun, konkrétně funkci displayOpenWithDialog(). V menu našeho ukázkového programu už máme akci "Otevřít v programu...", takže stačí napsat příslušný slot.
... #include <KRun> #include <KFileDialog> #include <KUrl> ... ... void MainWindow::showOpenWithDialog() { KUrl url = KFileDialog::getOpenUrls().first(); if (!url.isEmpty()) { KRun::displayOpenWithDialog(url, this); } }
Když už tu máme KFileDialog, ještě zmíním existenci KEncondingFileDialog, který kromě výběru souboru umožňuje vybrat i vnější kódování, ve kterém se má soubor otevřít.
Nakonec ještě tarball s kompletními zdrojovými kódy.
Jak je vidět z příkladu, KDE se nám snaží spoustu věcí usnadnit tím, že je definuje a dodělává za nás. Zároveň je tím zajištěna uniformnost uživatelského rozhraní. Tohle ale není ani zdaleka vše, co KDE umí. Příště se už podíváme na pokročilejší technologie umožňující použití složitějších prvků napříč aplikacemi a zajišťující lepší integraci aplikací s prostředím KDE ale i s ostatními aplikacemi.
Nemůžu říct, jak je to s přenostitelností na MacOS. Existuje sice mac.kde.org, ale nepřijde mi to moc živé.
Na Windows je KDE for Windows. Problém je akorát s kompilátorem (aplikace musí být zkompilována stejným kompilátorem jako zbytek KDE), navíc ne všechny části KDE už jsou naportované na Windows. Také to je nepohodlné, protože je potřeba mít nainstalované minimálně kdelibs, které si samy přitáhnou spustu závislosti z GNU světa.
Ale závislosti se dají řešit produkováním staticky linkovaných binárek, případně distribucí potřebných knihoven spolu s programem (jak je to na Windows víceméně zvykem). Málo z uživatelů Windows bude mít zájem si cokoliv kompilovat, takže širší nasazení aplikace stejně bude vyžadovat vydávání binárních balíčků pro Windows, a tam si stačí udělat jeden buildroot s konfigurací a pak je to jen otázka toho nastartovat jednou za čas VirtualBox, aktualizovat zdrojáky a modlit se, aby se to přeložilo (vlastní zkušnosti ).
Poslední věcí platformou je Symbian a na ten nejspíš můžete zapomenout :)
Zcela jiným prvkem je stabilita. Té bych se asi na Windows bál. Základní věci (kdelibs) už budou odlazené, ale některé obskurnější funkce budou podle mě ještě hodně problematické.
No celkově z tohohle románu vyplývá, že s multiplatformností to není nejhorší, ale čísté Qt je lepší volba.
4.5.4 testuji uz pres tyden a ani amarok a ani digikam nemaji pod win7 zadne problemy .... zatim zadny pad ... coz sem celkem cumel ...
jen konqueror nejak nechce brat flash .....
mam kde 4.6.0 v gentooo a 4.5.4 v win7 a oboje behaji bez problemu .... takze zalezi na programatorovy aplikace jak multiplatforme to napise ... Qt a KDElibs s tim problem nemaji
to som velmi zvedavy co to je akonadi... lebo kym som tomu nevytvoril a nenastavil databazu v systemovom mysql, tak kmail 4.4.7 si zakazdym spustal mysql sam, co sa mu nedarilo a padal. O tom s velkou preciznostou informoval zmateneho uzivatela modalnym oknom. Potom som s velkou zvedavostou pozrel do tej databazy..a zistil ze tam prakticky NIC nie je (cca 5 tabuliek s 8 zaznamami spolu)!
Akonadi je nejen ta databaze, je to i sada tzv. "resources" (nenapada me ted vhodny preklad, "zdroj" mi prijde divny), ktere se staraji o ziskavani dat (napr. z emailove schranky), ukladani a opetovne zpristupnovani (napr. emailovemu klientu).
a tiez sa divim preco doteraz nebol nikto schopny vytvorit nejake UI na vyhladavanie cez strigi...
Jestli to nebude tim, ze Strigi je sluzba, ktera se stara pouze o indexovani dat. Pro hledani v techto datech je sluzba Nepomuk. Vyhledavat pomoci Nepomuku muzete napriklad v KRunneru nebo od KDE 4.6 i v Dolphinu.
Chapem to dobre ze kym konecne nepride stabilne KDE 4.6 o rok ci dva, tak budem musiet stale pouzivat find/locate/grep na hladanie suborov? Hoci tieto informacie v databaze strigi/nepomuk su? Je to taky problem na to spravit jednoduche GUI pouzitelne uz teraz v 4.4? Napriklad KRunner neponuka subory podla mena alebo obsahu, aspon u mna.a tiez sa divim preco doteraz nebol nikto schopny vytvorit nejake UI na vyhladavanie cez strigi...
Jestli to nebude tim, ze Strigi je sluzba, ktera se stara pouze o indexovani dat. Pro hledani v techto datech je sluzba Nepomuk. Vyhledavat pomoci Nepomuku muzete napriklad v KRunneru nebo od KDE 4.6 i v Dolphinu.
No KDE 4.6 melo puvodne vyjit jeste ted v lednu, za rok uz se bude blizit KDE 4.7 .
Nevim jak to bylo v KDE 4.4, ale v 4.5 uz to slape bez problemu pres KRunner, vyhledava i podle obsahu. V KDE 4.6 uz pujde filtrovat a vyhledavat i v Dolphinu.
to dnes vyslo ... , ten rok ale ubehl co? a jak bylo psano drive , krunner umi hledat v indexu strigi, nepomuk take a gui pro strigi je tez
to dnes vyslo ... , ten rok ale ubehl co? a jak bylo psano drive , krunner umi hledat v indexu strigi, nepomuk take a gui pro strigi je tez
Jo a ta mysql prý časem zmizí – nějaké lokální úložiště se už chystá.
Pry se pracuje na podpore pro Virtuoso, cili stejnou databazi jako pouziva Strigi.
ono uz je tam misto mysql Virtuoso .... , ale ma byt podpora pro vice backendu .. od sqlite po vetsi databaze ...
nevim proc mam na gentoo kde 4.5 s virtuosem a po mysql ani stopa ....
a 4.6 v dep tree taky neni ani stopa pro mysql
Tak ted jsem z toho nejaky zmateny. Prikaz akonadictl status
mi vyplivnul Akonadi Server Search Support: available (backend: Virtuoso)
, ale v kcm_akonadi je na vyber pouze MySQL a PostgreSQL a data z PIM aplikaci jsou opravdu ulozena v MySQL.
Jinými slovy, čím se zaobírá poslední už hotová část seriálu ?
Nechci jen popisovat API, chci ty technologie podrobneji predstavit, ukazat jejich vyhody (a pripadne nevyhody), poukazat na nejaky problemy a zakernosti. Urcite ale neplanuju rozebirat vnitrni fungovani tech technologii (to nikoho moc nezajima).
Zatim jsou planu jsou KParts, KIO, Akonadi, KWallet, Sonnet.
Co presne si vy predstavujete pod pokrocilejsimi tematy a technikami?
Bude se tento seriál věnovat i pokročilejším tématům a technikám nebo to je další z řady "úvodů programování v <doplň>API</doplň> pro uklízečky a chovance Jedličkova ústavu ?".Tak tohle mě dneska opravdu pobavilo
Co byste doporučili na vývoj KDevelop nebo Qt Creator?
Ja to beru jako logickou zalezitost - jednou je to QtCreator, tak proc by mel zvyrazonovat tridy KDE?
ISSN 1214-1267, (c) 1999-2007 Stickfish s.r.o.