Programm mit Sub das sich automatisch schließt

Python und das Qt-Toolkit, erstellen von GUIs mittels des Qt-Designers.
Antworten
Schechner
User
Beiträge: 8
Registriert: Donnerstag 26. Januar 2017, 22:38

Hallo,

ich brauch mal eure Hilfe, ich bin in der GUI-Welt recht neu und deshalb wäre es schön wenn ihr so manches verzeiht :)

Jetzt zu meinem Projekt, ich hab vor eine GUI zu schreiben wenn ich einen Button klicke soll sich ein zweites Fenster öffnen mit dem Hinweis, das sich das Fenster automatisch schließt.
Im Hintergrund soll, sobald das zweite Fenster offen ist ein Background prozess gestartet werden.

Anbei habe ich mal ein stück Code das die Funktion relativ gut macht, jedoch schließt er das Sub-Fenster und öffnet das selbe wieder, normal soll er wieder das Main Fenster zeigen.

Code: Alles auswählen

from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import QThread, pyqtSignal
import sys
import time
from pyqt_ui import Ui_Form_Sub, Ui_Form_Main


class Main_Window(QtWidgets.QMainWindow, Ui_Form_Main):

    switch_window = QtCore.pyqtSignal()

    def __init__(self, parent=None):
        super(QtWidgets.QWidget, self).__init__(parent)
        self.setupUi(self)
        self.pushButton.setText("Git clone with Thread")
        # Here we are telling to call git_clone method when
        # someone clicks on the pushButton.
        self.pushButton.clicked.connect(self.git_clone)
        self.git_thread = CloneThread()  # This is the thread object
        # Connect the signal from the thread to the finished method
        self.git_thread.signal.connect(self.finished)


    def git_clone(self):
        self.switch_window.emit()
        self.pushButton.setEnabled(False)  # Disables the pushButton
        self.textEdit.setText("Started git clone operation.")  # Updates the UI
        self.git_thread.start()  # Finally starts the thread

    def finished(self, run):
        #self.textEdit.setText("Close Popup".format(result))  # Show the output to the user
        self.pushButton.setEnabled(True)  # Enable the pushButton
        ########
        ########
        #######
        #falscher switch
        self.switch_window.emit()

class CloneThread(QThread):

    signal = pyqtSignal('PyQt_PyObject')
    switch_window = QtCore.pyqtSignal()

    def __init__(self):
        QThread.__init__(self)
        #evtl. umbasteln zu treeview
        self.git_url = ""

    # run method gets called when we start the thread
    def run(self):
        #hier wird rfid bearbeitet und wartet auf das ok
        time.sleep(1)
        #reader.write(text)
        # git clone done, now inform the main thread with the output
        self.signal.emit(self)


class RFID_popup(QtWidgets.QWidget, Ui_Form_Sub):

    switch_window = QtCore.pyqtSignal()

    def __init__(self):
        QtWidgets.QWidget.__init__(self)
        self.setupUi(self)


    def pushbutton_handler(self):
        print("123")
        self.switch_window.emit()


class Controller:

    def __init__(self):
        pass

    def show_main_win(self):
        self.login = Main_Window()
        self.login.switch_window.connect(self.show_rfid_popup)
        self.login.show()

    def show_rfid_popup(self):
        self.window = RFID_popup()
        self.window.switch_window.connect(self.show_main_win)
        self.login.close()
        self.window.show()


def main():
    app = QtWidgets.QApplication(sys.argv)
    controller = Controller()
    controller.show_main_win()
    sys.exit(app.exec_())


if __name__ == '__main__':
    main()
das Problem was ich jetzt sehe, der übergibt nicht mehr das Objekt vom Main-Fenster.
Aber vielleicht ist es auch ein ganz was anders.
Hoffe ihr könnt mich, mein Problem verstehen und mir hoffentlich auf meinen Fehler aufmerksam machen.

mfg Schechner
Benutzeravatar
__blackjack__
User
Beiträge: 13100
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Schechner: Was auf jeden Fall schon mal etwas schräg ist, ist das `QWidget` als erstes Argument von `super()` wo die Klasse gar nicht direkt von `QWidget` erbt, also so einiges in der Vererbungshierarchie überprungen werden dürfte. Bei Python 3 braucht man bei `super()` auch überhaupt keine Argumente mehr anzugeben. Dann kann man auch nichts falsches übergeben.

Eine `__init__()` deren Implementierung nur ``pass`` enthält würde man nur machen wenn man explizit den Aufruf einer geerbten `__init__()` durch nichts ersetzen will, was komisch wäre. Aber `Controller` erbt ja von nichts.

