Qt designer project gondok

 ( atman | 2007. április 6., péntek - 10:42 )

Sziasztok!

Még elég új tag vagyok.Bocsássatok meg ha valami olyasmit kérdeznék amit már kérdeztek, de abban a stádiumban vagyok, hogy nem érek rá keresgélni. A szakdolgozatom témája egy (nagyon röviden) kliens szervek progi megirása + adatbázis kezelés. Én mivel szg hálózati mérnök leszek ezért Linux alatt akarom elkésziteni a progit grafikus környezetben. Ehhez a legjobb fejlesztői környezet számomra a Qt designer. Ezt a topicot szeretném használni az időközben felmerülő problémáim közszemlére bocsátásra, igy lehet hogy más is tanul belőle. amúgy majd a kész szakdolgozatomat is elküldöm annak aki kéri.

A most aktuális probléma: A programomat csak illetékesek használhatják username/passwd.
A progi föablaka előtt egy info gyüjtő ablak jelenik meg ami bekéri ezeket az adatokat. A föablak osztályának init() fügvényében hivom meg a bejelentkezö ablakot:
[mainform.ui.h:]

#include "passwdform.h"

void MainForm::init()
{
QDialog* passwdform = new PasswdForm;
passwdform->exec();
}

Ez addig jó is hogy a föablak meg sem jelenik mig a passwdform látható.

Probléma: Nem tudom hogy kell kivédeni azt hogy ha a felhasználó csak simán bezárja a passwdform ablakot akkor ne jelenjen meg a mainform ablak. Mert ezzel a koddal még csak ez történik. Melyik az a signal amit figyelni kell a passwdform bezarasakor vagy ha alapértelmezetten nincs ilyen akkor hogy kéne megirni. Gondolom ez a signal figyelné hogy az OK gomb a Cancel gomb vagy az X gomb vezetett az ablak bezárásához. ....
Nem akarok tovább "beszélni", hátha valaki csipőből tudja a választ.

Hozzászólás megjelenítési lehetőségek

A választott hozzászólás megjelenítési mód a „Beállítás” gombbal rögzíthető.

Szebb megoldás, ha a főablak menüjét cseréled vagy a menüpontok állapotot váltogatod... Mit is értek ez alatt?
1. Elindul a program. Megjelenik a főablak egy alap menüvel. File/Help... stb. Semmi funkció...
2. A használathoz be kell jelentkezni (vagy be lehet zárni a programot a File/Exit).
3. Ha bejelentkezik valaki megkapja a jogosultságának megfelelő menüt.
4. Így újraindítás nélkül ki és be lehet jelentkezni más-más felhasználóval...
5. Elegánsabb és kezelhetőbb megoldás... főleg ha adatbázisban van a felhasználó/jelszó/jogosultság tárolva...

Perger Attila

MAIN:

int main(int argc, char *argv[])
{
  QApplication app(argc, argv);
  Q_INIT_RESOURCE(images);

  QString error;
  SS::DataModule *dm = SS::DataModule::getDM();
  bool bResult;
  bResult = dm->doConnect(QString("QPSQL"), QString("localhost"), 5432, QString("SmartStorage"), QString("aperger"), QString("*******"), error);
  if (!bResult)
  {
    QMessageBox::critical(0, "" , error);
    return -1;
  }
  SS::MainWindows mainwindow;

  mainwindow.showMaximized();
  app.connect( &app, SIGNAL( lastWindowClosed() ), &app, SLOT( quit() ) );
	
  return app.exec();
}

LOGIN:

void MainWindows::on_actionLogin_activated()
  {
    LoginDlg login;
    int iResult = login.exec();
    if (iResult == QDialog::Rejected)
    {
      return;
    }
    UserList users;
    users.loadFromDB();
    User userdummy(login.editUser->text(), login.editPasswd->text(), QString(""));
    User user = users.value(userdummy.getName());
    if ( (user.getName()!=userdummy.getName()) || (user.getPassword()!=userdummy.getPassword()) ) {
      QMessageBox::critical(0, "" , "Invalid authentication");
      return;
    }
    // Itt kell beállítani a bejeletkezésnek megfellelő menüt....
  }

LOGOUT:
Itt csak vissza kell állítani az induláskori menüt...

A dolog egyszerűbb mint gondolnád.
A QDialog::exec()-nek ugyanis van egy visszatérési értéke.
A QDialog::done(int) slottal megadhatod közvetlen a visszatérési értéket, vagy az QDialog::accept()-tel a QDialog::Accepted-et, míg a QDialog::reject()-tel a QDialog::Rejected-et.

Nincs más dolgod, mint Ok (vagy Login) gomb megnyomását az accept-hez kapcsolni, míg a Cancelt a reject-hez, bár ebben az esetben a login ellenőrzését az exec meghívása után kell megtenned, és nem a dialog-on belül. Ha az ellenőrzést a dialog-on belül szeretnéd, akkor sincs gond, mivel a slot sima fv, így meghívhatod te is az ellenőrző rutinod végén. (Végén, mert bezárja az ablakot)
Ha a dialog ablak fejlécén van bezárás gomb, akkor az emlékeim szerint defaultból reject-re van kapcsolva.

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o

Köszi tr3w! Amit irtál az tuti működik utánnanéztem, de megerősitett a válaszod. Nem szoktam ölbetett kézzel üldögélve várni a megoldásra. :-)

De valamiért aperger megoldása szimpatikusabb lett. Talán mert igazad van abban,hogy igy "szebb" lesz a program. Viszont az a forrás amit mellékeltél hozzá az számomra teljesen érthetetlen. Lehet hogy azért mert Qt-ban még elég kezdö vagyok, de biztos hogy Qt-alapú a forrásod? Kérhetnék magyarázatot mellé. PL.: SS::... . Mi az az SS?

by atman

aperger megoldása sem más ha megnézed:

void MainWindows::on_actionLogin_activated()
  {
    LoginDlg login;
    int iResult = login.exec();
    if (iResult == QDialog::Rejected)
    ...

Itt van a kutya elásva.
Az, hogy a login eredényét hogyan, mikor használod fel, az már igazán rád van bízva.

(Szerintem, ha login nélkül nem lehet semmit csinálni a programban, és az átjelentkezés sem gyakori felhasználói tevékenység, akkor talán szerencsésebb (és egyszerűbb) rögtön az elején bekérni a login adatokat, úgy, ahogy te is akartad. De ez igazán feladatfüggő meggondolás.)

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o

Szia,

Hát az az "SS::" az valami hasonló, csak én definiáltam:


#include [iostream]

using namespace std;

int main(int argc, char *argv[])
{
  std::cout << "Hello world!\n"; // Így mindig jó
  
  cout << "Hello world!\n"; // Így is jó, ha ...

  return 0;
}

Okes vettem, hogy lehet megállapítani hogy mi történt. De... :-) mindig van egy de.

Tegyük fel hogy az esemény Reject. Jelzem előre nem a main.cpp ben akarom megoldani a problémát.

A main.cpp-ben deklarálva van a főablak MainForm mainform; Amint ehhez a sorhoz érkezik a program futása azzonal végrehajtja a kontruktorát + az én MainForm::init() függvényemet amit a Qt ajánl fel "külsö" kontruktorként. És a mainform.ui.h ban van kifejtve. Innen szeretném az egész programot bezárni "normálisan" ha Rejectet kapok, aminek a vizsgálata is az init() függvénynben van.
void MainForm::init()
{
QDialog* passwdform = new PasswdForm;
if ( passwdform->exec() == QDialog::Rejected ) {
QWidget* mainform = new MainForm;
mainform->close();
delete passwdform;
return;
}
}

Ez igy nem mükodik...

Először is bevallom, hogy nem használok Designert.
Tehát nem tudom, hogy az init pontosan mikor fut le, de gondolom közvetlen a konstruktor után (azaz onnan meghívva).

A fenti kód teljesen természetes, hogy nem működik. Mert mi is történik?
A MainForm::initben vagyunk tehát már van egy (feltehetőleg félig kész (azaz még a konstruktorban vagyunk)) MainForm-unk.

A te kódod ekkor:

QWidget* mainform = new MainForm;
mainform->close();

