mousePressEvent für Shutdown-Befehl?

Python und das Qt-Toolkit, erstellen von GUIs mittels des Qt-Designers.
Antworten
Tinker232
User
Beiträge: 50
Registriert: Mittwoch 25. Juli 2018, 13:45

Liebe Community,

ich bin recht neu mit Python unterwegs und bastel mir gerade einen kleinen Datenlogger mit dem RPi zusammen.
Das GUI habe ich mit QT Designer erstellt und ist recht einfach gehalten mit einigen Buttons.
Nun möchte ich gerne den Button "Herunterfahren" (pushbutton_shutdown) so steuern, dass wenn man ihn gedrückt hält ein Fenster aufgeht in dem ein Timer von 5 Sekunden auf 0 herunterzählt und dann das Programm schließt und den Raspberry herunterfährt. Sollte vorher der Button losgelassen werden schließt das Fenster wieder.

Es muss nicht zwangweise ein Fenster sein. Theoretisch könnte auch der Text im Button als Counter dienen oder habt ihr andere Vorschläge?
Ich hab das mal probiert weiß aber überhaupt nicht wie ich das mit dem "Button gedrückt" realisieren kann. Bin eben noch blutiger Anfänger in der ganzen Sache...
Die anderen Buttons habe ich bisher alle mittels pushbutton_xyz.clicked.connect(self.funktion_xyz) verknüpft...

Wäre sehr nett von Euch :)
LG Tina
__deets__
User
Beiträge: 14528
Registriert: Mittwoch 14. Oktober 2015, 14:29

Es gibt die Signale pressed und released.

http://doc.qt.io/qt-5/qabstractbutton.html#pressed

Mit denen sollte sich das umsetzen lassen. Du musst im pressed einen timer starten, den du im released stoppst. Und der bei erreichen der 0 dann eben den shutdown ausfuehrt.
Tinker232
User
Beiträge: 50
Registriert: Mittwoch 25. Juli 2018, 13:45

Danke für den Tipp.
Ich habe das nun folgendermaßen umgesetzt...
Leider habe ich das Problem, dass sobald ich den Pushbutton_shutdown drücke sofort mein Fenster öffnet, was ja auch richtig sein soll. Dadurch wird aber mein gedrückthalten des Buttons gelöst und das Fenster schließt wieder...
Das Ziel war es in diesem neu geöffneten Fenster dann mit einem LCD Number die verbleibendenden Sekunden anzuzeigen...

Code: Alles auswählen

	... GUI Konstruktor...
	
	self.shutdownTimer = QTimer(self)
	self.shutdownTimer.timeout.connect(self.raspberryShutdown)
	
	self.pushbutton_shutdown.pressed.connect(self.startTimer)
	self.pushbutton_shutdown.released.connect(self.stopTimer)

def startTimer(self):
	self.shutdownTimer.start(5000)
	self.window= QMainWindow()
	self.ui = Ui_Shutdown()
	self.ui.setupUi(self.window)
	self.window.show()
	
def stopTimer(self):
	self.window.close()
	self.shutdownTimer.stop()
	
def raspberryShutdown(self):
	print("Raspberry wird heruntergefahren")
	
def
Benutzeravatar
__blackjack__
User
Beiträge: 13077
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Tinker232: Bei einer Lösung mit Fenster mit Countdown wäre es üblicher *dort* eine Schaltfläche zum abbrechen des Countdowns anzubieten. Und vielleicht auch eine zum sofort herunterfahren, falls der Benutzer sicher ist, das er nicht so lange warten möchte.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Tinker232
User
Beiträge: 50
Registriert: Mittwoch 25. Juli 2018, 13:45

Ja, das wird wohl nicht anders gehen... Danke
Tinker232
User
Beiträge: 50
Registriert: Mittwoch 25. Juli 2018, 13:45