In der `__init__()` sollte aber etwas stehen weil in den anderen beiden Methoden sonst neue Attribute eingeführt werden. Nach der `__init__()` sollte ein Objekt aber komplett nutzbar initialisiert sein, das heisst alle Attribute sollten danach existieren. Da ja offenbar immer nur ein Fenster offen sein soll, braucht man dafür dann auch nur ein Attribut. Die beiden Methoden sind ein bischen asymmetrisch. Ein Fenster wird geschlossen, das andere nicht.

Wenn man das dann implementiert sehen die beiden `show_*()`-Methoden nahezu gleich aus und unterscheiden sich nur durch die Klasse mit der das neue Fenster geöffnet wird.

Code: Alles auswählen

class Controller:
    def __init__(self):
        self.window = None
        self.window_classes = cycle([MainWindow, RfidPopup])
    
    def show_next_window(self):
        if self.window:
            self.window.close()
        self.window = next(self.window_classes)()
        self.window.switch_window.connect(self.show_next_window)
        self.window.show()
Bei den anderen Klassen kann ich nicht wirklich nachvollziehen was da wann tatsächlich passieren soll. Ich finde das/die Signal(e) auch komisch. Normalerweise hat man Signale die sagen das etwas innerhalb des Objekts passiert *ist*, diese `switch_window`-Signale scheinen dafür da zu sein das etwas ausserhalb des Objekts passieren *soll*. Das stellt das ganze irgendwie auf den Kopf. Man würde da eher ein `git_clone_started`- und `git_clone_finished`-Signal zur verfügung stellen. Was damit andere Objekte ausserhalb dann tun, zum Beispiel Fenster anzeigen/verstecken ist deren Sache und hat nichts im Signalnamen zu suchen.

Die `finished()`-Methode würde ich nicht so nennen, denn das ist keine Tätigkeit. Das wäre ein passender Name für ein Signal — `QThread` hat ja so eines. Wenn einem keine Tätigkeit für die Methode einfällt und die als Rückruf für ein Ereignis/Signal verwendet wird, nennt man so etwas oft auch `on_<ereignis/signal()`, also hier `on_finished()`.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Schechner
User
Beiträge: 8
Registriert: Donnerstag 26. Januar 2017, 22:38

Vielen Dank für deine Tipps und Hinweise.
Ich werde die Morgen versuchen Umzusetzen.

ich habe im Anhang nochmal eine xlsx Datei um dem Ablauf/funktion zu zeigen,

Hoffe damit kann ich es ein bisschen verständlicher machen.
Ich denke nämlich ich werde nochmal deine/eure Hilfe brauchen :)

Mit freundlichen Grüßen Schechner

https://easyupload.io/2hdbrx
Benutzeravatar
__blackjack__
User
Beiträge: 13100
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Schechner: Hm, da steht ja, dass das schliessen des Hauptfensters optional ist. Ich würde das auch nicht machen. Verschwindende und wieder auftauchende Hauptfenster sind IMHO keine gute UX. Es sieht so aus als wenn einfach nur für die Dauer der Bearbeitung des Problems ein (modaler?) Dialog aufgehen sollte. Das zurück zum Hauptfenster hat man dann ja automatisch wenn das Dialogfenster zu geht.

Eine Exceltabelle die gar keine Daten enthält und auf die ein Ablaufdiagramm gezeichnet ist, habe ich auch noch nicht gesehen. 🙂
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Schechner
User
Beiträge: 8
Registriert: Donnerstag 26. Januar 2017, 22:38

Ok, dann ist für mich Dialog wohl die bessere Wahl.
Eine Exceltabelle die gar keine Daten enthält und auf die ein Ablaufdiagramm gezeichnet ist, habe ich auch noch nicht gesehen. 🙂
ja gut ... immer mal was neues :)

Ich probier mal mein glück beim Dialog, aber ich denke ich werde nochmal auf euch/dich zurück greifen müssen :/

vielen Dank schon mal
Schechner
User
Beiträge: 8
Registriert: Donnerstag 26. Januar 2017, 22:38

Hallo,

ich wollte gerade mal ein Update zu meine Problem bringen.
Ich hab jetzt alles überarbeitet und auf eine dialogbox gesetzt.

