Seite 1 von 1

In einer *.py Datei auf eine *.ui Datei zugreifen

Verfasst: Sonntag 28. Juni 2020, 16:12
von Saturn89
Hallo,

ich mache gerade erste Versuche mit dem Qt-Creator. Mit dem Designer habe ich ein Fenster mit verschiedenen WIdgets erstellt. Habe auch jedem Widgets einen "sinnvollen" Namen gegeben.
Qt-Creator erstellt daraus eine *.ui Datei und hat mir auch eine main.py-Datei erstellt.
Wenn ich main.py ausführe wird mein erstelltes Fenset ordentlich angezeigt.

Meine Frage ist, wie ich nun aber in main.py auf die WIdgets zugreifen kann? Ich muss denen ja noch Funktionen und Inhalte hinzufügen.

Eine Combobox heißt zm Beispiel "Auswahlmenü", aber wenn ich darauf zugreifen will mit "self.Auswahlmenü(...)"

Bekomme ich die Fehlermeldung:
AttributeError: 'widgets' object has no attribute 'Auswahlmenü'

Ich komme hier nicht ganz weiter.

Habe auch schon die *.ui in eine *.py Datei umwandeln lassen, aber der Code der dabei rauskommt ist mehr als unübersichtlich. Würde gerne das Layout von der Funktion trennen. Vorallem ist es auch angenehmer bei nachträglichen Änderungen.

Danke schon einmal für eure Zeit.

Viele Grüße

Re: In einer *.py Datei auf eine *.ui Datei zugreifen

Verfasst: Sonntag 28. Juni 2020, 18:16
von __blackjack__
@Saturn89: Wie sieht denn Deine `main.py` aus?

Re: In einer *.py Datei auf eine *.ui Datei zugreifen

Verfasst: Sonntag 28. Juni 2020, 19:21
von Saturn89

Code: Alles auswählen

#! usr/bin/env Python3
# This Python file uses the following encoding: utf-8

import sys
import os
import pandas as pd

from PySide2.QtWidgets import QApplication, QWidget
from PySide2.QtCore import QFile
from PySide2.QtUiTools import QUiLoader


class widgets(QWidget):
    def __init__(self, parent=None):
        super(widgets, self).__init__()
        self.load_ui()

    def load_ui(self):
        loader = QUiLoader()
        path = os.path.join(os.path.dirname(__file__), "form.ui")
        ui_file = QFile(path)
        ui_file.open(QFile.ReadOnly)
        loader.load(ui_file, self)
        ui_file.close()


if __name__ == "__main__":
    app = QApplication([])
    widget = widgets()
    widget.show()
    sys.exit(app.exec_())
So sieht der aus, der wurde von Qt-Creator erstellt.

Grüße

Re: In einer *.py Datei auf eine *.ui Datei zugreifen

Verfasst: Sonntag 28. Juni 2020, 19:46
von __blackjack__
@Saturn89: Da fehlt jetzt aber die Zeile die einen Fehler verursacht und ich bin mir ziemlich sicher das der Qt-Designer da keinen Import für `pandas` reingeschrieben hat.

Klassennamen schreibt man in Python in MixedCase, also `Widgets`, allerdings ist der Name nicht wirklich passend, denn es ist ja *ein* Widget. Und dann wäre der Name immer noch *sehr* generisch gewählt.

Ich würde aus `load_ui()` auch keine eigene Methode machen.

Re: In einer *.py Datei auf eine *.ui Datei zugreifen

Verfasst: Sonntag 28. Juni 2020, 21:41
von Saturn89
Oh das mit pandas stimmt natürlich. Das war noch von Versuchen drin .
Die Methode wurde vom Qt-Creator erstell. Danke für den Hinweis mit MixedCase und der Namenswahl. Für den Versuch habe ich mir bei dem Namen nicht viel Mühe gegeben.

