Hlavní navigace

Textový editor na 350 řádků v Gtkmm

Tomáš Velecký 12. 4. 2011

Pokračování seriálu o gtkmm – dnes spolu začneme v C++ tvořit minimalistický, ale funkční textový editor. Tomu bude pro zajímavost předcházet malý průzkum kódu ostatních „konkurenčních“ editorů. Také si představíme další dva widgety a část XML kódu pocházejícího z knihovny Glade ve formě menu.

Jak to bude vypadat aneb menší statistika

Textový editor, to je velmi široký pojem. Samozřejmě, Microsoft Wordu nebo Open/LibreOffice Writeru se pravděpodobně moc podobat nebude. Zaprvé bych je označil spíše jako textové procesory a zadruhé, pokud bychom se zabývali tvorbou něčeho takového, neměl by se tento seriál jmenovat „Knihovna Gtkmm pro tvorbu GTK+ v C++“, ale raději ( ⇒ určitě) „Tvoříme textový procesor v gtkmm a C++“. To, co se musí vysvětlit na tomto ukázkovém programu, se totiž nevejde do jednoho článku, ale do dvou. Samotný kód našeho editoru by vydal na články tři. Jen tak pro zajímavost, OpenOffice.org je také v C++ a jako celek měl v roce 2003 asi 9 miliónů řádků kódu (zdroj).

Náš textový editor se tedy více podobá geditu nebo ne. I oproti těm je však velmi osekaný. Takový zdrojový kód ne „měří“ 42 tisíc řádků a geditu dokonce 5,4×104 řádků! (No dobrá, nebudu to tak zveličovat – 5,4×104 je jen 54 tisíc.) Pak tu máme ještě základnější editory – KWrite, jehož kód má asi 1 300 řádků a Leafpad – 8 000. Náš editor se asi nejvíce podobá Leafpadu, který ani nepodporuje zvýrazňování částí kódu a přesto má asi 8 000 řádků, což je asi 23krát více, než kolik má ten náš.

Takže co vlastně bude umět? Měl by alespoň základně podporovat klávesové zkratky Ctrl+C, Ctrl+V apod. Také by neměl mlčet (stavový řádek) a měl by umět říct něco o sobě (dialog „O programu“). Takovou slušností textového editoru je ochota upravovat text (nějaké pole pro práci s textem), a ten následně ukládat (položky v menu, propojené s klávesovými zkratkami; dialogy pro výběr cesty), popř. otevírat (práce s taby v „notebooku“). Některé texťáky, zvláště textové procesory, mají ještě další, obrázkové, menu. My budeme mít nějaký základ také.

Screenshot ukázkového programu

Novinky

Změna ikonky okna

Začneme něčím lehčím. Až doposud měly všechny programy ikonu jakéhosi prázdného okna. To může změnit metoda okenní třídy set_icon_from_file(), které jako argument předáme cestu k souboru s ikonou.

Ikona programu

Je v podstatě jedno, jaké bude mít ikona rozměry. Také je možné použít set_icon(). Tato metoda však vyžaduje ikonu v podobě třídy Gdk::Pixbuf, kterou pravděpodobně podrobněji rozebereme v jednom z příštích dílů.

Menu

Zatím všem oknům něco chybělo. Také vás to trochu štvalo? Už nebude, textový editor to menu mít bude. Tedy obligátní lištu nahoře, kterou najdete snad v každém programu s položkami Soubor, Úpravy, Nástroje, Nápověda a podobně, ale které se už různé aplikace, zvláště webové prohlížeče, zbavují, aby zobrazily co nejvíce z webové stránky. K tomu přidáme ještě „ikonkové menu“, nástrojovou lištu.

Ukázka jednoduchého menu

Jelikož se jedná o widget s velkým množstvím položek a kód by byl velice dlouhý, pomůžeme si s XML, které by vygenerovalo např. Glade. Právě proto zatím nebudu moc vysvětlovat, jak to funguje, ale zaměřím se na to, jak menu vytvořit. Více do hloubky to bude vysvětleno v díle o Glade, prozatím přikládám okomentovaný kód. Ke změně je třeba pozměnit pouze XML kód a přiřadit položku do ActionGroup.

#include <gtkmm.h>
#include <iostream>

class Okno : public Gtk::Window {
    public:
        Okno();
        void Handler();

    protected:
        Gtk::VBox ram;
        Glib::RefPtr<Gtk::UIManager> UIManager;
        Glib::RefPtr<Gtk::ActionGroup> ActionGroup;
        Glib::RefPtr<Gtk::TextBuffer> Buffer;
};

