Mit Funktion erstelltes `QDialog`-Objekt wird nicht angezeigt.

Python und das Qt-Toolkit, erstellen von GUIs mittels des Qt-Designers.
Antworten
Atalanttore
User
Beiträge: 341
Registriert: Freitag 6. August 2010, 17:03

Sonntag 28. April 2019, 20:06

Hallo

Ich habe als Beispiel eine Funktion erstellt, die prüft, ob Daten vorhanden sind. Falls ja, sollen die Daten in einem `QDialog` ausgegeben werden. Leider wird kein Dialog angezeigt, obwohl ein `QDialog`-Objekt erstellt wird.

Nur wenn das `QDialog`-Objekt in der Funktion `main()` erstellt wurde, wird es auch angezeigt. Warum ist das so?

Code:

Code: Alles auswählen

#!/usr/bin/python3
import sys
from PyQt5.QtWidgets import QApplication, QDialog, QListWidget


class DataDialog(QDialog):
    def __init__(self, data, parent=None):
        super().__init__()
        self.list_widget = QListWidget(self)
        self.list_widget.addItems(data)
        self.list_widget.show()


def check_data(data=None):
    if data:
        print(data)
        data_dialog = DataDialog(data)
        data_dialog.show()
        print(data_dialog)
        

def main():
    app = QApplication(sys.argv)
    check_data(["a", "b", "c"])

    dialog = DataDialog(["In", "Funktion", "main()", "erstellter", "Dialog"])
    dialog.show()

    sys.exit(app.exec_())


if __name__ == "__main__":
    main()
Gruß
Atalanttore
__deets__
User
Beiträge: 6206
Registriert: Mittwoch 14. Oktober 2015, 14:29

Sonntag 28. April 2019, 21:54

Dir fehlt der Aufruf von QDialog.exec_. Wie in der Dokumentation beschrieben.
__deets__
User
Beiträge: 6206
Registriert: Mittwoch 14. Oktober 2015, 14:29

Sonntag 28. April 2019, 22:34

Oh, sehe gerade den anderen Grund: du erhältst keine Referenz auf das Objekt. Es wird also garbage collected.
Atalanttore
User
Beiträge: 341
Registriert: Freitag 6. August 2010, 17:03

Montag 29. April 2019, 19:38

@__deets__: Danke. Wieder mal der Garbage Collector.

Ich habe die Funktion `check_data()` nun so angepasst, dass sie das in ihr erstellte `DataDialog`-Objekt zurückgibt. Eine Referenz auf das zurückgegebene Objekt wird dann in der `main()`-Funktion erstellt. Damit funktioniert der Code nun so wie gewünscht.

Beim Aufruf der `exec_`-Methode des `QDialog` konnte ich beim Verhalten aber keinen Unterschied feststellen. Es funktioniert sowohl mit als auch ohne. Was hast du damit genau gemeint?

Code:

Code: Alles auswählen

#!/usr/bin/python3
import sys
from PyQt5.QtWidgets import QApplication, QDialog, QListWidget


class DataDialog(QDialog):
    def __init__(self, data, parent=None):
        super().__init__()
        self.list_widget = QListWidget(self)
        self.list_widget.addItems(data)
        self.list_widget.show()


def check_data(data=None):
    if data:
        print(data)
        data_dialog = DataDialog(data)
        data_dialog.show()
        data_dialog.exec_()  # Funktioniert auch ohne
        return data_dialog


def main():
    app = QApplication(sys.argv)
    data_dialog = check_data(["a", "b", "c"])

    # dialog = DataDialog(["In", "Funktion", "main()", "erstellter", "Dialog"])
    # dialog.show()

    sys.exit(app.exec_())


if __name__ == "__main__":
    main()
Gruß
Atalanttore
__deets__
User
Beiträge: 6206
Registriert: Mittwoch 14. Oktober 2015, 14:29

Montag 29. April 2019, 22:00

Das der Dialog angezeigt wird in dem Moment. Wo,it man ihn nicht zurück geben muss. Was übrigens auch besser wäre, denn akut ist das Design Murks. In der Funktion GUI Elemente erzeugen, und das auch noch konditionell - das ist kompliziert, vermischt Datenverarbeitung mit Darstellung, und erfordert zu viel wissen auf der aufrufenden Seite.
Atalanttore
User
Beiträge: 341
Registriert: Freitag 6. August 2010, 17:03