Soweit entspricht auch alles meinen Vorstellungen, jedoch kann ich das Fenster nicht schließen nach dem background job. da sagt er mir
AttributeError: 'Ui_Dialog' object has no attribute 'close'
ich denke auch ich weiß warum, jedoch nicht wie ich es beheben kann :)
<PyQt5.QtWidgets.QDialog object at 0x05AA46E8>
<PyQt5.QtWidgets.QDialog object at 0x05AA46E8>
<PyQt5.QtWidgets.QDialog object at 0x05AA46E8>
done
<dialog_test.Ui_Dialog object at 0x05AD2AA8>
Traceback (most recent call last):
File "c:\programmierung\gui\dialog_test.py", line 57, in close_dialog
self.close
AttributeError: 'Ui_Dialog' object has no attribute 'close'
der übergibt mir beim beenden das falsche object

Hier nochmal der Code, vielleicht kann mir einer auf die Sprünge helfen

Main programm

Code: Alles auswählen

from PyQt5 import QtCore, QtGui, QtWidgets

from dialog_test import Ui_Dialog as Form

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(670, 492)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.verticalLayout = QtWidgets.QVBoxLayout(self.centralwidget)
        self.verticalLayout.setObjectName("verticalLayout")
        self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
        self.horizontalLayout_2.setObjectName("horizontalLayout_2")
        self.tableWidget = QtWidgets.QTableWidget(self.centralwidget)
        self.tableWidget.setObjectName("tableWidget")
        self.tableWidget.setColumnCount(3)
        self.tableWidget.setRowCount(0)
        item = QtWidgets.QTableWidgetItem()
        self.tableWidget.setHorizontalHeaderItem(0, item)
        item = QtWidgets.QTableWidgetItem()
        self.tableWidget.setHorizontalHeaderItem(1, item)
        item = QtWidgets.QTableWidgetItem()
        self.tableWidget.setHorizontalHeaderItem(2, item)
        self.horizontalLayout_2.addWidget(self.tableWidget)
        self.verticalLayout.addLayout(self.horizontalLayout_2)
        self.horizontalLayout = QtWidgets.QHBoxLayout()
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.firstbutton = QtWidgets.QPushButton(self.centralwidget)
        self.firstbutton.setObjectName("firstbutton")
        self.horizontalLayout.addWidget(self.firstbutton)
        self.secondbutton = QtWidgets.QPushButton(self.centralwidget)
        self.secondbutton.setObjectName("secondbutton")
        self.horizontalLayout.addWidget(self.secondbutton)
        self.verticalLayout.addLayout(self.horizontalLayout)
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 670, 21))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

        self.firstbutton.clicked.connect(self.open_dialog)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        item = self.tableWidget.horizontalHeaderItem(0)
        item.setText(_translate("MainWindow", "First column"))
        item = self.tableWidget.horizontalHeaderItem(1)
        item.setText(_translate("MainWindow", "Second column"))
        item = self.tableWidget.horizontalHeaderItem(2)
        item.setText(_translate("MainWindow", "Third"))
        self.firstbutton.setText(_translate("MainWindow", "Add"))
        self.secondbutton.setText(_translate("MainWindow", "Delete"))

    def open_dialog(self):
        dialog = QtWidgets.QDialog()
        dialog.ui = Form()
        dialog.ui.setupUi(dialog)
        dialog.ui.saveFileDialog(dialog)
        #dialog.exec_()
        #dialog.show()


if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    MainWindow = QtWidgets.QMainWindow()
    ui = Ui_MainWindow()
    ui.setupUi(MainWindow)
    MainWindow.show()
    sys.exit(app.exec_())
Dialog

Code: Alles auswählen

from PyQt5 import QtCore, QtGui, QtWidgets
from job_test import RFID
import time