Azt csinálja, hogy létrehoz mégegy MainFormot (ami valszeg mekint meghívja az init-et, és innen ez rekurzió egész addig, míg rejectel lép ki az újra és újra mgjelenő login ablak).
Majd ezt az újonnan létrehozott ablakot bezárja. (Ez az ablak pedig meg sem jelenik, hiszen se szülője nincs, se a show fv-t nem hívod meg. Lényeges, hogy nincs szülője, mert a szülő show-ja meghívja a gyerekek show-ját, ezért nem kell a konstruktorban show-okat hivogatni, mikor felpakolod a gombokat, stb. (Amit te persze nem csinálsz, hiszen a Designer generálja neked ezt a kódot))

Tehát mégegyszer: a MainForm::init()-ben vagyunk, azaz már létezik (egy félkész) MainForm objektum, hisz épp azt konstruáljuk.
Erre az objektumra a "this" mutató mutat. Tehát, mi ezt az ablakot akarjuk bezárni, akkor a "this->close()" fvhívás kell.
Ilyet nem gyakran látsz, mivel a "this->" felesleges.
close() elég, mivel az objektum saját tagfüggvényét hívod.
(Fv hívásnál először megnézi az objektum saját tagfüggvényeit, ha ott nem talál ilyet, akkor sorban az ősökét, és csak ez után nézi a globális fv-eket.)

És akkor jöjjön, hogy ez miért nem szép.
Azért, mert a MainForm-ot már a konstruktorban bezárod.
Jobb esetben ez nem szép, rosszabb esetben (ha a trolltech beli kollégák nem figyeltek nagyon) ez memory leak-et, vagy runtime errort okozhat. (Egyébként valószínűleg nem.)

Két megoldás van.
Az egyik aperger megoldása, miszerint szépen megkonstruáljuk a MainForm-ot, majd abban van egy login menü, és utána tesszük elérhetővé a program szolgáltatásait.

A másik, hogy a main-ben először feldobjuk a login ablakot, majd csak utána (ha a login sikeres) konstruáljuk meg a MainForm-ot.

Valahogy így:

int main(int argc, char* argv[])
{
    QApplication app(argc, argv);

    PasswdForm passwdform;
    if ( passwdform.exec() == QDialog::Rejected ) 
    {
         return -1;    
    }
    MainForm mainform;
    mainform.show();
    return app.exec();
}

Mi is történik itt?
Alőször is lokális változóként hozzunk létre az ablakokat egész egyszerűen azért, mert megtehetjük. :) Gyorsabb, és kényelmesebb.
Azért tehetjük meg, mert a program sosem lép ki a mainből, csak ha már befejeződött.

A PasswdForm egy QDialog leszármazott, azaz saját eventloop-ja van.
Ez azt jelenti, hogy az exec hívásával elindul ez a saját eventloop, és addig fut, míg ki nem lépünk a Form-ból, ekkor a main futása folytatódik az exec()-től.

Most vagy kilépünk -1-gyel (esetleg hibaüzenet, vagy 3x-i próbálkozás, stb.) és akkor a lokális PasswdForm magától megszűnik (csodás nem?), vagy tovább megyünk.
Megkonstruáljuk a MainFormot, ami egy QWidget, vagy QFrame, vagy QMainWindow leszármazott, (ezeknek közös "őse" a QWidget), ami _nem_ QDialog tehát nincs saját EventLoop-ja. A QApplication EventLoopját használja.

A konstruktor persze meghívja a sokat emlegetett MainForm::init()-et is.

Ezután jon a mainform.show(), ami kirakja az egészet a képernyőre, és rögtön visszatér (tehát nem vár semmit).

Ezután az app.exec() elindítja a QApplication eventloopját, és akkor tér vissza, ha a főablakot bezárjuk (vagy egyéb agresszívabb megoldással kilépünk).

Visszatérési értéke pedig lehet a programunk visszatérési értéke.

Remélem minden világos.

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o

Hm... Ez tartalmas magyarázat volt. Szeretem az ilyet. Egy kezdő programozó számára minden ilyen magyarázat aranyat ér. Többet mintha elolvasna egy könyvet. Már annál a résznél leesett a tantusz, hogy rekurzio... Innentől egy hullámhosszon voltunk. Annyira nyilvánvaló az egész hogy nem értem hogy nem jöttem rá. Majd beküldöm az én megoldásomat amit a ti elképzeléseitek alapján gondoltam ki...

De... :-) ha már megint irok. A progi psql adatbázissal kommunikál + ipari kornyezetben müködö motorokrol gyüjt informáciot egy vezérlő (minden motornak saját vezérlője van) és ezzekkel a vezérlőkkel is kommunkiál a progi. A progi lekérdezi a motorokat idönként az állapotukrol és az infot elmenti az adatbázisban. A program előtt ülö felhasználo mindent megtudhat a rendszer összes motorjárol.

Problem: Elöfordulhat, hogy a proginak egyszerre kell az ügyfelet kiszolgálni és egy kommunikációt elvégezni a hálózaton. Az az érzésem hogy nem uszom meg hogy multithread-es progit irjak. Az igazsághoz hozzátartozik hogy a vezérlö is felveheti a kapcsolatot a szg-el és forditva. A vezérlö le lesz szimulálva egy másik progiban.
Mind az ügyfél program mind a vezérlö program ellát szerver-kliens funkciókat. Ha az ügyfélprogiban fut egy tcp/ip szerver akkor tuti hogy egyéb tevékenységek csak egy másik szálon futhatnak. Vagy rosszul látom?

Én még életemben nem írtam hálózatkezelést Qt alatt, de ahogy nézem ez is event-driven, azaz pl signalt küld akkor, ha új kapcsolat jött, vagy adat érkezett, tehát nem kell thread kezelés.

Ha jól értem amit írsz, akkor az adatgyűjtést én külön programba szervezném, ami mindig fut. Persze lehet ez is Qt-s (4.0 óta könnyen lehet GUI nélküli Qt-s programot írni).

Tehát lenne egy daemon ami lekérdezi a vezérlőket (vagy azok kapcsolódnak rá, ez végülis mindegy, de keverni nem érdemes), és az adatokat elrakja az adatbázisban.
Ettől teljesen függetlenül lenne a grafikus felület, ami az adatbázis adatait kezeli. Ha esetleg tényleg realtime adatokra lenne szükség, akkor azt is a daemonon keresztül kérném le.

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o

Felmerült ismét valami amit nem tudok megoldani vagyis nem tudom hogy müködik. Ez az ablakok sarkában található 3 gomb minimized, maximized, closed.
Hogy lehet elkapni ezeknek a gomboknak a signálját és egyénileg felhasználi ill a meglévő connect-et felül definiálni?

Close gomb:

void MainWindow::closeEvent(QCloseEvent*)
{
}

szoval a closeEvent-tet kell felulirni hozza.

Nekem pl. szokot lenni egy slotFileQuit-om es en azt hivom meg belule.

gondolom a tobbi event-tet is igy kell lereaglani.

Az ilyen jellegű event kezelőket megtalálod a QWidget leírásában, a Protected Functions résznél.
Ha nem tetszik a default működés, csak definiáld felül az eredetit (virtuális az összes).

Amit most konkrétan keresel, az a hideEvent, showEvent és a closeEvent.

Annyit érdemes még megnézni (de ez benne van a leírásban is), hogy lehetőséged van nem elfogadni (QEvent::accept(), QEvent::ignore()) egy eventet.
Ez pl akkor jön jól, ha x-re kattintva meg akarod kérdezni a felhasználót, hogy biztos ki akar-e lépni. Ha igen, akkor accept (vagy semmi, mert ez a default), ha nem, akkor ignore. Ennyi az egész.

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o

Ez bejött köszi. Simán ahogy "mondtad". Én is valami event félére tippeltem de nem goldoltam hogy ennyire egyszerü lesz.

Van egy kis problémám megint.

Szabad-e olyat csinálni, hogy A MainForm-ban deklarált FindData osztály-ban is deklarálva van a MainForm.

Erre azért volna szükségem mert a MainForm objetumaira szükségem van a FindData függvényeihez - irásra és olvasásra is -, de a FindData függvényeit a MainForban hivogatnám. Ja és ami még fontos. Mivel a FindData osztály egy adatbázisban keresgetne adatokat ezért a QSqlCursor-bol származtatnám,tehát az hogy orokolje a MainForm adattagjait eleve kizárva.