Dienstag 30. April 2019, 15:28

@__deets__: Ich habe nun Darstellung und Auswertung der Daten getrennt. Ein `DataDialog()`-Objekt wird direkt beim Start erzeugt, aber erst dann angezeigt, wenn die Auswertung der Daten erfolgreich war.

Ist der Code damit brauchbar?

Code:

Code: Alles auswählen

#!/usr/bin/python3
import sys
from PyQt5.QtWidgets import QApplication, QDialog, QListWidget


measurement_values = [1.4, 4.7, 7.9]


class DataDialog(QDialog):
    def __init__(self, parent=None):
        super().__init__()
        self.list_widget = QListWidget(self)
        self.list_widget.show()

    def add_items(self, values_for_widget):
        values = ["{:.2f}".format(x) for x in values_for_widget]
        self.list_widget.addItems(values)
        print(values_for_widget)


def check_data(data_to_check=None):
    if data_to_check:
        # Code zur Datenauswertung.

        # Wenn Daten wie gewünscht, dann
        return True


def main():
    app = QApplication(sys.argv)
    data_dialog = DataDialog()

    if check_data(measurement_values):
        data_dialog.add_items(measurement_values)
        data_dialog.show()

    sys.exit(app.exec_())


if __name__ == "__main__":
    main()
Gruß
Atalanttore
__deets__
User
Beiträge: 6206
Registriert: Mittwoch 14. Oktober 2015, 14:29

Dienstag 30. April 2019, 15:43

Besser, allerdings verstehe ich nicht, warum du dann nicht auch den data_dialog im if erzeugst. Du beziehst dich ja ansonsten nirgendwo darauf.
Atalanttore
User
Beiträge: 341
Registriert: Freitag 6. August 2010, 17:03

Mittwoch 1. Mai 2019, 15:08

@__deets__: Der `data_dialog` wird nun in der if-Bedingung erzeugt.

Gibt es noch weitere Punkte, die man verbessern könnte, damit das Design richtig gut wird?

Gruß
Atalanttore
__deets__
User
Beiträge: 6206
Registriert: Mittwoch 14. Oktober 2015, 14:29

Mittwoch 1. Mai 2019, 17:28

@Attalantore: nein, er wird immer erzeugt. Er wird dann nur bedingt angezeigt.

Code: Alles auswählen

   data_dialog = DataDialog()
 
    if check_data(measurement_values):

Ich persoenlich erbe auch selten bis nie von GUI-Klassen. Sehe ich auch hier keinen wirklichen Grund zu. Allerdings ist bei einem solchen Beispiel, das ja nicht wirklich etwas sinnvolles macht, auch nur so viel zu verbessern.
Atalanttore
User
Beiträge: 341
Registriert: Freitag 6. August 2010, 17:03

Mittwoch 1. Mai 2019, 18:26

Mit der vorherigen Änderung habe ich die Erzeugung des `data_dialog`-Objekts in die if-Bedingung verschoben. Wenn die if-Bedingung nun nicht mehr erfüllt wird, wird zumindest bei mir keine Referenz auf das `data_dialog`-Objekt mehr erzeugt.
Wird das `data_dialog`-Objekt trotzdem bereits beim Programmstart erzeugt und später nur noch die Referenz darauf erstellt?

Code:

Code: Alles auswählen

#!/usr/bin/python3
import sys
from PyQt5.QtWidgets import QApplication, QDialog, QListWidget


measurement_values = None


class DataDialog(QDialog):
    def __init__(self, parent=None):
        super().__init__()
        self.list_widget = QListWidget(self)
        self.list_widget.show()

    def add_items(self, values_for_widget):
        values = ["{:.2f}".format(x) for x in values_for_widget]
        self.list_widget.addItems(values)
        print(values_for_widget)


def check_data(data_to_check=None):
    if data_to_check:
        # Code zur Datenauswertung.

        # Wenn Daten wie gewünscht, dann
        return True


def main():
    app = QApplication(sys.argv)

    if check_data(measurement_values):
        data_dialog = DataDialog()
        data_dialog.add_items(measurement_values)
        data_dialog.show()

    print(type(data_dialog))  # Fehler

    sys.exit(app.exec_())


if __name__ == "__main__":
    main()