Okno::Okno() {
    set_default_size(200, 100);

    ActionGroup = Gtk::ActionGroup::create();

    // První argument metod create() musí korespondovat s označením prvku v XML
    // Druhý argument určuje, jaký název má daná položka nést, případně (pokud jej zadáe pomocí Gtk::Stock::NĚCO), jakou klávesovou zkratkou se dá vyvolat

    // Položka "Nabídka"
    ActionGroup->add(Gtk::Action::create("Nabidka", "Nabídka")); // Místo "Nabídka" může dát také Gtk::Stock::NĚCO
    // Submenu položky "Nabídka"
    ActionGroup->add(Gtk::Action::create("Podnabidka", "Soubory"));
    ActionGroup->add(Gtk::Action::create("Ukoncit", Gtk::Stock::QUIT), sigc::mem_fun(*this, &Okno::Handler));
    // Submenu položky "Podnabídka"
    ActionGroup->add(Gtk::Action::create("Ulozit", Gtk::Stock::SAVE), sigc::mem_fun(*this, &Okno::Handler));
    ActionGroup->add(Gtk::Action::create("Novy", Gtk::Stock::NEW, "Noový soouboor"), sigc::mem_fun(*this, &Okno::Handler)); // Ač je zadáno, že se jedná o Gtk::Stock::NEW, položka nese název Noový soouboor
    ActionGroup->add(Gtk::Action::create("Neco", Gtk::Stock::OPEN), Gtk::AccelKey("E"), sigc::mem_fun(*this, &Okno::Handler)); // Ač se má jednat o položku Gtk::Stock::OPEN, vyvolává se klávesovou zkratkou Ctrl+E, ne Ctrl+O

    UIManager = Gtk::UIManager::create();
    UIManager->insert_action_group(ActionGroup);

    add_accel_group(UIManager->get_accel_group());

    // Zde je rozvržení menu v XML
    Glib::ustring ui_info =
    "<ui>"
    "  <menubar name='Menu'>"
    "    <menu action='Nabidka'>"
    "      <menu action='Podnabidka'>"
    "        <menuitem action='Ulozit'/>"
    "        <menuitem action='Novy'/>"
    "        <menuitem action='Neco'/>"
    "      </menu>"
    "      <separator/>"
    "      <menuitem action='Ukoncit'/>"
    "    </menu>"
    "  </menubar>"
    "  <toolbar  name='NastrojovaLista'>"
    "    <toolitem action='Novy'/>"
    "    <toolitem action='Ukoncit'/>"
    "  </toolbar>"
    "</ui>";

    // Toto vygeneruje uživatelské rozhraní z výše napsaného XML kódu
    try {
        UIManager->add_ui_from_string(ui_info);
    } catch(const Glib::Error& ex) {
        std::cerr << "Selhalo vytvoření menu: " <<  ex.what();
    }

    add(ram);

    // Toto přiřazuje jednotlivé prvky UI příslušným kontejnerům (pokud daný prvek existuje)
    Gtk::Widget* Menubar = UIManager->get_widget("/Menu");
    if (Menubar) {ram.pack_start(*Menubar, Gtk::PACK_SHRINK);}

    Gtk::Widget* Toolbar = UIManager->get_widget("/NastrojovaLista");
    if (Toolbar) {ram.pack_start(*Toolbar, Gtk::PACK_SHRINK);}

    show_all_children();
}

void Okno::Handler() {
    std::cout << "Byla stisknuta nějaká položka v menu." << std::endl;
}

int main(int argc, char *argv[]) {
    Gtk::Main kit(argc, argv);
    Okno okno;
    Gtk::Main::run(okno);
    return 0;
}

Dialog „O programu“

Asi každý autor se chce nějak pod svým programem podepsat, případně zveřejnit e-mail, na který mohou uživatelé posílat bug reporty. Ani jednoduše zjistit, jakou verzi programu vlastně používám, nemusí být k zahození. Od toho je v gtkmm hned několik tříd standardizovaných dialogových oken. Jeden z nich řeší problém, který jsem právě popsal a jmenuje se jednoduše AboutDialog. Práce s dalšími dvěma je popsána níže.

Dialog O programu

Pro zadání toho, co se má v okně zobrazit, jsou určeny metody set_comments(), set_copyright(), set_license(), set_program_name(), set_version() a set_website(), jenž požadují pouze jeden řetězec a set_authors(), které předáme vector ze standardní knihovny. Rozdíl mezi set_copyright() a set_license() je, že do set_copyright() se píše, kdo program napsal, a to se poté zobrazí hned v hlavním okně dialogu; zatímco set_license() zobrazí licenci, kterou si může uživatel přečíst až po kliknutí na příslušné tlačítko (viz screenshot). V dokumentaci je jich samozřejmě ještě více.

#include <gtkmm.h>

class HlavniOkno : public Gtk::Window {
    public:
        HlavniOkno();

    protected:
        void OProgramu();
        Gtk::Button otevri_dialog;
        Gtk::AboutDialog o_programu;
};

