Pagina principala
Informatii
Internationalizarea
pROgrame KDE/Qt
Download
Documentatii
Despre LKR
Contact
Resurse
Harta site-ului

Programarea setarilor utilizator in KDE 3


   Andreas Nicolai
   18 Mai 2003

Partea a-VI-a: Crearea si executia dialogului

In cele din urma ne intoarcem la sursa ferestrei principale pe care am vazut-o in prima parte. Mai jos sint modificarile aduse la fisierul antet settingstutorial.h:


#ifndef _SETTINGSTUTORIAL_H_
#define _SETTINGSTUTORIAL_H_

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <kmainwindow.h>

class QMouseEvent;
class KPushButton;
class PrefDialog;

/// Aceasta este fereastra principala a aplicatiei tutorial
class SettingsTutorial : public KMainWindow {
    Q_OBJECT
  public:
    /// Constructorul implicit
    SettingsTutorial();

  public slots:
    /// Executa dialogul de preferinte
    void executePreferencesDlg();
    /// Actualizeaza widget-urile astfel incit
    /// utilizatorul sa vada ca setarile au fost aplicate
    void applyPreferences();

  private:
    KPushButton    *m_theTextButton;
    PrefDialog     *m_prefDialog;
};

#endif // _SETTINGSTUTORIAL_H_
settingstutorial.h

In comparatie cu listingul din prima parte, acesta a fost modificat foarte mult. In primul rind destructorul nu mai exista (oricum nu aveam nevoie de el). Dupa care am adaugat doua sloturi publice executePreferencesDlg() care va executa dialogul si applyPreferences(). Singura diferenta dintre sloturi publice si functii membru publice consta in faptul ca sloturile pot fi conectate la semnale. O sa va explic mecanismul semnal/slot putin mai incolo. La urma am adaugat citeva variabile membru private: una pentru dialogul de preferinte si una pentru buton. Deoarece vom avea nevoie de ceva ca sa apsam pentru a afisa dialogul, vom inlocui eticheta "Hello world" cu un buton.

Acum sa aruncam o privire asupra implementarii. Acesta este noul settingstutorial.cpp:


#include <qevent.h>         // pentru QMouseEvent
#include <qlayout.h>        // pentru QVBoxLayout
#include <klocale.h>        // pentru i18n()
#include <kpushbutton.h>    // pentru KPushButton

#include "settingstutorial.h"
#include "settingstutorial.moc"

#include "prefdialog.h"     // clasa PrefDialog
#include "configuration.h"  // clasa Configuration si Config()

SettingsTutorial::SettingsTutorial()
  : KMainWindow(0, i18n("SettingsTutorial")), m_prefDialog(0)
{
  m_theTextButton = new KPushButton( this );
  setCentralWidget(m_theTextButton);
  applyPreferences();
  connect(m_theTextButton, SIGNAL(clicked()),
        this, SLOT( executePreferencesDlg()));
}

void SettingsTutorial::executePreferencesDlg() {
  // cream dialogul la cerere
  if (m_prefDialog==0)
      m_prefDialog=new PrefDialog(this);
  if (m_prefDialog->exec()==QDialog::Accepted)
      applyPreferences();
};

void SettingsTutorial::applyPreferences() {
  m_theTextButton->setText(Config().m_text);
  m_theTextButton->setPaletteForegroundColor(Config().m_textColor);
  m_theTextButton->setFont(Config().m_font);
};
settingstutorial.cpp

Comentariile dupa fisierele incluse ne arata clasa care este declarata in acel fisier. Atit fisierele antet din QT cit si cele KDE urmeaza urmatoarea conventie in ce priveste numirea fisierelor: de obicei sint numite precum clasa, dar cu litere mici si cu .h la sfirsit (la fel cum am facut si noi in acest tutorial).

Acum sa ne uitam putin la constructor. Continutul vechi l-am scos si am pus ceva nou in loc. Ati remarcat parametrul aditional in lista de initializare? Initializam pointerul la dialogul de preferinte cu 0. Vom discuta mai tirziu despre asta. Acum sa lamurim codul din constructor:

m_theTextButton = new KPushButton( this );

In aceasta linie cream un obiect KPushButton al carui parinte este fereastra principala Amintiti-va de discutia din partea a-V-a cu privire la ce obiecte trebuie distruse si care nu.

setCentralWidget(m_theTextButton);

Aici setam butonul ca widget central. Intr-un program normal un widget central va fi ceva mult mai complex: un editor de text sau un widget de vizualizare HTML. Oricum pentru exemplul nostru este foarte bun un buton. Fiind widget central KPushButton va umple toata fereastra principala.

Dupa aceea apelam functia/slotul membru applyPreferences() care seteaza trei proprietati ale butonului. Acestea sint proprietatile pe care le vom schimba in dialogul de setari.

Nota:
Aici vedeti importanta obiectului de configurare. Este foarte robust si inca de la inceput putem folosi functii care utilizeaza datele din configurare fara sa ne facem griji daca valorile sint sau nu valide. Asa ca nu vom duplica codul sursa si putem folosi functia membru applyPreferences() imediat.

