GUI-Elemente nachträglich hinzufügen

Alles rund um die Programmierung mit Qt
Antworten
Gapa
Beiträge: 63
Registriert: 4. Juli 2008 14:33
Kontaktdaten:

GUI-Elemente nachträglich hinzufügen

Beitrag von Gapa »

Hallo,

Ich habe ein Gui mit einem Index indem sich mehrere Links befinden und einer GroupBox, deren Inhalt sich je nachdem welchen Link man angeklickt hat ändern soll.
Mein Problem ist nun, wie habe ich nachträglich Einfluss auf die GroupBox??
Anfangs wird die GroupBox im Konstruktor des Fensters erstellt und mit einem Element belegt. Wenn man nun auf einen Link im Index klickt, so wird abgefragt welcher Link geklickt wurde und dann sollen abhängig vom jeweiligen Link Elemente in der GroupBox erscheinen.
Hier mein bisheriger Code:

Window.h

Code: Alles auswählen

//MainWindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QObject>
#include <QWidget>
#include <QCloseEvent>
#include <QMessageBox>
#include <QGroupBox>
#include <QLabel>

class MainWindow : public QWidget
{
  Q_OBJECT

  private:
    QGroupBox *gb_index, *gb_details;
    QLabel *link1, *start, *linkclicked;

  public:
    MainWindow();
    ~MainWindow();
    void closeEvent(QCloseEvent *event);
    void DisplayDetails(QString Link);

  public slots:
    void LinkClicked(const QString &Link);
};

#endif
Window.cpp

Code: Alles auswählen

//MainWindow.cpp

#include "MainWindow.h"

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//---Konstruktor---

MainWindow::MainWindow()
{
  gb_index = new QGroupBox("Index", this);
  gb_index->setGeometry(5, 50, 200, 500);

  link1 = new QLabel("<a href=\"index_link1\">Link 1</a>", gb_index);
  link1->setGeometry(10, 15, 100, 30);

  gb_details = new QGroupBox(this);
  gb_details->setGeometry(220, 57, 670, 493);

  start = new QLabel("Dieser Text erscheint beim Programmstart", gb_details);
  start->setGeometry(15, 15, 80, 30);

  
  connect(link1, SIGNAL(linkActivated(const QString&)), this, SLOT(LinkClicked(const QString&)));

  setMinimumWidth(900);
  setMinimumHeight(560);
  setMaximumWidth(900);
  setMaximumHeight(560);
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//---Detruktor---

MainWindow::~MainWindow()
{
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//---closeEvent---

void MainWindow::closeEvent(QCloseEvent *event)
{
  QMessageBox *msgbox_event_close;
  msgbox_event_close = new QMessageBox(this);
  msgbox_event_close->setText("Sind sie sicher, dass sie das Programm beenden wollen?");
  msgbox_event_close->addButton("Beenden", QMessageBox::YesRole);
  msgbox_event_close->addButton("Abbrechen", QMessageBox::NoRole);
  if(msgbox_event_close->exec() == 1)
   event->ignore();
 return;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//---LinkClicked---

void MainWindow::LinkClicked(const QString &Link)
{
  if(Link.left(5) == "index")
   this->DisplayDetails(Link);
 return;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//---DisplayDetails---

void MainWindow::DisplayDetails(QString Link)
{
  if(!Link.compare("index_link1"))
  {
    linkclicked = new QLabel("Dieser Text erscheint beim Klicken auf den Link Nr. 1", gb_details);
    linkclicked->setGeometry(15,15,80,30);
    this->updateGeometry();
  }
}
Doch leider funktioniert das Ganze so einfach nicht...
Kann mir da jemand weiterhelfen??

Viele Grüße
Gapa
Zuletzt geändert von Gapa am 8. August 2009 14:44, insgesamt 1-mal geändert.
Gestern war heute noch morgen!
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Beitrag von franzf »

1) verwende ein Layout, welches du der groupBox zuweist.
2) verwende ein QListWidget/View anstatt von links.
3) verwende nur EIN Label statt 2 (start und linkclicked), welches in DisplayDetails() per QLabel::setText() seinen Text geändert bekommt. Neben dem dass das ein schönes MemLeak ist, wenn du ständig mit new neue QLabels instantiierst, die alten aber nie zerstörst (delete).

Dann ist dein Problem auch schon gelöst. Denn linkclicked liegt unter start, und kann deshalb auch nie gesehen werden...
Gapa
Beiträge: 63
Registriert: 4. Juli 2008 14:33
Kontaktdaten:

...

Beitrag von Gapa »

Hallo,

vielen Dank für die Antwort!
Ich hab mir schon sowas in der Art gedacht.
So einfach ist das aber leider nicht, denn meine verschiedenen "Layouts" bestehen leider nicht nur aus jeweils einem Label bzw. überhaupt den gleichen Elementen. Da gibt es dann Links bei denen sollen dann 2 Editboxen und ein paar Buttons auftauchen und dann gibts wieder welche da sollen nur ein paar Text-Labels zu sehen sein... Wie stelle ich das dann an?? Nach deinem Tipp müsste ich für jedes Layout alle Elemente zur Verfügung stellen und diese dann je nach Layout modifizieren, das Problem ist halt dass ich nicht immer die gleichen Elemente brauche...

Hoffe auf Hilfe!
Neben dem dass das ein schönes MemLeak ist, wenn du ständig mit new neue QLabels instantiierst, die alten aber nie zerstörst (delete).
Nää die hätt ich schon wieder ausm Speicher geholt hab das nur nich in mein Code mitreingepostet ;) ... deshalb frag ich mich ob meine Variante prinzipiell nicht doch sinnvoll ist... ist nur ne Überlegung weil ich einfach nicht weiß wie ich es besser machen soll - jedoch klappt die Variante ja so nicht also... HILFE!!

