Scallierung von QtWidgets

Python und das Qt-Toolkit, erstellen von GUIs mittels des Qt-Designers.
Nobuddy
User
Beiträge: 994
Registriert: Montag 30. Januar 2012, 16:38

Hallo zusammen,

möchte mich in PyQt einarbeiten. Dazu habe ich im Designer ein Dialog-Fenster erstellt. Anschliessend habe ich die ui-Datei in eine py-Datei umgewandelt.

Code: Alles auswählen

# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'untitled.ui'
#
# Created: Thu Jun 22 18:51:51 2017
#      by: PyQt5 UI code generator 5.2.1
#
# WARNING! All changes made in this file will be lost!

from PyQt5 import QtCore, QtGui, QtWidgets

class Ui_Dialog(object):
    def setupUi(self, Dialog):
        Dialog.setObjectName("Dialog")
        Dialog.resize(266, 116)
        self.buttonBox = QtWidgets.QDialogButtonBox(Dialog)
        self.buttonBox.setGeometry(QtCore.QRect(30, 80, 201, 28))
        self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
        self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
        self.buttonBox.setObjectName("buttonBox")
        self.label = QtWidgets.QLabel(Dialog)
        self.label.setGeometry(QtCore.QRect(30, 20, 208, 22))
        self.label.setScaledContents(True)
        self.label.setObjectName("label")

        self.retranslateUi(Dialog)
        self.buttonBox.accepted.connect(Dialog.accept)
        self.buttonBox.rejected.connect(Dialog.reject)
        QtCore.QMetaObject.connectSlotsByName(Dialog)

    def retranslateUi(self, Dialog):
        _translate = QtCore.QCoreApplication.translate
        Dialog.setWindowTitle(_translate("Dialog", "Dialog"))
        Dialog.setToolTip(_translate("Dialog", "<html><head/><body><p><br/></p></body></html>"))
        Dialog.setWhatsThis(_translate("Dialog", "<html><head/><body><p><br/></p></body></html>"))
        self.label.setText(_translate("Dialog", "<html><head/><body><p><span style=\" font-size:14pt;\">Änderung übernehmen?</span></p></body></html>"))


if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    Dialog = QtWidgets.QDialog()
    ui = Ui_Dialog()
    ui.setupUi(Dialog)
    Dialog.show()
    sys.exit(app.exec_())
Was mich interessiert ist, wenn ich den Code überarbeite, dass verschiedener Text mit übergeben werden kann.
Wie sieht es bei längerem Text aus, muss ich die Werte beim Dialog.resize und bei self.label.setGeometry händisch anpassen, oder gibt es da eine Lösung das Ganze automatisch zu Scallieren?

Grüße Nobuddy
BlackJack

@Nobuddy: Wie es aussieht hast Du die Widgets im Designer absolut platziert und keine Layouts verwendet. Das ist also wie `place()` in Tk — sollte man auch in anderen GUI-Rahmenwerken nicht machen.

`pack()` aus Tk entspricht ungefähr dem `QBoxLayout` beziehungsweise den Unterklassen `QHBoxLayout` und `QVBoxLayout`, und `grid()` dem `QGridLayout`. Qt kennt ausserdem ein `QFormLayout` das für die typischen Dialoge mit zwei Spalten — links die Beschreibung, rechts die Eingabeelemente — gedacht ist. Das kann man zwar auch mit einem `QGridLayout` selber machen, aber `QFormLayout` macht das etwas einfacher und passt die Anzeige auch der Plattform etwas an, was beispielsweise die Ausrichtung der Beschreibungen (rechts-/linksbündig) angeht.

Und dann gibt es noch ein paar weitere wie `QButtonGroup` und `QStackLayout`. Und `QSpacerItem` kann auch praktisch/nützlich sein.
Nobuddy
User
Beiträge: 994
Registriert: Montag 30. Januar 2012, 16:38

@BlackJack, Danke für Deine Info!
Kenne mich da noch nicht aus, habe das aus dem Designer 1:1 übernommen.
Können diese Einstellungen auch schon im Designer gemacht werden?
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

@Nobuddy: die Layouts müssen sogar schon im Designer designed werden, weil man nachträglich nicht im erzeugten Python-Code herumschreiben will, weil man eigentlich erst gar keinen Python-Code erzeugen will, sondern die ui-Datei per loadui direkt verwenden will.
Nobuddy
User
Beiträge: 994
Registriert: Montag 30. Januar 2012, 16:38

@Sirius3, das mit der Benutzung von ui-Dateien habe ich auch schon gelesen.
Aber manchmal muss man doch ein paar Dinge im Code ändern und dann brauche ich ja eine py-Datei, oder?

Ich möchte für den Text im Label "Änderungen übernehmen?" auch anderen Text verwenden, wie z.B. "Datei löschen?".
Das geht wohl nicht über den Designer?