In cele din urma "conectam" ceva. Acum va voi explica despre conceptul semnal/slot din QT: Ori de cite ori utilizatorul executa o anumita actiune pentru un widget, cum ar fi apasarea unui buton sau miscarea unui potentiometru (slider) etc., widget-ul va emite un semnal. Acest semnal difera de la widget la widget. Puteti invata mai multe despre aceste semnale aruncind o privire in documentatia QT/KDE. Un semnal poate trimite si parametrii! De exemplu QSlider va emite un semnal valueChanged() si va trimite ca parametru o valoare intreaga, ce reprezinta noua valoare a potentiometrului. Trimiterea de parametrii va scuteste de a citi manual valorile din widget Parametrii sint necesari in multe situatii, dar in tutorialul nostru nu vom folosi nici unul.

Un semnal poate fi conectat la un slot. Sloturile sint functii membru care au parametrii identici cu semnalele la care sint conectate. Nota traducerii: Exista si posibilitatea de conectare de semnale si sloturi cu parametrii diferiti. Altfel zis, puteti ignora parametrii transmisi de semnale.

In exemplul cu QSlider cream un slot sliderValueChanged(int newValue) si il conectam la semnalul valueChanged(). Linia de cod va arata asa:

connect(mySlider, SIGNAL(valueChanged(int)),
        targetWidget, SLOT(sliderValueChanged(int)));

Pointerii mySlider and targetWidet sint clasele detinatoare a semnalului, respectiv a slotului. Adica semnalul valueChanged() este un membru al obiectului mySlider si slotul sliderValueChanged(int newValue) este membru al obiectului targetWidet. Dupa conectare orice semnal valueChanged(int) al potentiometrului mySlider va avea ca rezultat apelarea targetWidget->sliderValueChanged(int) cu parametrul respectiv. Este foarte simplu.

Acum sa aplicam cele invatate in dialogul nostru. Avem un widget interactiv KPushButton. Acesta emite un semnal clicked() cind utilizatorul il apasa. Mai avem si un slot executePreferencesDlg(). Trebuie doar sa conectam semnalul la slot:

connect(m_theTextButton, SIGNAL(clicked()),
        this, SLOT(executePreferencesDlg()));

Functia connect() primeste patru parametrii:

  1. un pointer la widget-ul care emite semalul, in cazul nostru KPushButton
  2. numele semnalului in interiorul macroului SIGNAL(), care este clicked()
  3. un pointer la obiectul care va primi semnalul, care este fereastra principala, this
  4. numele slotului in interiorul macroului SLOT(), care este executePreferencesDlg()

Sa aruncam o privire la functia membru executePreferencesDlg():

if (m_prefDialog==0)
  m_prefDialog=new PrefDialog(this);

Codul de mai sus arata straniu nu? De ce nu cream dialogul pe stiva ori de cite ori functia este apelata (ca un obiect local si sa-l distrugem la iesirea din functie)? Performanta ar fi slaba. Nu este mare lucru sa cream dialogul local, dar in felul acesta dialogul va fi creat doar o singura data in timpul executiei programului. De ce nu cream dialogul direct in constructor? Poate nu vom folosi acest dialog de fiecare data cind programul va fi rulat, asa ca vom irosi timp pretios la inceputul executiei programului. Folosind "crearea la cerere" (create on demand), dialogul va fi creat doar o singura data, atunci cind va fi folosit pentru prima data.

Nota:
Un set reguli in ceea ce priveste crearea dialogurilor ar fi urmatorul. Daca dialogul este folosit (aproape) de fiecare data cind programul incepe, creati dialogul la initializarea programului. Daca dialogul este folosit din cind in cind, creati-l la cerere, ca in exemplul nostru. Daca dialogul este unul standard KDE, folositi dialogurile de-a gata ale librariilor KDE, fie ca este un simplu dialog de mesaje sau unul mai complex cum ar fi un dialog de editare de URL-uri.

Inainte de a continua va pun o intrebare. Ce se va intimpla cu programul nostru daca masina pe care ruleaza ramine fara memorie si operatia new esueaza? Este putin probabil sa se intimple asa ceva, dar... Nota traducerii: Iata o legatura pentru cei care nu cunosc foarte bine standardele C++: http://www.parashift.com/c++-faq-lite/

if (m_prefDialog->exec()==QDialog::Accepted)
  applyPreferences();

In cele din urma executam dialogul si daca dialogul iese cu "Ok" apelam functia membru applyPreferences(). Functia applyPreferences() este foarte simpla asa ca nu va fi explicata. Incercati sa va dati seama singuri. Observati ca folosim proprietati ale obiectului singleton care este intors de functia Config().

Compilati programul si testati-l. Ori de cite ori veti apasa butonul va aparea dialogul nostru.

Oricum, mai avem de implementat functionalitatea lui. Si o vom implementa, dar in partea a-VII-a. Proiectul curent il puteti descarca de aici: settingstutorial-04.tar.gz



Traducere de Bogdan Daniel Vatra. Adaptare de Claudiu Costin.