Ja die Zeile fehlt, da ich nur geraten habe wie es funktionieren könnte. Ich bin da absolut planlos. Es gehört aber schon in „def load_ui“? Also wenn wir es als Methode lassen. Bin aber an der Variante ohne Methode auch interessiert, vorallem wenn es besser wäre.
Wenn ja wie würde den eine Zeile aufgebaut sein?
Wie wenn ich mit tkinter auf ein Optionsmenü zB zugreife?

Ich hoffe ich drücke mich verständlich aus :D

Danke für dein Interesse und die Hilfe
Grüße

Re: In einer *.py Datei auf eine *.ui Datei zugreifen

Verfasst: Montag 29. Juni 2020, 04:55
von Sirius3
Eigentlich funktioniert es so, wie du es umschreibst. Daher ist es auch so wichtig, genau den Code zu sehen, der nicht funktioniert. Und die genaue Fehlermeldung dazu.

Re: In einer *.py Datei auf eine *.ui Datei zugreifen

Verfasst: Dienstag 30. Juni 2020, 06:45
von Saturn89
Guten Morgen und sorry für die späte Antwort.

Ersteinmal Danke für eure Hilfe, hier habe ich den Code mit meinen Versuchen:

Code: Alles auswählen

#! usr/bin/env Python3
# This Python file uses the following encoding: utf-8
import sys
import os
from PySide2.QtWidgets import QApplication, QWidget
from PySide2.QtCore import QFile
from PySide2.QtUiTools import QUiLoader
class widgets(QWidget):
    def __init__(self, parent=None):
        super(widgets, self).__init__()
        self.load_ui()
    def load_ui(self):
        loader = QUiLoader()
        path = os.path.join(os.path.dirname(__file__), "form.ui")
        ui_file = QFile(path)
        ui_file.open(QFile.ReadOnly)
        loader.load(ui_file, self)
        ui_file.close()
        self.auswahl = list(["test", "test2"])
        self.choicebox_material = QComboBox(
            self, *self.auswahl
        )
if __name__ == "__main__":
    app = QApplication([])
    widget = widgets()
    widget.show()
    sys.exit(app.exec_())
Hier die Fehlermeldung:

Code: Alles auswählen