Röviden: persze.

Hosszan:
Általában ha csak egy-két fv-t akarsz hivogatni amik visszatérési érték nélküliek (ilyen do(), run(), calculate(), enablePause() jellegűek), akkor a Qt-s megoldás az, hogy saját slotot, meg signalt hasznáklsz.

Ha jól értem, akkor most neked nem ez kell.
Nyugodtan lehet egy MainForm* adattagod a FindData-ban. Ilyesmit tipikusan a FindData konstruktorában érdemes átadni.
Persze a maghívni kívánt fv-ek legyenek public-ok, vagy ha annyira FindData specifikus fv-ek, akkor lehet private vagy protected, és a MainForm-nak lehet a FindData barát osztálya (friend class).

Valmi ilyesmi:

// finddata.h
...
class MainForm;

class FindData : public...
{
public:
    FindData(MainForm* mf, ...);

private:
    MainForm* m_mainForm;
};
...

// mainform.h
...
class FindData;

class MainForm : public...
{
...
private:
   friend class FindData;
   Data getData() const;
   void setData(Data);

   FindData* m_findData;
};

Mint látod, nem kell a headerbe include-olni a másik headert (amíg csak a mutatóra van szükséged). Ez egyrészt gyorsítja a fordítást, másrészt megoldja a körkörös include problémáját.

Szerk:
Újraolvastam a kérdést. Ugye nem azt akarod, hogy a FindData fv-ei állítgatják a MainForm GUI elemeit (QLabel::setText, táblázat, stb).
Mert az úgy nagyon csúnya.
Arra kell törekedni, hogy a megjelenítés váljon el az adatkezeléstől.
Tehát úgy kell megírni a FindData-t, hogy akár egy consolos alkalmazás is használhassa. Tehát pl legyenek publikus fv-ek benne, amikkel minden lényeges adatot le lehet kérdezni, amikkel aztán fel lehet tölteni a GUI elemeket.
Persze ha egy vezérlőt QStringList-tel lehet egyszerűen feltölteni (pl QComboBox), akkor a FindData adhat vissza QStringList-et.

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o

Mindig nagy segítséget jelentenek nekem a válaszaid, ez most is igy lett.
Nem ma kezdted a "szakmát"!? Kérdéseimnek még nincs vége... csak szünetel egy kicsit amig megemésztem és alkalmazom a fent emlitett instrukciokat. Megfogadom a tanácsodat ismét. Jól ráláttál. Valóban a FindData-t akartam megbizni a kutakodással és a megjelenitéssel is. De ahogy rávilágittottál a dolgokra megértettem megint egysmást. Köszönöm.

Örülök, ha segítettem.
A "szakmát" bizonyos értelemben nem rég kezdtem (nem olyan régen keresek pénzt ezzel), más szempontból már több éve.
De azért nem volt az olyan rég, hogy 20 sor C++ kódomban 40 hiba volt. :)

A Qt rejtelmeivel meg magam is mostanában ismerkedek, így "benne vagyok" a dologban.

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o

Új problémám keletkezett. Ez inkább a c++ ismereteim hiánya mint a Qt.
Van egy QListBox-om a MainForm-ban megjellennek benne bizonyos adatok. Amikor kétszer klikkelsz egy adatra akkor a mélyebb szintet mutatja. Ezt sikerült jól megirni. Namost előfordul hogy nem arra az adatra akartál kétszer klikkelni ezért kell egy backButton. El kellene menteni az előbbi állapotot. Ezt én dinamikusan akartam megoldami, de valahogy a mútatókat nem sikerült még teljesen feldolgoznom.

A MainFormban lévő QListBox dinamikusan van deklarálva és úgy gondoltam hogy így elmenthetem:

MainForm::slotDoubleClick(const QString& item){
...
backListBox = new QListBox;
backListBox = findList;
...
}

MainForm::slotBackButtonClick(){
...
findList = backListBox;
delete backListBox;
...
}

Nem szintaktikai hibám van mert lefordul. De olyan mintha nem mentette volna el az adatokat, mert amikor visszaadja eltünik az ablak a MainForm-bol.

No.1: Miért Qt3.x? Miért nem Qt4.2?

No.2:
Most vagy rosszul másoltad ide a kódot, vagy nagyon nem értem.

Tehát gondolom a "backListBox"-ra akarod menteni a régit, ugye?

Nade: a backListBox és a findList mutatók. Mutatók között az értékadás pedig annyit tesz, hogy mindkét mutató ugyan arra az objektumra mutat.

Tehát, ha ezt írod:

backListBox = new QListBox;
backListBox = findList;

akkor először létrehozol egy QListBox objektumot a memóriában, amire ráállítod a backListBox mutatódat.
Ezután a backListBox mutatóját átállítod arra az objektumra amire a findList mutat.
Tehát van egy QListBox objektumod, amire semmi nem mutat, azaz nem is tudod többé felszabadítani (memory leak).
Illetve van két mutatód, ami ugyanarra a QListBox objektumra mutat.

Ugyan ez a probléma itt is:

findList = backListBox;
delete backListBox;

A findList mutatót ráállítod a backListBox által mutatott objektumra, azaz amire eddig mutatott a findList, arra valszeg nem mutat semmi. (Felszabadíthatatlan: memory leak)
Majd ezután felszabadítod a backListBox által mutatott objektumot, ami persze megegyezik a findList objektummal, tehát ezután mindkettő csak szemétre mutat a memóriában.

Mi történik a kettőt összerakva?
Kezdetben van egy QListBox-od, ami látszik is az ablakon, rá van állítva a findList.
Duplán rákattintasz, erre beállítod mindkét mutatót erre az objektumra.
Nyomsz egy back-et, amikoris újra mindkét mutatót erre az objektumra állítod (azaz kb nem csinálsz semmit), ezután törlöd ezt az objektumot, ami annak rendje és módja szerint eltűnik az ablakről. Cool...

Miért nem fog ez soha rendesen működni?
1:
Ha a mutatók által mutatott objektumot szeretnéd lemásolni akkor ez:

backListBox = new QListBox;
*backListBox = *findList;

Vagy még inkább ez:

backListBox = new QListBox(*findList);

kéne.
Az első az = (értékadás) operátort hívja meg két QListBox-ra, a másik meg a copy constructort (a második várhatóan gyorsabb, gyakran az első a másodikra van visszavezetve).
Ez persze nem működik, mert ilyenje _nincs_ a QListBox osztálynak.
A QListBox nem egy tároló, hanem a GUI része, különösebben nincs értelme másolgatni.

A tartalmukat kell tehát lemeteni, esetleg a pozíciót és hasonlókat.
Ahogy néztem viszonylag macerás kinyerni a tartalmat, lehet szerencsésebb az adatokat külön is tárolni (tehát feltöltéskor lemented az adatokat is egy változóba (pl QList), és duplaklicknél ezt másolod).

További gondolatok:
Ilyet nem csinálunk: backListBox = new QListBox;
Mi hiányzik? A Parent Widget. Qt-ben egy widgetnek nincs parentje, és az az ablak. Azaz elvielg ha parent nélkül létrehozol egy widgetet, és meghívod a show-t, akkor új ablakot nyitottál.
Ez azért is fontos, mert ha megadod a parent-et, akkor a konstruktor felveteti magát a parent gyermekei közé, tehát ha a parent megszűnik, autómatikusan megszünteti a gyermekeit is (ezért nem kell a widgeteket egyenként törölni), sőt egyéb esetekben is fontos, pl ha egy widgetre meghívod a setEnabled(false)-t, akkor a gyermekei is inaktívak lesznek.
Sőt, show-t se szoktunk hivogatni, méghozzá azért, mert ha a widget konstruktorában adjuk hozzá a widgeteket (gombokat pl) akkor a widget show-ja meghívja a gyerekek show-ját is. (Ezzel persze nem találkozol a Designert használsz).
Jelen esetben azért nem történt látszólag semmi, mert nem hívtál rá show()-t.

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o