Ist es eigentlich möglich eine py_Datei in eine ui_Datei umzuwandeln?
__deets__
User
Beiträge: 14494
Registriert: Mittwoch 14. Oktober 2015, 14:29

Nein, man muss keine Dinge im Code aenderen. Und es gibt auch keinen Weg zurueck von Python zu XML - woher soll der UI-Designer wissen, was er mit deinen beliebigen Aenderungen anzufangen hat?

Der Weg zu erreichen was du moechtest fuehrt ueber die Namen der Objekte. Du vergibst im Designer Namen fuer die Objekte, die du anfassen moechtest. Das vom UI-Loader zurueckgegebene Objekt erlaubt dir dann mit findChildren (oder so aehnlich) zB auf ein Label zuzugreifen, und dort aenderst du halt den Text.
BlackJack

@Nobuddy: An dem generiertem Code muss man nichts ändern. Kann man ja auch gar nicht, zumindest nicht wenn man nicht ab da auf den Designer verzichten möchte, denn wenn man am generierten Code was ändert, wird diese Änderung beim erneuten generieren überschrieben. Das heisst selbst wenn man Code generiert muss und will man daran nichts verändern, weswegen man sich den Zwischenschritt mit dem Code generieren sparen kann.

Der umgekehrte Weg *.py → *.ui ist nicht vorgesehen. So etwas könnte man sich vielleicht programmieren, macht aber nur Sinn wenn man schon ein Programm/Projekt hat, aus dem man die GUI als *.ui-Dateien extrahieren will. Und das würde dann auch nicht den Python-Code umwandeln, denn das wäre schwierig bis unmöglich, sondern man würde dann eher ein Werkzeug schreiben das eine gerade angezeigte GUI als *.ui-Datei speichert.

Du kannst im Code selbstverständlich auch bei einem aus einer *.ui-Datei erstellten Dialog alles ändern. Speziell das was Du da jetzt allerdings anscheinend vorhast ist eher nichts für einen Dialog den man mit dem Designer erstellt. Ein optionales Icon, einen Text, optional eine Beschreibung, und Standardschaltflächen ist ein Fall für `QMessageBox`. Dein Beispiel (ungetestet):

Code: Alles auswählen

        result = QMessageBox.question(
            parent_widget,
            'Dialog',
            u'Änderung übernehmen?',
            QMessageBox.Cancel | QMessageBox.Ok
        )
        if result == QMessageBox.Ok:
            do_something()
Das hätte jetzt noch das Icon für Fragen, was in Deinem Dialog nicht vorhanden ist. Wenn man die `QMessageBox` ohne Icon haben möchte, muss man sie selbst erstellen, statt eine der statischen Methoden zu verwenden.

Auch für ganz einfache Eingaben von *einem* Wert gibt es schon etwas fertiges: `QInputDialog`.
Nobuddy
User
Beiträge: 994
Registriert: Montag 30. Januar 2012, 16:38

Hallo __deets__ und BlackJack ,

Danke für Eure Ausführung, das Ganze wird mir jetzt schon etwas klarer.
Wenn also alles mit dem Designer geht, muss ich mit diesem befassen, da habe ich noch erhebliche Schwierigkeiten.... :wink:

Grüße Nobuddy
Nobuddy
User
Beiträge: 994
Registriert: Montag 30. Januar 2012, 16:38

@BlackJack, ich habe doch noch eine Bitte!
Es wäre für mich hilfreich, wenn Du mir mit dem Designer eine ui-Datei erstellen könntest, die folgendes beinhaltet:
- QFormLayout
- QMessageBox.question

So könnte ich dann über den Designer, das Ganze nachvollziehen.

Wäre dies möglich?

Grüße Nobuddy
BlackJack

@Nobuddy: Also ein Dialog mit einem `QFormLayout` könnte so aussehen:
[codebox=xml file=Unbenannt.xml]<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Dialog</class>
<widget class="QDialog" name="Dialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>184</width>
<height>87</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Vorname</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="given_name_edit"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Nachname</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="surname_edit"/>
</item>
<item row="2" column="0" colspan="2">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>Dialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>Dialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>[/code]

`QMessageBox.question()` ist eine Methode. Ich weiss nicht wie die im Designer aussehen sollte. Die ruft man auf um einen einfachen Benachrichtigungsdialoge anzuzeigen und den Aufruf habe ich ja schon in einem Beitrag gezeigt. So einen Dialog im Designer zu erzeugen rechtfertigt den Aufwand doch auch gar nicht. Zumal die `QMessageBox` je nach Plattform und vielleicht auch Einstellungen noch mehr macht. Zum Beispiel Töne abspielen oder über die Taskleiste auf sich aufmerksam machen. Zumindest früher war es unter Windows zumindest früher so das Fehlerdialoge, also `QMessageBox.critical()` und `QMessagBox.warning()`, auch einen Warnton ausgegeben haben. Bei `QMessageBox.about()` entscheidet die Plattform ob der Modal angezeigt wird oder nicht.
Nobuddy
User
Beiträge: 994
Registriert: Montag 30. Januar 2012, 16:38