Traceback (most recent call last):
  File "/home/user/xxx/main.py", line 32, in <module>
    widget = widgets()
  File "/home/user/xxx/main.py", line 15, in __init__
    self.load_ui()
  File "/home/user/xxx/main.py", line 25, in load_ui
    self.choicebox_material = QComboBox(
NameError: name 'QComboBox' is not defined
07:25:54: /usr/bin/python exited with code 1
Mir ist klar, das "QComboBox" hier der Fehler ist, mit tkinter würde ich "tkinter.Optionsmenu" schreiben.
Mir ist aber nicht klar, wie ich es hier ansprechen muss. Die erstellte ComboBox habe ich im Designer "choicebox_material" genannt, wenn ich das an die Stelle von "QComboBox" schreibe bekomme ich die Meldunf "undefined name 'choicebox_material'.

Vielen Dank und freundliche Grüße

Edit: Die Verbesserung mit MixedCase werde ich bei meinem richitgen Code dann auf jeden Fall anwenden.

Re: In einer *.py Datei auf eine *.ui Datei zugreifen

Verfasst: Dienstag 30. Juni 2020, 07:16
von Sirius3
Würdest Du tatsächlich eine neue QComboBox erstellen wollen, müßtest Du sie aus QWidgets importieren, aber das willst Du ja gar nicht.
Du willst die vorhandene choicebox_material mit Daten füllen.
`super` benutzt man ohne Parameter, dafür fehlt der Parameter beim __init__-Aufruf. Dem Dateinamen definiert man am besten als Konstanten am Anfang der Datei. Der list-Aufruf ist bei einer Liste überflüssig.
Das was unter `if __name__...` steht, sollte in eine Funktion `main` wandern.

Code: Alles auswählen

#! usr/bin/env Python3
# This Python file uses the following encoding: utf-8
import sys
import os
from PySide2.QtWidgets import QApplication, QWidget
from PySide2.QtCore import QFile
from PySide2.QtUiTools import QUiLoader

FORM_UI_FILENAME = os.path.join(os.path.dirname(__file__), "form.ui")

class Widgets(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        loader = QUiLoader()
        ui_file = QFile(FORM_UI_FILENAME )
        ui_file.open(QFile.ReadOnly)
        loader.load(ui_file, self)
        ui_file.close()
        self.choicebox_material.addItems(["test", "test2"])

def main():
    app = QApplication([])
    widget = Widgets()
    widget.show()
    sys.exit(app.exec_())

if __name__ == "__main__":
    main()

Re: In einer *.py Datei auf eine *.ui Datei zugreifen

Verfasst: Dienstag 30. Juni 2020, 08:01
von Saturn89
Danke für die schnelle Antwort.

Habe deinen Code gleich gegen meinen ersetzt, allerdings funktioniert das Ganze noch nicht.
Fehlermeldung:

Code: Alles auswählen

Traceback (most recent call last):
  File "/home/user/xxx/main.py", line 29, in <module>
    main()
  File "/home/user/xxx/main.py", line 23, in main
    widget = Widgets()
  File "/home/user/xxx/main.py", line 18, in __init__
    self.choicebox_material.addItems(["test", "test2"])
AttributeError: 'Widgets' object has no attribute 'choicebox_material'
08:46:17: /usr/bin/python exited with code 1
Die "choicebox_material" existiert aber, kann hier leider keine Bilder hochladen, hatte einen Screenshot gemacht.
Da du in deinem Code "Widgets" geschrieben hast, habe ich im "Designer" das "widgets" auch zu "Widgets" geändert. Das brachte aber keine Änderung.

Hast du noch eine Idee, wo ich einen Fehler gemacht haben könnte?

Danke und viele Grüße :)

Re: In einer *.py Datei auf eine *.ui Datei zugreifen

Verfasst: Dienstag 30. Juni 2020, 09:02
von Sirius3
PySide2 verhält sich hier anders als PyQt.
Der UI-Loader erzeugt ein Widget und das kann man nicht verhindern. Man kann also keine eigene Widget-Klasse definieren, sondern kann nur einen Wrapper schreiben.

Code: Alles auswählen

import sys
import os
from PySide2.QtWidgets import QApplication, QWidget
from PySide2.QtCore import QFile
from PySide2.QtUiTools import QUiLoader

FORM_UI_FILENAME = os.path.join(os.path.dirname(__file__), "form.ui")

class Form:
    def __init__(self, parent=None):
        self.widget = QUiLoader().load(FORM_UI_FILENAME, parent)
        self.widget.choicebox_material.addItems(["test", "test2"])

def main():
    app = QApplication([])
    form = Form()
    form.widget.show()
    sys.exit(app.exec_())

if __name__ == "__main__":
    main()

Re: In einer *.py Datei auf eine *.ui Datei zugreifen

Verfasst: Dienstag 30. Juni 2020, 09:38
von Saturn89
Oh das hätte ich ja nie alleine hinbekommen, aber jetzt funktioniert es, vielen Dank dafür ! :)

Allerdings sind mir noch weitere Fragen aufgekommen, hätte gerne das vor der Auswahl eine Bezeichnung in der ComboBox steht, also zum Beispiel "Material auswählen". Das soll dort stehen wenn noch nichts ausgewählt wurde.
Mit tkinter habe ich dafür eine tkinter.StrinkVar definiert und das beim erstellen des Optionsmenü mit in die Klammer geschrieben. Geht das hier auch so ähnlich?

Das gleiche hätte ich auch gerne in Eingabefelder, in der ich eine Texteingabe vom Benutzer erwarte. Allerdings sollte das nicht so sein, dass dort steht "Hier Text eingeben" und dass der Benutzer den Inhalt vor seiner Eingabe erst löschen muss, sondern das mit dem anklicken der vordefinierte Inhalt ("Hier Text eingeben") verschwindet.