1. Azért Qt 3.x - bár nem tudom, hogy honnan tudod hogy ezt használom - mert kb 5x telepitettem linux disztribuciók tömtelegét mire rátaláltam a Feroda Core 6 -ra ami stablilan fut a notebook-omon. Ezután - mivel lassal le kéne adni a szakdogát :-( - nem volt idöm hogy a Qt 4-hez szerezzek cuccokat. Örültem, hogy az aktuális Qt és a Postgres birják egymást és hajlandóak együtt dolgozni. A Qt 3.x-hoz van némi magyar nyelvü doksim. És példaprogik. Mivel kb. 3-hete programozok c++ -ban Qt alatt. :-)

2. Nem másoltama kódot, hanemcsak birtam azt a rész ami a kérdés szempontjábólfontos lehet. De jól ráéreztél megint a lényegre. Igy megint arra sikerült rávilágitnod amit kérdeztem.

"bár nem tudom, hogy honnan tudod hogy ezt használom"
4-ben nincs QListBox (illetve a könnyű átállás miatt át lett nevezve Q3ListBox-ra).

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o

Új ismeret hiányosság lépett fel. :)

1. Hogy lehet megvizsgálni hogy egy mutatóhoz rendeltem-e egy memóriaterületet? Nem akarok delele terület törlést használni ha nincs lefoglalva terület a mutatóhoz. Illetve ha már mutat objektumra akkor nem akarok ujjabbat létrehozni.

Globálisan van deklarálva egy QListView és egy QIconView tipusu mutató.
Egy azon területen jelenhet meg mind a két objektum ezért kell a vizsgálat. Plusz azért hogy ha valaki a létrehozó QPushButton-t állandóan nyumogatja akkor ne hozzon létre annyi példányt ahány gombnyomás történik.

Ne fáradj. Megvan a problemmegoldása. Csak azért irok ide egyból mihelyt felmerül valami mert lehel, hogy innen hamarabb megtudom a megoldást, minthogy átnézem az összes doksimat a válasz reményében. Most hamarabb akadtam a válaszra a doksiaim között. :-)

Tehat, hogy mas is értse:

QListView* listView;
QIconView* iconView;
QGridLayout* ViewBoxLayout;

//Globális változók

void MainForm::view_listview()
{
ViewBox->setColumnLayout(0, Qt::Vertical );
ViewBox->layout()->setSpacing( 6 );
ViewBox->layout()->setMargin( 11 );
ViewBoxLayout = new QGridLayout( ViewBox->layout() );
ViewBoxLayout->setAlignment( Qt::AlignTop );
listView = new QListView( ViewBox, "listView" );
ViewBoxLayout->addWidget( listView, 0, 0 );
listView->show();
}
// a listview objektum deklarációja;

void MainForm::view_iconview()
{
ViewBox->setColumnLayout(0, Qt::Vertical );
ViewBox->layout()->setSpacing( 6 );
ViewBox->layout()->setMargin( 11 );
ViewBoxLayout = new QGridLayout( ViewBox->layout() );
ViewBoxLayout->setAlignment( Qt::AlignTop );
iconView = new QIconView( ViewBox, "listView" );
ViewBoxLayout->addWidget( iconView, 0, 0 );
iconView->show();
}
// az iconview objektum deklarációja;

void MainForm::slotNewObjectAction()
{
if(!iconView && ViewBoxLayout) {delete ViewBoxLayout;}
if(listView) {delete listView;}
if(!iconView) {view_iconview();}
return;
}
// az iconview meghivása;

void MainForm::slotNewButton()
{
if(!listView && ViewBoxLayout) {delete ViewBoxLayout;}
if(iconView) {delete iconView;}
if(!listView) {view_listview();}
return;
}
// a listview meghivasa;

Csak röviden. :-)

by atman

Ez így általános esetben kevés lesz, bár a konkrét esetben lehet, hogy működik.

Az standard technika, hogy a "nem használt" mutatót 0-ra (C-ben NULL-ra) állítjuk, majd memóriafoglalásnál csak azt nézzük, hogy 0-e.
De delete-nél viszont 0-ra kell állítani (ez hiányzik nálad), mert a delete nem teszi ezt meg. Azaz a törlés után a mutató még mindig oda mutat, ahol az objektum volt.

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o

De az objektumot igy töröltem... Ahha értelek. Szóval az hogy törölsz valamit a memóriából még nem jelenti azt hogy ott minden érték nullára van állítva, csak azt jelenti, hogy mostmár bárki használhatja az a memória területet. Igy törlés után még rá is kell állítami nullára, hogy vizsgálható legyen. Illetve a globális deklarációnál is nullára kell állítani.

Egyrészt az biztos, hogy memoriaterület felszabadításánál nem nullázódik semmi.

De az
if (ptr)
csak azt nézi, hogy a mutató értéke 0, vagy sem. (Tehát nem az általa mutatott objektum 0-e.)
A pointer az csak egy számot tartalmaz (32 bites gépen pont akkora mint egy int), méghozzá az általa mutatott objektum címét a memóriában.
Definíció szerint 0-s cím nincs (ez nem feltétlenül 0 számot jelent, de mindenképp olyan értéket, ahol nem lehet memóriát foglalni).
Ezért ami 0-ra mutat, az nem mutat sehova.
Ezért adnak vissza 0-t hiba esetén az olyan fv-ek, amik mutatót kéne visszaadjanak.

Úgy érzem a mutatók körül van a fejedben egy kis kavar, ennek nézz utána.
Pár példa:

// *x az x által mutatott objektum, &y az y változó címe

int a=0;
int* b=0; // *b == undefined
b=&a;     // *b == a == 0; b!=0;
a=1;      // *b == a == 1
++(*b);   // *b == a == 2

int t[2];
t[0]=1;
t[1]=2;
b=t;      // b==&(t[0]); *b == t[0] == 1; *(b+1) == t[1] == 2;
++b;      // b==&(t[1]); *(b-1) == t[0] == 1; *b == t[1] == 2;
--(*b)    // b==&(t[1]); *(b-1) == t[0] == 1; *b == t[1] == 1;

Remélem minden világos...

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o

Nekem is lenne egy kerdesem itt, hamar szobakerult a qt4

Van egy QTableView-m amit egy QSqlQueryModel-el toltok fel. Szeretnem megkapni a View-nak azt az adatat amire rakatintok.

Ez megy is ugy, hogy lekerem a View indexet es az alapjan kiszedem a Model-bol. A gond az, hogy hasznalok QSortFilterProxyModel-lt is aminel ez a modszer nem mukodik.

Hogyan tudom megoldani?

udv.
greyow

Model/View-ben nem vagyok otthon, de ezt találtam:
http://doc.trolltech.com/4.2/model-view-selection.html

Így elsőre nekem elég zavaros, de gondolom azért van így túlbonyolítva, hogy minden model-view párosítással működjön.

Ha már most is így csinálod, akkor passz...

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o

Mindig azok a fránya mutatók. :-)

Nah: Szükségem van rá, hogy menet közben hozzak létre egy Widget objetumot. Namost ha statikusan deklarálom akkor a függvény lefutása után "meghal". Ha dinamikusan deklarálom akkor a függvény lefutása után még él ugyan, de nem tudom hogy a függvényből kilépve hogy hivatkozhatok rá hogy kinyirjam. :-)

void MainForm::view_welcomenote()
{
QTextEdit* textEdit = new QTextEdit( firstTab, "textEdit" );
...
}

Akarom, hogy éljen még, ugyanis attól hogy megjelent és mutatja az adatokat még nem biztos, hogy ebben a függvénben is kell eltüntetni. Az sokmindentől függhet hogy mikor.

De ezt:

void MainForm::valami_kill_fv()
{
delete textEdit;
}

nem tehetem meg mert nem fogja tudni, hogy ki az a textEdit.
Nem megoldás hogy ledeklarálom globálisként mert nem tudom, hogy mennyi fog kelleni belőle. Ugyanis ha létrejön egy akkor helyet kap a QTabWidget objektumon.
De ha bezárok egy tabot akkor ki kéne nyirni.

Hoppá:
QTextEdit* textEdit = new QTextEdit( firstTab, "textEdit" );

Ugye ha a firstTab-ot bezárom akkor viszi magával a textEdit-et is?
ez a megoldás...