Hallo zusammen,
ich habe es nun etwas verändert und so öffnet sich nachdem der Button gedrückt wurde ein Dialogfenster mit einem lcdNumber der entsprechend von 5 auf 0 herunterzählt. Den Vorgang kann man über eine pushbutton abbrechen.
Leider habe ich nun ein Problem bei dem Counter, den ich zunächst erstmal nur mit einem print Befehl in der Console realisiert habe. Das Fenster öffnet sich erst nachdem der Counter fertig ist, d.h. also der Counter blockiert die Darstellung des Fensters, oder?
Wie gehe ich am besten vor? Wo würdet ihr den Counter entsprechend setzen? Muss hier wieder mit einem Thread gearbeitet werden oder wo genau liegt mein Fehler?

Code: Alles auswählen

	
class Dialog(QDialog, Ui_Shutdown):
	def __init__(self):
		QDialog.__init__(self)
		self.setupUi(self)
			
class GUI(QMainWindow, UI_MainWindow)
	def __init__(self):
		super(MainWindow, self).__init__()
		self.setupUi(self)
	
		self.shutdownTimer = QTimer(self)
		self.shutdownTimer.timeout.connect(self.raspberryShutdown)
	
		self.pushbutton_shutdown.pressed.connect(self.startTimer)

	def startTimer(self):
		self.dialog= Dialog()
		self.dialog.show()
		self.tme = 5
		for i in range (0, self.tme):
			print(i)
			i = i + 1
		else: 
			print("fertig")

	def stopTimer(self):
		self.window.close()
		self.shutdownTimer.stop()
	
	def raspberryShutdown(self):
		print("Raspberry wird heruntergefahren")
Benutzeravatar
__blackjack__
User
Beiträge: 13077
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Tinker232: Ich würde den Countdown ja als erstes mal in den Dialog verfrachten und den Dialog dann auch modal machen, damit nur der Dialog bedient werden kann und nicht mehr das Hauptfenster.

Wenn Du in der GUI jede Sekunde etwas machen möchtest, dann gibts dafür `QTimer`-Objekte die sekündlich etwas aufrufen können. Ob heruntergefahren werden soll oder nicht, ist dann der Rückgabewert des Dialogs. Der kann ja wenn er modal ist, dem Aufrufer mitteilen wie er beendet wurde, durch Ablauf des Countdowns oder betätigen einer „Sofort herunterfahren“-Schaltfläche, oder weil der Countdown durch eine Schaltfläche abgebrochen wurde.

Übrigens würde ich jetzt auch das `clicked`-Signal statt des `pressed`-Signals verwenden, damit sich diese Schaltfläche normal verhält, so wie der Benutzer das von allen anderen Schaltflächen gewohnt ist.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Tinker232
User
Beiträge: 50
Registriert: Mittwoch 25. Juli 2018, 13:45

Ok danke...
hab das self.dialog.show() nun in self.dialog.exec_() umgewandelt. Der Counter zählt nun auch soweit herunter. Leider ist es mir nicht gelungen, nach dem self.dialog.exec_() meine self.dialog.countdown() auszuführen um die Funktion in der Dialog.py auszuführe und mir dann einen entsprechenden Rückgabewert liefert. Bzw. das funktioniert schon, aber erst nachdem ich das Dialogfenster mit X geschlossen habe oder wie soll ich es sonst ermöglichen, dass ich einen Rückgabewert vom Dialog bekomme?
LG
__deets__
User
Beiträge: 14528
Registriert: Mittwoch 14. Oktober 2015, 14:29

Das countdown kommt doch auch nicht nach, sondern waehrend des Dialogs. Und das Dialog-Objekt hat Slots, die du aufrufen kannst - accept() und reject() - die du aus coundown aufrufen kannst, in deinem Fall denke ich mal accept. Damit sollte dann der Rueckgabewert von exec_ dir verraten, was der User ausgewaehlt bzw ignoriert hat.
Tinker232
User
Beiträge: 50
Registriert: Mittwoch 25. Juli 2018, 13:45

Oh, ok sorry Leute ich bin zu blöd. Könnt ihr mir ein Beispiel nennen wie das auszusehen hat? :x