Achso und dann noch zur Code-Erstellung: Ich habe mehrere Widgets, definiere ich die alle in "def__init__"? Vorallem muss Python erst noch eine Datei auslesen, aus der die Inhalte für die Widgets später kommen. Würde das gerne übersichtlich aufbauen.

Hoffe ich habe mich verständlich ausgedrückt :D

Grüße

Re: In einer *.py Datei auf eine *.ui Datei zugreifen

Verfasst: Dienstag 30. Juni 2020, 09:59
von Sirius3
Du kannst ja selbst im Designer die Optionen anschauen. QLineEdit kennt placeholderText, QComboBox nicht.

Was Du Widgets nennst, ist ja normalerweise ein Fenster. Wenn Du mehrere Fenster hast, kannst Du die auch alle in einer Klasse erzeugen, oder Du machst mehrere davon, je nachdem wie Deine Abhängigkeiten sind.

Re: In einer *.py Datei auf eine *.ui Datei zugreifen

Verfasst: Dienstag 30. Juni 2020, 10:11
von Saturn89
In den Optionen der QComboBox hatte ich geschaut und dort nichts gefunden, aber laut deiner Aussage ist das dann nicht möglich?

Mit Widgets meinte ich Objekte wie "ComboBox," "Eingabefelder" und soweiter.

Also ich habe eine Fenster, das beinhaltet vier "QComboBoxen" und der Inhalt dafür wird aus einer extra Datei ausgelesen. Gehört das Auslesen und das befüllen der ComboBoxen alles in "def__init__"?
Wenn ich dafür eine extra Methode machen soll, muss ich dann dabei etwas beachten?

Danke und Grüße

Re: In einer *.py Datei auf eine *.ui Datei zugreifen

Verfasst: Dienstag 30. Juni 2020, 10:25
von __blackjack__
@Saturn89: Üblicherweise versucht man die Programmlogik von der GUI zu trennen, also die Programmlogik eigenständig zu haben und die GUI dann da drauf zu setzen. In der Regel in dem man der GUI die Programmlogik als Argument übergibt. Das auslesen der Datei würde dann eher *vor* dem Aufruf der `__init__()` passieren, das befüllen der Komboboxen dann in der `__init__()` weil das ja zur Initialisierung der GUI gehört.

Re: In einer *.py Datei auf eine *.ui Datei zugreifen

Verfasst: Dienstag 30. Juni 2020, 10:33
von Saturn89
Okay vielen Danke für die Information.

Dann werde ich mich mal darum kümmern. :D

Grüße

Re: In einer *.py Datei auf eine *.ui Datei zugreifen

Verfasst: Dienstag 30. Juni 2020, 12:59
von Saturn89
Ich habe nun eine Methode erstellt in welcher ich meine Daten einlese:

Code: Alles auswählen

def ReadIn(self):
        self.material_xlsx = pd.read_excel(DATABASE, sheet_name='Material', index_col='Material')
        self.material = list(self.material_xlsx.index)
def __init__(self, parent=None):
    	self.widget = QUiLoader().load(FORM_UI_FILENAME, parent)
      	self.widget.choicebox_material.addItems(self.material)
Wie bekomme ich denn jetzt

Code: Alles auswählen

self.material
in die Methode

Code: Alles auswählen

def __init__
?

Wenn ich in der Methode

Code: Alles auswählen

def __init__
meine Daten auslesen, dann funktioniert das wunderbar, aber ich sollte ja erst alles einlesen und dann init aufrufen.

Wäre sehr nett, wenn ihr mir da nochmals helfen könnt.
Fehlermeldung:

Code: Alles auswählen

Traceback (most recent call last):
  File "/home/user/xxx/main.py", line 37, in <module>
    main()
  File "/home/user/xxx/main.py", line 31, in main
    form = Form()
  File "/home/user/xxx/main.py", line 25, in __init__
    self.widget.choicebox_material.addItems(self.material)
AttributeError: 'Form' object has no attribute 'material'
Danke schon ein mal.

Grüße