Az első kérdés az, hogy miért kell neked mutató erre az objektumra. Ha csak a megszüntetés miatt, és a TextEdit tartalmát a signal-slot rendszeren keresztül szerzed meg, akkor nem kell.
Azért nem kell, mert a QTextEdit konstruktorának első paramétere a szülő, amit ha megadunk, akkor a QTextEdit bekerül a gyermekek közé, tehát amint a szülő widget megszűnik, megszűnik ez is.
(Az talán lényeges, hogy a QTabWidget::removeTab(int) nem szabadítja fel a widgetet.
Ha azt akarod, hogy meg is szűnjön, felül kell definiálnod a tabRemoved(int) virtual protected fv-t, és kézzel megszüntetni.)

Ha egyébként is el akarod érni a QTextEdit-et, akkor két lehetőséged van:
Az egyik (gondolom erre gondoltál), hogy a második konstruktor paraméterben megadsz egy nevet, majd eléred a qt_find_obj_child fv-nyel. Ez nem szép, viszont csúnya, és még lassú is...

A másik megoldás, hogy letárolod a mutatót. Hova? Hát természetesen az osztály adattagjai közé.
Ha nem tudod hány lesz, akkor tárolóba rakod, jelen esetben egy QList kell neked.
Legyen mondjuk az igen kreatív "textEdits" a neve. Ekkor a létrehozás ilyen lesz:

void MainForm::view_welcomenote()
{
   QTextEdit* textEdit = new QTextEdit( firstTab, "textEdit" );
   textEdits.append(textEdit);
   ...
}

Innentől a textEdits[n]-nel hivatkozhatsz a QTextEditekre.

Ja, most látom csak, hogy Qt3.x-ben nincs QList osztály...
Akkor a standard std::vector kell neked, append helyett push_back-kel.

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o

Úgy látom, hogy a QPtrList osztály tökéletesen alkalazható az álatad elmondotakhoz.
A leirásából legalábbis ezt veszem ki. Viszont ezenkivül van itt még valami.

Van a QTabWidget osztály: viewTab = new QTabWidget( centralWidget(),"viewTab" );
Ezt nem én deklaráltam hanem a mainform.ui ahogy létrehoztam.

Létrehozni benne egy új tabot igy lehet:
QWidget* newTab = new QWidget(viewTab,"newTab");
QGridLayout* tabLayout = new QGridLayout(newTab,1 ,1 ,0 ,6,"tabLayout");
QIconView* iconView = new QIconView(newTab,"iconView");
tabLayout->addwidget(iconView,0,0);
viewTab->insertTab(newTab,QString::fromLatin1("Felirat"));
iconView->show();

Én szertnék dinaikusan létrehozni tabokat és azon megjelenitő objektumokat mint pl.:egy QIconView.

Egy olyan signal-slot eseményre mint
pl.: signal: clickNewPushButton() -> slot: CreateNewTab()

Ezesetben nem mondhatom mindegyik tabra, hogy newTab. Hogy lehet ezt mégis dinamikusra csinálni. Az már nem gond hogy később hivatkozzak egy tabra, mert arra ott van a QTabWidget osztály: QWidget * page ( int index ) const függvénye.

Nem értem a kérdést...
Tehát van egy CreateNewTab slotod.
Az miért nem jó, hogy létrehozod az új widgetet (mint fent), és hozzáadod insertTab-bal?

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o

Azért nem jó mert nem tudom előre hogy hány azonos tipusú Tab-ra lesz szükségem. Pl.: QListView-ba akarom helyezni egy adatbáziskeresés eredményeit ami egy Tabon lesz. Ahány keresést hajt végre az illető annyi tab+QListView kell. Nem tudom előre hogy mennyi. Igy nem tudom megmondani előre annak a QWigdet-nek a nevét aki a legközelebbi Tab lesz.

Lehet, hogy ismét ráakadtam a megoldásra:
Globálisként deklarálva:

QPtrList'<'QWidget'>' tab;
QPtrList'<'QGridLayout'>' layout;
QPtrList'<'QListView'>' listView;
QPtrList'<'QIconView'>' iconView;

valami_fv()
{

tab.append( new QWidget(viewTab));
layout.append( new QGridLayout(tab.last()));
listView.append( new QWidget(tab.last()));
layout.last()->addWidget( listView.last(), 0, 0 );
viewTab->insertTab( tab.last(), tr("Welcome") );

listView.last()->show();
}

valami ilyesmi.
Nem tudom, hogy a > és < miért nem jelenik meg jól ezért tettem aposztrófok közé.

Ki van próbálva és működik :-)

by atman

Hmmm...
Tartok tőle alapvető gondok vannak...

Minek tárolod a mutatókat?
Egészen biztos vagyok benne, hogy pl a layout mutatókat a büdös életben nem használod ezen a fv-en kívül.
Melyiket használod egyáltalán máshol is?

Te írtad (kicsit hozzáírok):

valami_fv()
{
   QWidget* newTab = new QWidget(viewTab,"newTab");
   QGridLayout* tabLayout = new QGridLayout(newTab,1 ,1 ,0 ,6,"tabLayout");
   QIconView* iconView = new QIconView(newTab,"iconView");
   tabLayout->addwidget(iconView,0,0);
   viewTab->insertTab(newTab,QString::fromLatin1("Felirat"));
   iconView->show();
}

Azt talán kb tudod mi történik közben. De mi történik a záró }-nél?
megszűnik a newTab, tabLayout, iconView mutató.
De csak a mutató!!!
Maga az objektum amikre mutatott nem! Ezért használtunk new-t.
Az ott maradt a memóriában, a képernyőn, és működik is, hiszen az insertTab-bal befűztük a widgetek rendszerébe.

Tehát ahhoz, hogy új, független tab-okat hozz létre azokon különböző widgetekkel, nem kell semmilyen QPtrList-es ökörködés.

Az csak akkor kell, ha pl közvetlen el akarod érni valamely widgetet a program futása alatt később is. Tehát pl ha egy QListView-nek a tartalmát később is meg akarod változtatni. Akkor lehet használni a QPtrList<QListView>-et, de csak ekkor kell, és csak ez.

Más:

Mi ez a Globálisként deklarálva szörnyűség?
Objektum orientáltan programozol, az esetek 99,99%-ban nem kell globális változó.
Azért objektum orientáltan, mert ezt az osztályt amit most csináltál, felhasználhatod máshol is. Egy programon belül többször. Ha globális változókat használsz akkor ez a két független objektum ugyanazokat a globális változókat használja, és összekeveredik. (ez meg nekünk nem jó (kivéve, ha pont ezt akarjuk, de akkor meg statikus adattagokat használunk).

Tehát miért nem adattagként deklarálod az osztályban?

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o

Egyszerü.:-) Már próbáltam. Az viszont lehet, hogy ezt is rosszul.

Pl.: Azt biztos tudod, hogy a mainform.h-t és a mainform.cpp-t nem érem el közvetlenül. Ezeket a fájlokat a Qt csinálja a mainform.ui-bol. Egyedül amin modositani tudok az a mainform.ui.h . Itt lehet ugyan még osztályváltozókat deklarálni, de csak olyan tipusokat melyek a mainform.h-ban class ként vannak implementálva.
Igy pl.: tudok QString változót deklarálni, de nem tudok QPtrList-et vagy QIconView-te stb. Ezért döntöttem a globális deklaráció melett.

2. Igazad van, a listView-en kivül nincs szükségem a többi mutatóra. Bár emliteted, hogy a removeTab nem fogja a Tab tartalmát is megszüntetni ezért nekem kell gondoskodnom róla. Ahhoz meg szükségem van a mutatókra.

Hogy tudom elérni hogy az aktuális ablakon álva az egérrel. Jobb click esetén az aktuális tab bezáródjon.

Igy nem ment:
connect( listView.last(), SIGNAL( rightButtonClicked() ), viewTab, SLOT( removePage(viewTab->currentPage()) ) );

"Azt biztos tudod, hogy a mainform.h-t és a mainform.cpp-t nem érem el közvetlenül. Ezeket a fájlokat a Qt csinálja a mainform.ui-bol. Egyedül amin modositani tudok az a mainform.ui.h . Itt lehet ugyan még osztályváltozókat deklarálni, de csak olyan tipusokat melyek a mainform.h-ban class ként vannak implementálva."

Nekünk erre a következő trükköt tanították:
Designerben nem a MainForm class-t hoztuk létre, hanem a MainFormBase-t vagy ha ugy jobban tetszik MainFormUI-t.
Majd ebből származtattuk a MainForm class-t, valahogy így:

// mainform.h
#include "mainformbase.h"

class MainFrom : MainFormBase
{
...
};

A MainFormBase signaljai és slotjai működnek, össze vannak kapcsolva, minden jó, közben meg bármit hozzávehetsz pluszban amit csak akarsz. Ekkor a mainformbase.ui.h nem is kell (ha emlékezetem nem csal).
Konstruktort/destruktort persze létre kell hozni, és persze az argumentumokat át kell adni a Base konstruktorának.
Részletesebben:
http://doc.trolltech.com/3.3/designer-manual-6.html
Subclassing szekció. (Ha nem is olvasod el, a kódrészletek magukért beszélnek.)

"Bár emliteted, hogy a removeTab nem fogja a Tab tartalmát is megszüntetni ezért nekem kell gondoskodnom róla. Ahhoz meg szükségem van a mutatókra."
Nincs. Pláne ennyire.
Ha már mindenáron rögtön törölni akarod, akkor sem kell az összes mutató, elég a fő widget (létrehozáskor a newTab ha jól látom) címe, arra delete-t hívva az automatikusan megszünteti a gyermekeit (a layout-ot, meg a többit).
Ez a mutató meg úgyis kell a removePage-hez (most látom, hogy Qt3.3-ban ez van, nem removeTab).
Tehát:

void MainForm::removeCurrentPage() //ez egy slot
{
    QWidget* removeIt=viewTab->currentPage();
    viewTab->removePage(removeIt);
    delete removeIt;
}

és ekkor azt hiszem, hogy a

connect( listView.last(), SIGNAL( rightButtonClicked() ), this, SLOT( removeCurrentPage() ) );

jó lesz neked.

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o

Ez a connect:

connect( listView.last(), SIGNAL( rightButtonClicked() ), this, SLOT( removeCurrentPage() ) );

Nem müködik. És azt is tudom miért.
Ennek a szignálnak: rightButtonClicked() a pontos definiciója:

void rightButtonPressed ( QListViewItem *, const QPoint &, int )

Ebből és a leirásából én azt veszemki, hogy csak akkor ad jelzést, ha egy valós QListViewItem * -re történt egy jobbklick. Üres ablak esetén nem ad szignált. Igy nem tudom használni a currentPage() bezárására. Valami más kell. Ugyérzem nem fogom elkerülni hogy irjak egy class ListView : public QListView osztályt amiben átdefiniálom a mouseReleaseEvent ( QMouseEvent * e ) függvényt amin belül megvizsgálom, hogy melyik egérgombot engedtem fel és ha QMouseEvent::button() = RightButton akkor bezáresemény indul.

Remélem nem lesz ilyen bonyolult...

Pedig könnyen lehet, hogy ez a megoldás. Nem kell aggódni, ez nem olyan bonyolult, vagy hosszadalmas.
Arra figyelj, hogy ha nem jobb gomb volt, akkor érdemes meghívni az eredeti kezelőt.
Szerintem egyébként gondold meg, hogy mennyire szerencsés ilyen egyedi működést írni, általában jobb, ha próbálsz alkalmazkodni a környezethez. Egy felhasználó pl nem nagyon fog rájönni erre a bezárásra...

Én egyébként jobbgomb esetén sem a bezárást hívnám meg, hanem csinálnék egy jobbgomb eseményt, majd azt kötném a bezáráshoz. Ez jobban illik a rendszerbe. (És meghívnám jobbgomb esetén is az eredeti kezelőt.)

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o

:-) Tudni fogja. Bár hozzákell tennem mivel ez egy szakdolgozat miatt iródik igy nem nagyon fogja ezt a verziót v1.0 felhasználó használni. Jó programozónak tünsz, igy biztosan te is úgy gondolod, hogy "Egy program soha nincs kész! Saw by atman" Nem biztos, hogy ez az angol kifejezés igy helyes, de mindegy. Honnan fogja "egyébként" tudni egy ezt a programot használó felhasználó, hogy jobgomb egy tabon és a tab bezárul? Te még soha nem probáltad ki egy uj alkalmazásnál, hogy a jobbgomb mit csinál? Nem biztos, hogy erre gondol de az tuti hogy hamar megtanulja. :-)

Egyébként nem egyböl fog bezárodni az a tab. Mivel előbb-utóbb a listView-nek ill. az iconView-nek lesznek elemei nem akarom ezt a funkciót csak úgy elkotyavetyélni. Egy Property ablak fog megjeleni amin kiválaszthatja, hogy mit szeretne tenni: a tabbal, azzal az elemel amin jobb gombot nyomott, esetleg akar nyitni egy új tabot stb....

Igy valóban nem közvetlenül a bezárás funkciók hajtódnak majd végre. Amúgymeg miért kellene visszaadnom az irányitás, ha már megszereztem az eredeti kezelőnek? Ha véletlenül bal gombot nyomtak le akkor arra is megirom azt a függvényt amit kigondoltam ehhez az eseményhez...

De az is lehet, hogy mivel nem sok idöm van a szakdoli leadásáig ezek a funkciók csak a v1.1 ben lesznek benne. Hozzáteszem lehet hogy nem is lesz v1.1 :-)

Valami context menüt akarsz?

Erre van beépített megoldás:
http://doc.trolltech.com/3.3/qwidget.html#contextMenuEvent
Ez igazából csak az eventkezelő, amit általában a jobbklick vált ki.
A legtöbbször persze QPopupMenu-t szoktak használni.

"Te még soha nem probáltad ki egy uj alkalmazásnál, hogy a jobbgomb mit csinál?"
Nem szoktam össze-vissza kattingatni. Akkor nyomok jobb gombot, ha logikus lenne hogy történjen valami, pl azért, mert más programban hasonló helyzetben szokott történni. (Arról nem beszélve, hogy nem vagyok átlag felhasználó.)

Itt a konzisztencia a lényeg. Nem véletlen, hogy minden komolyabb rendszerhez (win, osx, gnome, stb) több 100 oldalas doksi van, amiben olyan teljesen feleslegesnek tűnő előírások vannak, hogy a menubar első eleme a fájl legyen (már ha van), és az első menü legalsó eleme meg a kilépés.

Ilyesmire figyelni kell, mert enélkül a felhasználó semmit nem talál ott, ahol keresi, és sokkal tovább tart megtanulni a program kezelését, és fárasztóbb lesz a használat.

Most persze nem konkrétan a jobbgombos megoldásodra gondolok (a context menü nem egy egzotikus megoldás, tehát jó lehet), ezekre általában figyelni kell.

"Amúgymeg miért kellene visszaadnom az irányitás, ha már megszereztem az eredeti kezelőnek?"
Azért érdemes továbbadni, hogy a default működés is megmaradjon. Pl a kijelölés egy táblázatban.
De szerintem nézd meg a context menu eventet, azt gondolom az kell neked.
Sőt, ha már QListView, akkor:
http://doc.trolltech.com/3.3/qlistview.html#contextMenuRequested
Így még subclass sem kell.

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o

Elkezdtem objektum orientáli az eddig elkészitett kódomat, mivel nem volt túl szép egy szakdolgozathoz,meg elég sokat fejlődtem a kezdethez képest, amit leginkább neked köszönhetek tr3w. Több osztályt csinálok és az eddig mainform.ui.h-ban lévő kódot szétdobálom, logikusan ezekbe.
Igy járt az adatbázis kapcsolatért felelős uj Connection osztály is. Plusz ahogy tanácsoltad létrehoztam egy mainformbase osztályt is. A mainform.ui.h-ban csak az marad amit amugy sem akarok változtani, elérni a későbbiekben. Pl.: a slotHelpAboutAction().

Azzal vagyok bajban, hogy az igy megirt connect függvények nem mindegyike akar múködni. pl.: connect( findList, SIGNAL( highlighted(const QString&) ), this, SLOT( slotFindList(const QString& elem) ) );

ez nem működik. azt mondja,hogy:
QObject::connect: No such slot MainFormBase::slotFindList(const QString&elem)
QObject::connect: (sender name: 'findList')
QObject::connect: (receiver name: 'MainForm')

Pedig a connect-ben szereplö this az pont a MainFormBase aminek a MainForm a szülőosztálya. Még mielőtt megkérdeznéd szerepel a Q_OBJECT makró a MainFormBase osztályban.