@BlackJack, Danke für die ui_Datei und Deine weitere Ausführung.
Jetzt kann ich im Designer, mir das anschauen und nachbauen. :wink:

PS:
Konnte es im Designer nachbilden und somit einen kleinen Schritt weiter. :D
Nobuddy
User
Beiträge: 994
Registriert: Montag 30. Januar 2012, 16:38

Taste mich langsam heran, mit kleinen Erfolgen. :)

Code: Alles auswählen

from PyQt5 import uic, QtWidgets
import sys

class Ui(QtWidgets.QDialog):
    def __init__(self, dialog):
        super(Ui, self).__init__()
        uic.loadUi(dialog, self)
        self.show()

if __name__ == '__main__':
    dialog = 'untitled.ui'
    app = QtWidgets.QApplication(sys.argv)
    window = Ui(dialog)
    # Änderung Bezeichnung label_0
    window.label_0.setText("Lieferant")
    # Füge Text in QLineEdit ein
    window.line_edit_0.setText("aaaaaaaaaaaaaaaaa sssssssssssssssssssssss ddd")
    result = window.exec_()
    text = window.line_edit_0.text()
    print('## 00', result, text)
Bei der Ausgabe, wird nicht der ganze Text angezeigt.
Bei der Einstellung sizePolicy > Horizontale Einstellung, habe ich Expanding ausgewählt, leider ohne Änderung.
Hier noch kurz ein Snapshot dazu Bild

Woran liegt es, dass der Text nicht komplett angezeigt wird?

Grüße Nobuddy
BlackJack

@Nobuddy: Das liegt daran das der Text da nicht komplett rein passt in die Anzeige. Das ist doch bei Tk genau so — `Entry`\s werden nicht grösser wenn man mehr Text rein schreibt als in die angezeigte Breite passt. Das ist in jedem GUI-Rahmenwerk so das ich kenne.
Nobuddy
User
Beiträge: 994
Registriert: Montag 30. Januar 2012, 16:38

@BlackJack, das ist schon klar, nur nach den Einstellungen in QLineEdit, sollte es eigentlich funktionieren.

Einstellungen in QWidget:
- sizePolicy: Horizontale Einstellung = Expanding
- minimumSize: Breite = 200
- maximumSize: Breite = 16777215

Haben diese Einstellungen evtl. keine Auswirkung?
Wenn ja, welche Einstellungen muss ich sonst nehmen?
BlackJack

@Nobuddy: Die haben nicht die Auswirkungen die Du anscheinend vermutest. Das heisst dieses Widget *kann* expandieren wenn der Platz dafür da ist. Nicht das es von selber expandiert wenn da mehr Textinhalt drin ist. Tk verhält sich an der Stelle genau so. Da kann man beim Layout ja auch sagen das sich die Widgets ausbreiten dürfen wenn ihnen der Platz dafür zur Verfügung gestellt wird.
Nobuddy
User
Beiträge: 994
Registriert: Montag 30. Januar 2012, 16:38

Hmm, gibt es denn überhaupt eine Lösung den Layoutmanager zu aktualisieren, oder sonst eine?
BlackJack

@Nobuddy: Ich verstehe ehrlich gesagt nicht was Du da eigentlich möchtest‽ Das sich Eingabefelder in der Grösse dem Inhalt anpassen wäre wie gesagt sehr ungewöhnlich. Die wählt man beim Entwurf so gross das die überwiegende Anzahl der Eingaben passt, beziehungsweise geht man einfach davon aus das nicht alles angezeigt wird wenn mehr eingegeben wird. Kennst Du eine GUI die sich nicht so verhält?
Nobuddy
User
Beiträge: 994
Registriert: Montag 30. Januar 2012, 16:38

@BlackJack, habe mich vielleicht nicht richtig ausgedrückt.

Beim Starten der GUI, möchte ich die Widgets QLineEdit mit Daten aus eine Tabelle füllen. Die Textlänge kann hier sehr unterschiedlich sein.
Daher habe ich gedacht, dass eine Anpassung der GUI möglich sein müsste.
Mit tk funktioniert das bei mir.
BlackJack

@Nobuddy: Was machst Du denn in Tk?
Nobuddy
User
Beiträge: 994
Registriert: Montag 30. Januar 2012, 16:38

Ich habe da ein Modul geschrieben (ScreenCenter), dass die Fenstergrösse ermittelt und das Fenster im Bildschirm zentriert.

Code: Alles auswählen

        # Ermittlung der Fenstergröße, Zentrierung des Fensters auf dem Bildschirm
        x, y, hdw, hdh = ScreenCenter(self.screenx, self.screeny,
            self.xpos, self.ypos, self.button_counter).screen_center()
        self.my_win.geometry('{}x{}+{}+{}'.format(x, y, hdw, hdh))
        self.my_win.update()
Antworten