HlavniOkno::HlavniOkno() {
    otevri_dialog.set_label("Chcu něco vědět o tomto programu!");
    otevri_dialog.signal_clicked().connect(sigc::mem_fun(*this, &HlavniOkno::OProgramu));
    add(otevri_dialog);
    show_all();
}

void HlavniOkno::OProgramu() {
    std::vector<Glib::ustring> autori(2);
    autori[0] = "První Autor";
    autori[1] = "Druhý Autor";

    o_programu.set_authors(autori);
    o_programu.set_comments("Super program, který o sobě umí něco říct!");
    o_programu.set_copyright("Copyright (C), První Autor a Druhý Autor, 2011");
    o_programu.set_license("Zde je licence.");
    o_programu.set_program_name("Název programu");
    o_programu.set_version("číslo verze");
    o_programu.set_website("domovskastranka.cz");

    gint result = o_programu.run();
    switch (result) {
        case GTK_RESPONSE_DELETE_EVENT:
        break;
    }

    o_programu.hide();
}

int main(int argc, char *argv[]) {
    Gtk::Main kit(argc, argv);
    HlavniOkno hlavni_okno;
    Gtk::Main::run(hlavni_okno);
    return 0;
}

Pokračování

Příště editor dokončíme. K dispozici samozřejmě bude ke stažení jeho zdrojový kód, který – jak již bylo naznačeno v úvodních odstavcích – nemá ani 350 řádků.

Našli jste v článku chybu?

12. 4. 2011 1:55

Zejména to, že není potřeba např. sto padesátý první audio přehrávač, který je ± stejný a umí ± to samé jako těch 150 co už tady bylo před ním (a někdy ani to ne).
A naopak třeba vytvořit nový kvalitní úsporný kodek se cení, ale to se nedá naklikat, ale musí se to vymyslet.


12. 4. 2011 0:42

Je pěkné, že je tu článek o vývoji aplikací.
Přiznám se, že jsem článek prošel dost rychle (protože sám používám fpc/Lazarus), ale chtěl bych se zmínit o délce kódu.
Ono je hezké, že se dá napsat aplikace s mnoha funkcemi pomocí několika málo řádků kódu, ale v praxi to znamená, že to vlastně někdo jiný napsal za nás. Moderní IDE/RAD nabízejí silné komponenty (vizuální, databázové apod.) a pak opravdu stačí jen naklikat to na Form a pomocí pár řádků to provázat.
Koneckonců, nedávno tu byla …

Podnikatel.cz: Dárky v podnikání. Jak je uplatnit v daních?

Dárky v podnikání. Jak je uplatnit v daních?

Podnikatel.cz: Přehledná titulka, průvodci, responzivita

Přehledná titulka, průvodci, responzivita

Vitalia.cz: Jak koupit Mikuláše a nenaletět

Jak koupit Mikuláše a nenaletět

Lupa.cz: E-shopy: jen sleva už nestačí

E-shopy: jen sleva už nestačí

DigiZone.cz: Další dva kanály nabídnou HbbTV

Další dva kanály nabídnou HbbTV

Podnikatel.cz: Prodává přes internet. Kdy platí zdravotko?

Prodává přes internet. Kdy platí zdravotko?

120na80.cz: Rakovina oka. Jak ji poznáte?

Rakovina oka. Jak ji poznáte?

Vitalia.cz: Jsou čajové sáčky toxické?

Jsou čajové sáčky toxické?

Měšec.cz: Zdravotní a sociální pojištění 2017: Připlatíte

Zdravotní a sociální pojištění 2017: Připlatíte

120na80.cz: Pánové, pečujte o svoje přirození a prostatu

Pánové, pečujte o svoje přirození a prostatu

DigiZone.cz: NG natáčí v Praze seriál o Einsteinovi

NG natáčí v Praze seriál o Einsteinovi

Vitalia.cz: Říká amoleta - a myslí palačinka

Říká amoleta - a myslí palačinka

Lupa.cz: Google měl výpadek, nejel Gmail ani YouTube

Google měl výpadek, nejel Gmail ani YouTube

Podnikatel.cz: EET: Totálně nezvládli metodologii projektu

EET: Totálně nezvládli metodologii projektu

Měšec.cz: Finančním poradcům hrozí vracení provizí

Finančním poradcům hrozí vracení provizí

DigiZone.cz: Recenze Westworld: zavraždit a...

Recenze Westworld: zavraždit a...

Lupa.cz: UX přestává pro firmy být magie

UX přestává pro firmy být magie

Vitalia.cz: „Připluly“ z Německa a možná obsahují jed

„Připluly“ z Německa a možná obsahují jed

Podnikatel.cz: Víme první výsledky doby odezvy #EET

Víme první výsledky doby odezvy #EET

Podnikatel.cz: Na poslední chvíli šokuje vyjímkami v EET

Na poslední chvíli šokuje vyjímkami v EET