Bocs a szünetért; "nyaraltam".

A megoldás egyszerűbb mint gondolnád:

connect( findList, SIGNAL( highlighted(const QString&) ), this, SLOT( slotFindList(const QString&) ) );

Tehát az "elem" nem kell.
Idióta dolog, de ez van.

Ez az őskövület signal-slot kezelés a qt legnagyobb hibája (szerintem), és nagyon reménykedtem benne, hogy a qt4-ből kidobják, de nem... Nyilván kompatibilitási okok miatt. Félek tőle sosem kerül ki.

A gondot az okozza, hogy ez az egész signal-slot rendszer nem a C++ keretein belül lett megoldva (egyszerűen azért, mert mikor a Qt fejlesztése kezdődött (1991), még nem is volt C++ szabvány, nem hogy szabvány követő fordító (mondjuk az most sincs. :) )). Ha belenézel a generált moc_* fájlokba, elég csúnya dolgokat láthatsz, és kb fogalmad lesz, hogy hogyan is működik ez az egész. Valójában string manipulációkkal találja meg a connect fv a megfelelő signal-slot párokat. Ezért nem ad fordítás közben hibaüzenetet.

C++-os megoldás persze van (ld libsigc++, boost.signals, stb.), de úgy tűnik a qt-ban ez marad még jó darabig.

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o

Pont "jókor" mentél el nyaralni, ugyanis én sem voltam gép közelben tegnapig. Most volt az utolsó konzultációm,és minden napra jutott egy félévközi jegy vagy egy aláirást adó ZH...

Erre pont nem gondoltam, de igazából mint ahogy egy nőnek is vannak hisztijei amit soha nem fogunk megérteni :-) ,igy egy fejlesztő könyezetnek is lehet. Megérteni ezt sem fogjuk, de elfogadjuk és használjuk akkor úgy ahogy kéri...

Nem tul sokat,fogok mostmár kérdezni mert el kéne kezdeni irni a szakdolgozatot - hamar itt lesz május 11 - és igy csak a v0.1 fog elkészülni. :-) Csak olyan funkciók lesznek benne ami a szakdogához feltétlen kell. Meg tudjam csinálni az osztálydiagrammot illetve a függvények rövid leirását...

"A program is never done! by atman"

Valahogy,dokumentánom kéne a program fejlesztését, felhaszálói használatát. Úgy hallottam, hogy UML diagrammot is kéne produkálni.Ehhez nem tudom melyik a legjobb program linux alá. Te hogy építenéd fel a program leírását, bemutatását egy szakdolgozatba?

"A program is never done! by atman"

UML-diagramm készítő programot egyet sem ismerek (illetve a Rational Rose-t egyszer láttam, de az egyrészt pénzes, másrészt ágyúval verébre), de rákerestem guglin, és találtam egy csomó GNU/BSD-set. Jelen esetben majdnem mindegy szerintem.

Program leírással mindig gondban voltam, én ilyenkor valamelyik régi doksimat és/vagy barátom doksiját veszem elő, és felhasználom annak szerkezetét.

Neked is csak azt tudom ajánlani, hogy szerezz 1-2 (lehetőleg jól sikerült) szakdolgozatot, és törekedj hasonló felépítésre.

Ennél konkrétabb tanácsot már csak azért sem adhatok, mert ez valamennyire sulifüggő is.

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o

Ez jó ötlet csak már késő sajnos. Igazad van bele kellet volna néznem egy pár szakdoliba, de én 300 km-re lakok a sulitól és legközelebb más a szakgolim leadásakor utazok fel + letenni az utolsó 3 vizsgámat. Már alig várom ezt a pillanatot.

"A program is never done! by atman"

Én az Umbrello neveztűt használom, elég könnyű használni, be tud olvasni forráskódot, tud forráskódot generálni is stb. Az UML diagrammot menthetet bármilyen képkét, vagy XML-be. A legtöbb linux csomagkezelőjében benne van.

--
A lehetetlen csak a lusta ember kifogása!

Köszi ismerem, vagyis most ismekedek vele.

"A program is never done! by atman"

Született megint egy problémám.

Van egy QTabWidget -em (motorTabWidget) a MainForm-on. Tartalmát dinamikusan töltöm fel.

Készítettem egy class MotorTab : public QWidget osztályt.
Ez az osztály alkotja az összes Tab-ot a motorTabWidget-en igy:

motorTab = new MotorTab(motorTabWidget);
motorTabWidget->insertTab(motorTab, trUtf8("Motor"));

Namos az a kérdésem, hogy miután létrehoztam mondjuk 5 pédányt a motorTab-bol utána, hogy tudom elérni az egyes motorTab-ok elemeit?

Az nem működik, hogy:

motorTabWidget->page(2)->feszultsegSlider->setValue(100);

és az sem hogy:

motorTab = motorTabWidget->page(2);
motorTab->feszultsegSlider->setValue(100);

Több tippem nincs. Azt hittem hogyha a motorTab szülőosztája QWidget akkor egy olyan függvényt ami QWidget-et ad vissza (pl QTabWidget::page() ) átadhatom neki...

Mégis, hogy kaphatnám vissza az egyes tabok cimeit hogy kezelni tudjam az adattagjait?

"A program is never done! by atman"

Bocsi, már megvan. Visszaolvastam egy korábbi hozzászolást, mert rémlett, hogy volt hasonló problémám.

A megoldás a QPtrList motorTab.

"A program is never done! by atman"

A gondot az okozza, hogy a QTabWidget::page(int) QWidgetre mutató pointert ad vissza, QWidgetnek meg nincs feszultsegSlider adattagja.

A kérdés tehát az, hogy hogyan lesz a QWidget*-ból MotorTab*.

QWidget* ptr=motorTabWidget->page(2);

//C-s szintaxis: (csak kompatibilitás miatt van)
MotorTab* mt1=(MotorTab*) ptr;  

//C++-os: 
MotorTab* mt2=static_cast<MotorTab*>(ptr);  
// Működik de nem szép, ha valamit eltoltál (pl a QWidget nem ősosztálya a MotorTabnak,
// vagy bármi miatt értelmetlen a konverzió), ez akkor is megcsinálja és 
// az adattagra hivatkozásnál látszólag minden rendben, tagfüggvénytől meg elszáll..

//Méginkább C++-os:
MotorTab* mt3=dinamic_cast<MotorTab*>(ptr);
// Ha a konverzió meghiusúl, mt3==0, ezt egyszerű if-fel ellenőrizheted.

Tehát röviden:

motorTab = dynamic_cast<MotorTab*>(motorTabWidget->page(2));
Q_ASSERT(motorTab!=0);
motorTab->feszultsegSlider->setValue(100);

Én szoktam assertezni, biztos ami biztos, ha baj van rögtön kiderül (debug fordításnál), a relese-ből meg egyszerűen kimarad ez az ellenőrzés (tehát nem lassít, nem mintha számítana...).
http://doc.trolltech.com/3.3/qapplication.html#Q_ASSERT

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o

A QPtrList müködik, e ezt is ki fogom próbálni, mert előfordulhat, hogy valahol, valamikor ez a jobb megoldás és ahhoz gyakorolni kell a használatát.(Ez a Server-Monetcon project-hez tartozik)

Te miből tanultad a c++ -t. Valahogy én is úgy érzem, hogy egész mélyen meg kell értenem a működését ahhoz, hogy egyéni ötletek jussanak az eszembe a menet közben felmerelü problémák kezeléséhez.

Pl.: a friend működését sem értem még, úgyanis tegnap sok szívás ellenére sem működik úgy ahogy akarnám.(Itt a Client-Monetcon projected említem)

Mint ahogy azt már tudod a MainFormBase-m szülő osztálya a MainForm, igy a viewTab (QTabWidget) adattagot is gond nélkül eléri. Külön osztályt csináltam minden a viewTab-on megjelenhető widgetnek.
Igy van egy olyan wigdet osztály is ami egy IconView-t jelenít meg.