Re: In einer *.py Datei auf eine *.ui Datei zugreifen

Verfasst: Dienstag 30. Juni 2020, 14:29
von __blackjack__
@Saturn89: Ich würde das einlesen ausserhalb der Klasse lösen und der `__init__()` die Daten als Argument(e) mitgeben.

Nach dem abarbeiten von `__init__()` sollten alle Attribute existieren. Weitere Methoden sollten nicht später noch weitere Attribute hinzufügen. Das ist unübersichtlich und fehleranfällig. Nach der `__init__()` sollte sich das Objekt in einem benutzbaren Zustand befinden.

`ReadIn()` ist von von der Schreibweise her weder Python (klein_mit_unterstrichen) noch Qt (camelCase). MixedCase, also mit grossem Anfangsbuchstaben verwenden beide für Klassen.

Re: In einer *.py Datei auf eine *.ui Datei zugreifen

Verfasst: Dienstag 30. Juni 2020, 15:05
von Saturn89
@__blacljack__
Vielen Dank, jetzt funktioniert es :)

Grüße und einen schönen Tag noch

Re: In einer *.py Datei auf eine *.ui Datei zugreifen

Verfasst: Mittwoch 1. Juli 2020, 06:41
von Saturn89
Guten Morgen,

muss mich leider nochmal melden. Das Programm funktioniert soweit. Nun wollte ich unter Windows auf dem Anaconda installiert ist mit Hilfe von PyInstaller eine *.exe -Datei aus erstellen.
Das Programm hat zwar noch keine Funktion, ich muss aber zeigen dass das problemlos unter Windows funktioniert, bevor ich es vollständig umsetzen kann.
Die *.exe- Datei wollte ich so erstellen

Code: Alles auswählen

pyinstaller --onefile --windowed main.py
Dann bekomme ich aber folgende Fehlermeldung:
"maximum recursion depth exceeded"

Was bedeutet dass denn?
Bei einem anderen Programm hat das Erstellen fehlerfrei geklappt.

Hier die ganze "main.py":

Code: Alles auswählen

import sys
import os
from PySide2.QtWidgets import QApplication, QWidget
from PySide2.QtCore import QFile
from PySide2.QtUiTools import QUiLoader
import pandas as pd

# Dateienpfade definieren
FORM_UI_FILENAME = os.path.join(os.path.dirname(__file__), "form.ui")
DATABASE = os.path.join(os.path.dirname(__file__), "Excel.xlsx")
# Datenbank auslesen
producer = list(["Hersteller auswählen", "HerstellerA", "HerstellerB"])
wav_typ_xlsx = pd.read_excel(DATABASE, index_col='Modell')
wav_typ = list(wav_typ_xlsx.index)
rollface_xlsx = pd.read_excel(
    DATABASE, sheet_name='Rahmenbedingungen', index_col='Oberfläche')
rollface = list(rollface_xlsx.index)
material_xlsx = pd.read_excel(
    DATABASE, sheet_name='Material', index_col='Material')
material = list(material_xlsx.index)


class Form:
    # Benutzeroberfläche einlesen und Objekte füllen
    def __init__(self, parent=None):
        self.widget = QUiLoader().load(FORM_UI_FILENAME, parent)
        self.widget.choicebox_material.addItems(material)
        self.widget.choicebox_rollface.addItems(rollface)
        self.widget.choicebox_wavtyp.addItems(wav_typ)
        self.widget.choicebox_producer.addItems(producer)
        self.widget.button_cancel.clicked.connect(self.cancel)
        self.widget.button_math.clicked.connect(self.math)

# Berechnungen definieren
    def math(self):
        pass

# Abbrechen definieren
    def cancel(self):
        sys.exit()


def main():
    app = QApplication([])
    form = Form()
    form.widget.show()
    sys.exit(app.exec_())


if __name__ == "__main__":
    main()
Edit: Die Excel Datei ist im gleichen Verzeichnis wie die "main.py" Datei.

Vielen Dank schon einmal
und Grüße