Gruß
Atalanttore
__deets__
User
Beiträge: 6206
Registriert: Mittwoch 14. Oktober 2015, 14:29

Mittwoch 1. Mai 2019, 18:48

Ich verstehe deine Frage nicht. Das der Code einen Fehler wirft ist klar, weil data_dialog nunmal (wie gewünscht) in dem if Block erzeugt wird. Wie willst du das dann OHNE diesen if Block betreten you haben ausgeben?

Und magisch werden keine Objekte vorher erzeugt. Sondern dann, wenn du es programmierst.
Atalanttore
User
Beiträge: 341
Registriert: Freitag 6. August 2010, 17:03

Mittwoch 1. Mai 2019, 19:21

__deets__ hat geschrieben:
Mittwoch 1. Mai 2019, 17:28
@Attalantore: nein, er wird immer erzeugt. Er wird dann nur bedingt angezeigt.
Diese Aussage hat mich etwas verwirrt, aber in deinem letzten Beitrag hast du es jetzt klargestellt. Danke.


Um die Schwierigkeit im Übungsbeispiel zu steigern, würde ich nun gerne den im Hauptfenster eingegebenen Text von einer Funktion auswerten lassen und bei erfolgreicher Auswertung einen Dialog mit dem Ergebnis anzeigen lassen. Dazu habe ich den Code entsprechend angepasst, aber bei der Verknüpfung des Rückgabewertes der Funktion `check_data()` mit der if-Bedinung in der `main()`-Funktion komme ich nicht mehr weiter.
Ich könnte zwar den Code zur Erzeugung des Dialogs in die Funktion `check_data()` integrieren, aber dann wäre Datenverarbeitung und Darstellung nicht mehr getrennt. Wie macht man das am besten?


Einzelne Schritte (zumindest habe ich es mir so gedacht):
  1. Text in Textfeld des `main_window`-Objekts eingeben
  2. Nach Eingabe auf "Absenden" im `main_window`-Objekt klicken
  3. Eingabe in Funktion `check_data()` auswerten, ob Daten wie gewünscht sind
  4. Falls ja, Eingabe in `data_dialog`-Objekt ausgeben

main.py

Code: Alles auswählen

#!/usr/bin/python3
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QDialog, QListWidget
from PyQt5.uic import loadUi
from PyQt5.QtCore import pyqtSignal


class MainWindow(QMainWindow):
    entered_text = pyqtSignal(str)

    def __init__(self, parent=None):
        super().__init__(parent)
        loadUi("mainwindow.ui", self)
        self.show()

    def submit_text(self):
        self.entered_text.emit(self.textEdit.toPlainText())


class DataDialog(QDialog):
    def __init__(self, parent=None):
        super().__init__()
        self.list_widget = QListWidget(self)
        self.list_widget.show()

    def add_items(self, items):
        self.list_widget.addItems(items)


def check_data(data_to_check=None):
    print(data_to_check)
    if data_to_check:
        # Code zur Datenauswertung.

        # Wenn Daten wie gewünscht, dann
        return True


def main():
    app = QApplication(sys.argv)
    main_window = MainWindow()

    main_window.pushButton_submit.clicked.connect(main_window.submit_text)
    main_window.entered_text.connect(lambda data=main_window.entered_text: check_data(data))

    if check_data():
        data_dialog = DataDialog()
        data_dialog.add_items(main_window.entered_text)
        data_dialog.show()

    sys.exit(app.exec_())


if __name__ == "__main__":
    main()

mainwindow.ui

Code: Alles auswählen

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>MainWindow</class>
 <widget class="QMainWindow" name="MainWindow">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>400</width>
    <height>300</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>MainWindow</string>
  </property>
  <widget class="QWidget" name="centralwidget">
   <layout class="QGridLayout" name="gridLayout">
    <item row="0" column="0">
     <widget class="QTextEdit" name="textEdit"/>
    </item>
    <item row="1" column="0">
     <widget class="QPushButton" name="pushButton_submit">
      <property name="text">
       <string>Absenden</string>
      </property>
     </widget>
    </item>
   </layout>
  </widget>
  <widget class="QMenuBar" name="menubar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>400</width>
     <height>28</height>
    </rect>
   </property>
  </widget>
  <widget class="QStatusBar" name="statusbar"/>
 </widget>
 <resources/>
 <connections/>
</ui>
Gruß
Atalanttore
Antworten