Ach und danke für den Tipp mit dem QListWidget!! Kann ich gut gebrauchen! :)

Viele Grüße
Gapa
Gestern war heute noch morgen!
RHBaum
Beiträge: 1436
Registriert: 17. Juni 2005 09:58

Beitrag von RHBaum »

Neben dem dass das ein schönes MemLeak ist, wenn du ständig mit new neue QLabels instantiierst, die alten aber nie zerstörst (delete).
Falsch ! Es ist kein Memleak, sondern nur sinnlos verheizter speicherplatz. Das nen unterschied !

wie wir alle wissen, werden alle QObjecte(inklusive der von abgeleiteten) automatisch zerstoert, wenn Ihr Parent zerstoert wird, natuerlich nur sofern sie mit der parent beziehung erzeugt (beim konstruktor das parent angegeben wurden) oder nachtraeglich in einen Container eingefuegt wurden, der die parent-beziehung uebernimmt.

Memory Leak bedeutet -> du erzeugst dyn. objecte, und loeschst sie nicht wieder im Ablauf des Programmes, bzw. bist ned in der Lage zu ...

Trotzdem iss es natuerlich besser, nicht benoetigte Objecte zu loeschen, statt sie stundenlang unbenutzt im speicher rumdümpeln zu lassen ^^

Ciao ...
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Re: ...

Beitrag von franzf »

Gapa hat geschrieben:So einfach ist das aber leider nicht, denn meine verschiedenen "Layouts" bestehen leider nicht nur aus jeweils einem Label bzw. überhaupt den gleichen Elementen. Da gibt es dann Links bei denen sollen dann 2 Editboxen und ein paar Buttons auftauchen und dann gibts wieder welche da sollen nur ein paar Text-Labels zu sehen sein... Wie stelle ich das dann an??
Dann denk ein bisschen mehr OOP ;) Wenn die Anforderungen für jeden Link so stark differieren, solltest du für jede Darstellung ein eigenes Widget implementieren. Denn auf Dauer wird dein Code richtig wüst, wenn du für jede Aufgabe in deiner MainWindow-Klasse ständig einzelne Elemente erzeugst, diese irgendwie positionierst, am Ende connections erstellst/entfernst, usw. Wenn das alles in einzelnen Klassen gekapselt ist, wird das Erstellen in deiner MainWindow ein Einzeiler.
Gapa
Beiträge: 63
Registriert: 4. Juli 2008 14:33
Kontaktdaten:

...

Beitrag von Gapa »

Och das sieht dann aber auch nich mehr so toll aus wenn sich bei jedem Link dann ein neues Fenster öffnet...
Ich meine ich kann ja schon die einzelnen Layouts in Klassen packen um den Code zu verkürzen (wahrscheinlich hätte ich das irgendwann sowieso gemacht^^) aber dennoch würde ich gerne diese Layouts im MainWindow anzeigen lassen... irgendwie muss das doch realisierbar sein!
Wenn jemand noch nicht weiß was genau ich mir vorstelle dann soll er sich das (schwarze) "Examples and Demos" - Fenster von Qt4 mal anschauen welches bei der Installation mitgeliefert wird... Da sind auf der linken Seite die Links und beim Klick darauf ändert sich das Layout rechts aber es werden keine neuen Fenster erstellt.

Also wie füge ich nun nachträglich Elemente zu meinem Fenster hinzu? Muss ich die vorab schon in der MainWindow-Klasse deklarieren oder deklarieren und definieren oder was??

Viele Grüße
Gapa
Gestern war heute noch morgen!
RavenIV
Beiträge: 267
Registriert: 21. Januar 2009 14:24
Wohnort: Waldshut

Beitrag von RavenIV »

Dann schau doch einfach, wie es in diesem Example gemacht wird.
Dafür sind sie doch da.
Linux, das längste Text-Adventure aller Zeiten
Gapa
Beiträge: 63
Registriert: 4. Juli 2008 14:33
Kontaktdaten:

...

Beitrag von Gapa »

Nein ich meine doch nicht die Examples ich mein das Hauptfenster selbst... Mein Fenster soll (seeeehr vereinfacht natürlich) so agieren wie das "Examples and Demos" - Fenster von Qt...

Weiß aber bisher immer noch nicht weiter...

Viele Grüße
Gapa
Gestern war heute noch morgen!
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Re: ...

Beitrag von franzf »

Gapa hat geschrieben:Och das sieht dann aber auch nich mehr so toll aus wenn sich bei jedem Link dann ein neues Fenster öffnet...
Wie kommst du darauf?!?
Widget != neues Fenster. Du verwendest das dann so wie dein QLabel jetzt.
Also mit der GroupBox als parent erstellen und relativ positionieren.
Oder gleich ordentlich der groupBox ein Layout zuweisen und immer genau ein Widget in dem Layout behalten. Oder gleich von QGroupBox erben. Oder ... usw...

Und du weißt schon, dass das qtdemo-app auch nur ein Qt-Programm ist, welches mit den Qt-Sourcen geliefert wird?
<qt-src>/demos/qtdemo/
Und DAS sollst du dir anschauen.
Gapa
Beiträge: 63
Registriert: 4. Juli 2008 14:33
Kontaktdaten:

...

Beitrag von Gapa »

Achso einfach ein Widget erstellen ok...
Ja aber das löst mein Problem vom Anfang noch nicht!

Ich habe jetz die GroupBox deren Inhalt immer ein Widget (also ein "Layout") ist... Beim Programmstart erstellt der Konstruktor das komplette Fenster und erzeugt das "Start-Widget" (also das 1. Widget) in der GroupBox.
Wenn man nun auf einen Link klickt dann wird dieses Widget per delete() gelöscht - was auch funktioniert denn das Widget verschwindet aus dem Fenster - und ein neues Widget wird in die GroupBox gesetzt. (diese Widgets haben wie empfohlen natürlich die GroupBox als parent). Doch wenn ich das neue Widget erstellen lasse und das Fenster mittels updateGeometry() aktualisiere so erscheint das Widget nicht - stattdessen immer noch die leere GroupBox...

Wo liegt der Fehler??

Viele Grüße
Gapa
Gestern war heute noch morgen!
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Re: ...

Beitrag von franzf »

Gapa hat geschrieben:Ich habe jetz die GroupBox deren Inhalt immer ein Widget (also ein "Layout") ist...
Immer noch nicht komplett verstanden. Das Layout gehört direkt der groupBox, damit das Widget, das da rein kommt, automatisch in der Größe eingepasset wird.
Ich dachte an sowas:

Code: Alles auswählen

Mainwindow::MainWindow()
 : groupBox(new QGroupBox)
{
    QVBoxLayout* layout = new QVBoxLayout(groupBox);
    StandardWidget* standardWidget = new StandardWidget(groupBox);
    layout->addWidget(standardWidget);
}

void MainWindow::addAnotherWidget()
{
    QLayout* layout = groupBox->layout();
    QLayoutItem* item = layout->itemAt(0);
    layout->removeItem(item);
    delete item;
    AnotherWidget* anotherWidget = new AnotherWidget(groupBox);
    layout->addWidget(anotherWidget);
}
Und das braucht dann auch kein update() mehr.
Gapa
Beiträge: 63
Registriert: 4. Juli 2008 14:33
Kontaktdaten:

...

Beitrag von Gapa »

Ja ich habe dich schon verstanden nur hatte ich bisher versucht eine Lösung ohne QVBoxLayout zu finden..
Mit QVBoxLayout funktionierts da diese Klasse ja extra add/remove-Funktionen anbietet aber es bleibt mir immer noch ein Rätsel warum meine Methode nicht funktioniert...

Wenn es einfach nicht umsetzbar ist Widgets nachträglich der GroupBox hinzuzufügen ohne ein Layout dazwischenzusetzen werde ich mich mit dieser Lösung zufrieden geben müssen, aber sollte es doch irgendwie realisierbar sein dann lasst es mich doch bitte wissen!

@franzf: Aber dennoch vielen Dank für deine Bemühungen und den Code-Fetzen!!

Viele Grüße
Gapa
Gestern war heute noch morgen!
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Beitrag von franzf »

Es geht sicher auch ohne dem Layout, aber dann musst du resizen selber erledigen. Z.B. per eventFilter auf die groupBox dessen resizeEvent abfangen und das eingefügte widget selber resizen. Aber mit der GroupBox ist das nicht sooo ganz einfach. Da hat es ja nen Text (-> title). Und jeder Style malt das anders. von dem her ist ein Layout der einfachste Weg.

Und warum du das Layout unbedingt weglassen willst, versteh ich ehrlich gesagt nicht. Gibts da nen speziellen Grund?
Antworten