Nem tudtam tegnap semmilyen modón megoldani, hogy a fent említett osztály el tudja küldeni a MainFormBase-nak hogy melyik elemén történ dupla kattintás. Sikerült ugyan meghívni a MainFormBase egy fv-ét és látszólag az adat is átmegy, de olyan mintha a függvény ezután senkihez sem tartozna, mert nem enged magából meghivni a saját osztályaihoz tartozó fv-eket. Szegment hibával kiakad. Mindezt a friend segítségével próbáltam megvalósítani.

Csak pointerként hagyta elérni a fv-eket:

mainformbase->readItemName(const QString &item);

Én úgy látom jobban célt talált volna ez:

MainFormBase::readItemName(const QString &item);

de ezt nem engedte. Azt mondta, hogy nem tudja meghivni a fv-t objektum nélkül.

UI.: hogy csinálsz a hozzászólásaidban különbözö karakter formázó dolgokat?

"A program is never done! by atman"

"A QPtrList müködik..."
Persze, nem is azért mondtam, jelen pillanatban neked még talán logikus is lehet. Egyébként is azt mondják az okosok, hogy az ilyen explicit tipuskonverziót kerülni kell, mert a sok konverzió a rossz tervezés velejárója.

"Te miből tanultad a c++ -t. Valahogy én is úgy érzem, hogy egész mélyen meg kell értenem a működését ahhoz, hogy egyéni ötletek jussanak az eszembe a menet közben felmerelü problémák kezeléséhez."
Így van, a C++ ilyen, ezért számít nehéz nyelvnek. Csak akkor tudod igazán hatékonyan (vagy egyáltalán) használni, ha az egész nyelvet átlátod (és lehetőleg a libeket is).
(Érdekes, hogy ugyanezért nehéz rá fordítót írni (mondják az okosok), mert ha csak egy pár nyelvi elem nincs megírva, a többi sem működik rendesen, így nem nagyon lehet szépen lépésről lépésre haladni.)

Az elején valami Computerbooks-os csodával kezdtem, de csak az alapokra volt használható. A C++ könyv: "Bjarne Stroustrup: A C++ programozási nyelv". Ebben minden benne van, annyira részletesen, hogy nem csak az lesz világos, hogy valami hogyan működik, hanem az is, hogy miért pont úgy (a szerző az egyik tervezője a nyelvnek).
Mondjuk 2 kötet, 1300 oldal, és 9000 Ft. Ha komolyan gondolod a C++-t akkor kötelező, egyébként meg lehet, hogy más könyv is elég neked.

"UI.: hogy csinálsz a hozzászólásaidban különbözö karakter formázó dolgokat?"
A hozzászólás szerkesztése közben a "Beküldés" gomb felett van egy "BBCode jelölők" link, ott le van írva. Amit én használok az a "code" tag, így megmarad a formázás, a kisebb-nagyobb jel, stb.

"a friend működését sem értem még"
A friend semmi extra. Alapesetben egy osztály protected és private tagjait nem lehet máshonnan elérni. Ha megadsz egy fv-t vagy egy osztályt az osztály barátjaként, onnantól az eléri az osztály összes tagját (fv-eket és adatokat is).

"Mint ahogy azt már tudod a MainFormBase-m szülő osztálya a MainForm"
Fordítva, a MainForm szülője a MainFormBase, azaz a MainForm gyeremeke a MainFormBase-nek. (Feltéve, hogy a MainFormBase a designeres osztályod.)

"Nem tudtam tegnap semmilyen modón megoldani, hogy a fent említett osztály el tudja küldeni a MainFormBase-nak hogy melyik elemén történ dupla kattintás."
Qt: signal-slot

Ha jól értem valami ilyesmi kell:

// mainform.h
class MainForm : public MainFormBase
{
...
public slots:
    void dClickedItemName(const QString& name);
...

};

// mainform.cpp
// valahol ahol kell:
    MotorTabWidget* m=new MotorTabWidget();
    connect(m,SIGNAL( dClickedItemName(const QString&) ), this, SLOT( dClickedItemName(const QString&) ));
//persze hozzáadod a tabot ahogy eddig.


// tabwidget.h
class TabWidget : public QWidget
{
...
signals:
    dClickedItemName(const QString&);
};

// motortabwidget.h

class MotorTabWidget : public TabWidget
{
...

// motortabwidget.cpp
// ahol a duplakattot kezeled:
    emit dClickedItemName("myname");

Valami ilyesmi.
A külön TabWidget osztály nem feltétlen kell, ha nincs, akkor az összes *TabWidget osztályba vegyél fel dclicked signalt, illetve ahol használod.
A lényeg, hogy a signalokat összekösd (connect) a mainform slotjával.
Mind a slotot mind a signalt létrehozhatod a designerben is (MainFormBase-nek a slotot, MotorTabWidgetBase-nek a signált (már ha van ilyened)).

"Sikerült ugyan meghívni a MainFormBase egy fv-ét és látszólag az adat is átmegy, de olyan mintha a függvény ezután senkihez sem tartozna, mert nem enged magából meghivni a saját osztályaihoz tartozó fv-eket. Szegment hibával kiakad. Mindezt a friend segítségével próbáltam megvalósítani."

Nagyon kevered a dolgokat.

Tehát van az osztály, ez csak egy absztrakt leírása az objektumoknak. Az osztálynak nincsenek fv-ei, csak leírja, hogy az objektumnak milyen függvényei vannak. (Ez így pongyola, meg hülyeség de talán segít megérteni).

Lényeg, hogy te az objektum fv-eit tudod meghívni.
Valójában persze nem jön létre a fv kódja minden objektumhoz, minden fv egyszer jön létre a memóriában, mikor meghívod egy objektum egy fv-ét, akkor az megkapja az objektum címét (ez a "this" mutató).
Amikor egy fv-ben hivatkozol egy adattagra (pl "adat=0;", vagy egy tagfv-re ("f();"), az valójában egy "this->adat=0;" és "this->f();" hívás.

Néha persze van olyan fv ami logikailag az osztályhoz tartozik, de nincs szüksége objektumra. Pl QTime::currentTime();
Nyilván a QTime osztály része, de minek kéne neked QTime objektum ahhoz, hogy lekérdezd az időt.
Ezt hívjuk statikus tagfv-nek.

class QTime
{
...
static QTime currentTime();
...

Ilyenkor a fv meghívható objektum nélkül is (de pl működne a time->currentTime() is).
De ilyenkor nincs "this" mutató, így nem érheted el a nem statikus adattagokat illetve fv-eket.
Mert persze csinálhatsz statikus adattagot (static kulcsszó), de az ilyen adattagból egy van, és ezen osztozkodik az összes objektum.

Remélem már világos.

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o

"Mert persze csinálhatsz statikus adattagot (static kulcsszó), de az ilyen adattagból egy van, és ezen osztozkodik az összes objektum.

Remélem már világos."

Persze hogy, világos :), bár még sok sötét folt van előttem. Olyan ez mint az idegennyelv tanulás: az elején unalmas, gyötrelmes... de ahogy egyre jobban megérti az ember, egyre érdekesebbé válik. Érdekesebbé, de ugyan úgy gyötrelmes az előbre jutás...

Visszatérve: Ez a módszer tökéletesen alkalmas olyan Objektumok fv-nyeinek elérésére amelyekből csak egy van a program futása során Pl.: MainFormBase.

Nem tudtam eddig, hogy mire jó a static, de egy példán keresztül könnyeb felfogni, mint egy könyből olvasva.

" "Mint ahogy azt már tudod a MainFormBase-m szülő osztálya a MainForm"
Fordítva, a MainForm szülője a MainFormBase, azaz a MainForm gyeremeke a MainFormBase-nek. (Feltéve, hogy a MainFormBase a designeres osztályod.)"

Hát igen nekem a MainForm a MainFormBase szülőosztálya és ezen már semmi kedvem módosítani. Kb 20-30 fájlból áll már a projectem.

Most keveset vagyok internet közelben, mert elszigetelődve hajnalokig irom a progimat. Köszi minden segítséget. Mint igértem a forrást közre fogom adni. Ha másért nem, akkor azért, hogy igy senki ne csinálja. :-)

"A program is never done! by atman"

"Visszatérve: Ez a módszer tökéletesen alkalmas olyan Objektumok fv-nyeinek elérésére amelyekből csak egy van a program futása során Pl.: MainFormBase."

Alkalmas rá, de nem tökéletes. Sőt egyenesen csúnya. Mi a gond a signal-slot-tal?

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o