class Ui_Dialog(object):
    asyncFuncSignal = QtCore.pyqtSignal()

    def setupUi(self, Dialog):
        print(Dialog)
        Dialog.setObjectName("Dialog")
        print(Dialog)
        Dialog.resize(358, 126)
        self.verticalLayout = QtWidgets.QVBoxLayout(Dialog)
        self.verticalLayout.setObjectName("verticalLayout")
        self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
        self.horizontalLayout_2.setObjectName("horizontalLayout_2")
        self.dialoglabel2 = QtWidgets.QLabel(Dialog)
        self.dialoglabel2.setObjectName("dialoglabel")
        self.horizontalLayout_2.addWidget(self.dialoglabel2)
        self.verticalLayout.addLayout(self.horizontalLayout_2)

        self.horizontalLayout = QtWidgets.QHBoxLayout()
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.dialoglabel = QtWidgets.QLabel(Dialog)
        self.dialoglabel.setObjectName("dialoglabel")
        self.horizontalLayout.addWidget(self.dialoglabel)
        self.verticalLayout.addLayout(self.horizontalLayout)
        self.thread = None
        self.retranslateUi(Dialog)
        QtCore.QMetaObject.connectSlotsByName(Dialog)

    def retranslateUi(self, Dialog):
        _translate = QtCore.QCoreApplication.translate
        Dialog.setWindowTitle(_translate("Dialog", "Dialog"))
        self.dialoglabel2.setText(_translate("Dialog", "Bitte legen Sie den Toonies zum bespielen auf!"))
        self.dialoglabel.setText(_translate("Dialog", "Sobald der Vorgang abgeschlossen ist, schliesst sich das Fenster Automatisch!"))

    def saveFileDialog(self, Dialog):
        options = QtWidgets.QFileDialog.Options()
        options |= QtWidgets.QFileDialog.DontUseNativeDialog
        fileName, _ = QtWidgets.QFileDialog.getSaveFileName(Dialog,"QFileDialog.getSaveFileName()","","All Files (*);;Text Files (*.txt)", options=options)
        if fileName:
            self.start_rfid_job(Dialog)
            Dialog.exec_()
            Dialog.show()

    def start_rfid_job(self, Dialog):
        if self.thread is None or self.thread.isFinished():
            self.thread = RFID()
            print(Dialog)
            self.thread.finished.connect(self.close_dialog)
            #dialog.thread.finished.connect(self.test)
            self.thread.start()

    def close_dialog(self):
        print(self)
        self.close

Background Job

Code: Alles auswählen

from PyQt5.QtCore import QThread
import time
class RFID(QThread):

    def run(self):
        time.sleep(3) #Simuliert mein RFID-Modul
        print("done")
        self.finished.emit()
mfg Schechner
Jankie
User
Beiträge: 592
Registriert: Mittwoch 26. September 2018, 14:06

Beim close() fehlen die Klammern.
Schechner
User
Beiträge: 8
Registriert: Donnerstag 26. Januar 2017, 22:38

Vielen Dank für den Hinweis.

Jedoch ist es nicht besser geworden :(
<PyQt5.QtWidgets.QDialog object at 0x05B346E8>
<PyQt5.QtWidgets.QDialog object at 0x05B346E8>
<PyQt5.QtWidgets.QDialog object at 0x05B346E8>
done
Traceback (most recent call last):
File "c:\programmierung\gui\dialog_test.py", line 55, in close_dialog
self.close()
AttributeError: 'Ui_Dialog' object has no attribute 'close'
Sirius3
User
Beiträge: 17747
Registriert: Sonntag 21. Oktober 2012, 17:20

Natürlich hat UI_Dialog kein `close`, oder hast Du die Methode irgendwo implementiert?

Der eigentliche Dialog ist ein QDialog, der in open_dialog erzeugt wird.

Der ganze Code ist recht unübersichtlich. Statt aus dem Designer .py-Dateien zu erzeugen und die entgegen des Kommentars zu editieren, solltest Du mit uic.loadui direkt ui-Dateien laden. Dann kannst Du auch einfach von QDialog eine Klasse ableiten und dort noch benötigte Methoden hinzufügen. Diese Klasse hat dann auch eine close-Methode.
Schechner
User
Beiträge: 8
Registriert: Donnerstag 26. Januar 2017, 22:38

Sirius3 hat geschrieben: Freitag 28. Februar 2020, 09:47 Natürlich hat UI_Dialog kein `close`, oder hast Du die Methode irgendwo implementiert?

Der eigentliche Dialog ist ein QDialog, der in open_dialog erzeugt wird.

Der ganze Code ist recht unübersichtlich. Statt aus dem Designer .py-Dateien zu erzeugen und die entgegen des Kommentars zu editieren, solltest Du mit uic.loadui direkt ui-Dateien laden. Dann kannst Du auch einfach von QDialog eine Klasse ableiten und dort noch benötigte Methoden hinzufügen. Diese Klasse hat dann auch eine close-Methode.

sorry wenn es unübersichtlich ist, dachte mit der Aufteilung eine gewisse Übersicht reingebracht zu haben.


Ich hab jetzt das problem auch gelöst...

Ich habe das QThread gegen ein normales Thread "eingetauscht" und hab dann das dialog object übergeben, so konnte ich es nach dem beenden des Jobs schließen.
__deets__
User
Beiträge: 14536
Registriert: Mittwoch 14. Oktober 2015, 14:29

Das klingt nach zukünftigem Ärger. Wenn du aus dem Thread signale verschicken willst, musst du einen QThread benutzen.
Antworten