Mein Dialog wird in der xyz.py folgendermaßen aufgerufen: self.Dialog.exec()

Mein Countdown befindet sich im in dieem Dialog und wird über self.countdown() aufgerufen. Dieser startet den Qtimer und nach dem Timeout alle sekunde wird mein funktion lcdwert um jeweils einen Wert nach unten gesetzt bis er auf 0 ist, danach wird der countdown gestoppt und das fenster mit self.close() geschlossen und erstmal print("timer abgelaufen") ausgegeben...
Der Button wird folgendermaßen überwacht self.pushbutton_stop_clicked.connect(self.stop). Beim drücken wird dann self.stop aufgerufen welches den Timer stoppt und ebenfalls das Fenster schließt...
Tinker232
User
Beiträge: 50
Registriert: Mittwoch 25. Juli 2018, 13:45

Ich habe es nun wie folgt gelöst, vermutlich nicht schön, aber es funktioniert erstmal...

Der Dialog wird in der xyz.py folgendermaßen aufgerufen

Code: Alles auswählen

self.herunterfahren = self.dialog.exec_()
if self.herunterfahren == True:
	print("shutdown")
	#Weitere Befehle hier
else:
	print("kein shutdown")
Der Dialog beinhaltet unter anderem dann

Code: Alles auswählen

self.pushbutton_stop_clicked.connect(self.stop)
self.countdown()

def stop(self):
	self.cnt.stop()
	self.done(0)

def countdown(self):
	self.cnt = QTimer(self)
	self.cnt.timeout.connect(self.lcdWert)
	self.cnt.start(1000)
	
def lcdWert(self):
	if self.wert >= 0:
		self.lcd.display(self.wert)
		self.wer = self.wert - 1
	else:
		self.cnt.stop()
		self.done(1)
		
Gerne würde ich aber doch wissen wie es schöner aussehen könnte?!
Benutzeravatar
__blackjack__
User
Beiträge: 13077
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Tinker232: __deets__ hat `accept()` und `reject()` ja bereits erwähnt. Mit `done()` geht es auch, aber `accept()` und `reject()` vermitteln dem Leser deutlich besser als 0 und 1 bei `done()` was das eigentlich bedeutet.

Das ``self.herunterfahren == True:`` funtkioniert hier nur weil `True` gleichzeitig die Zahl 1 ist. Das ist aber eher verwirrend wenn man das so liest. Literale Vergleiche mit `True` und `False` sollte man sowieso nicht machen, denn da kommt ja wieder nur ein Wahrheitswert heraus. Entweder den den man schon hatte, dann kann man den auch ohne Vergleich verwenden, oder das Gegenteil, dafür gibt es ``not``.

Wenn man vergleicht und `accept()`/`reject()` verwendet, dann am besten mit der entsprechenden Konstante, dann wird der Code auch beim Aufrufer klarer.

Warum wird `herunterfahren` an das Objekt gebunden? Das ist doch nur ein lokaler Name der nur in dieser Methode und nur während des Aufrufs gebraucht wird. Im Grunde muss man das nicht einmal an einen Namen binden.

Code: Alles auswählen

        if self.shutdown_dialog.exec_() == QDialog.Accepted:
            print('Shutdown')
Und der Code im Dialog dann:

Code: Alles auswählen

    def __init__(self, …):
        # …
        self.countdown_timer = None
        self.pushbutton_stop.clicked.connect(self.stop_countdown)
        self.start_countdown()

    def stop_countdown(self):
        self.countdown_timer.stop()
        self.reject()

    def start_countdown(self):
        self.countdown_timer = QTimer(self)
        self.countdown_timer.timeout.connect(self.update_countdown)
        self.countdown_timer.start(1000)
        
    def update_countdown(self):
        if self.value >= 0:
            self.lcd.display(self.value)
            self.value  -= 1
        else:
            self.countdown_timer.stop()
            self.accept()
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Tinker232
User
Beiträge: 50
Registriert: Mittwoch 25. Juli 2018, 13:45

Super, vielen Dank für die Bemühungen...
Antworten