Reputation: 151
I'm trying to change style of button during action.
When button is pressed, I want to make them blue, then read rfid card and then by the ID change color of button to green or red (in code is just red to make it easier).
The problem is that changing style of button to blue do nothing (green and red works fine). It is waiting to finish 'clicked()' method? How to tell to PyQt5 "Do it now!" ?
Edit: I modified the pseudocode to reproducible Example.
#!/usr/bin/python3
import sys
import time
from PyQt5.QtWidgets import *
class MainScreen(QWidget):
def __init__(self):
QWidget.__init__(self)
self.button = QPushButton("TEXT")
self.button.clicked.connect(self.clicked)
self.changeButtonColor("black") # set default style
self.grid = QGridLayout(self)
self.grid.addWidget(self.button)
self.show()
def changeButtonColor(self, color):
self.button.setStyleSheet("QPushButton{ \
color: "+color+"; font: bold 18px;}")
def clicked(self):
self.changeButtonColor("blue") # nothing happening (this is my problem!)
uid = self.readCard() # reading ID from rfid card
self.changeButtonColor("red")
def readCard(self):
#return rfid.read() # in real case
time.sleep(2)
return "12345678"
def main():
app = QApplication(sys.argv)
window = MainScreen()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Upvotes: 1
Views: 95
Reputation: 1052
#!/usr/bin/python3
from PyQt5.QtWidgets import *
from PyQt5.QtCore import QSize
import asyncio
from quamash import QEventLoop
import qtawesome as qt
class MainScreen(QWidget):
oop = None
def __init__(self, _loop):
try:
QWidget.__init__(self)
self.loop = _loop
self.button = QPushButton("TEXT")
self.button.clicked.connect(self.synced_click)
self.button.setStyleSheet("color: black;")
self.grid = QGridLayout(self)
self.grid.addWidget(self.button)
self.show()
except Exception as e:
print(e)
def synced_click(self):
try:
asyncio.ensure_future(self.clicked(), loop=self.loop)
except Exception as e:
print('error')
print(e)
@asyncio.coroutine
async def clicked(self):
try:
spin_icon = qt.icon('fa5s.spinner', color='red', animation=qt.Spin(self.button))
self.loop.call_soon_threadsafe(self.button.setIconSize, QSize(12, 12))
self.loop.call_soon_threadsafe(self.button.setIcon, spin_icon)
self.loop.call_soon_threadsafe(self.button.setText, "progress")
self.loop.call_soon_threadsafe(self.button.setStyleSheet, "color: red;")
await asyncio.sleep(3)
tick_icon = qt.icon('fa5s.check-circle', color='blue')
self.loop.call_soon_threadsafe(self.button.setStyleSheet, "color:blue;")
self.loop.call_soon_threadsafe(self.button.setText, "done")
self.loop.call_soon_threadsafe(self.button.setIcon, tick_icon)
await asyncio.sleep(1)
except Exception as e:
print('error')
print(e)
if __name__ == "__main__":
import sys
loop = QEventLoop(QApplication(sys.argv))
asyncio.set_event_loop(loop=loop)
with loop:
window = MainScreen(loop)
loop.run_forever()
Upvotes: 0
Reputation: 13651
void QTimer::singleShot(int msec, const QObject *receiver, const char *member)
This static function calls a slot after a given time interval.
It is very convenient to use this function because you do not need to bother with a timerEvent or create a local QTimer object.
import sys
import time
from PyQt5.QtWidgets import *
from PyQt5 import QtCore
class MainScreen(QWidget):
def __init__(self):
QWidget.__init__(self)
self.button = QPushButton("TEXT")
self.button.clicked.connect(self.clicked)
self.changeButtonColor("black")
self.grid = QGridLayout(self)
self.grid.addWidget(self.button)
self.show()
def changeButtonColor(self, color):
self.button.setStyleSheet("QPushButton{ \
color: "+color+"; font: bold 18px;}")
def clicked(self):
self.changeButtonColor("blue") # nothing happening (this is my problem!)
# time.sleep(2) # reading ID from rfid card
# self.changeButtonColor("red")
QtCore.QTimer.singleShot(2000, lambda: self.changeButtonColor("red")) # <---
def main():
app = QApplication(sys.argv)
window = MainScreen()
sys.exit(app.exec_())
Update
import sys
import time
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtWidgets import *
class Worker(QtCore.QObject): # +++
started = QtCore.pyqtSignal()
finished = QtCore.pyqtSignal()
data = QtCore.pyqtSignal(str)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.running = False
self.stop = 5
@QtCore.pyqtSlot()
def read_data_from_sensor(self):
self.started.emit()
time.sleep(1) # We simulate the blocking process
while self.running and self.stop:
dt = time.strftime("%Y-%m-%d %H:%M:%S")
self.data.emit(dt)
time.sleep(1) # We simulate the blocking process
self.stop -= 1
self.finished.emit()
class MainScreen(QWidget):
def __init__(self):
QWidget.__init__(self)
self.button = QPushButton("TEXT Start")
self.button.clicked.connect(self.clicked)
self.changeButtonColor("black")
self.label_data = QtWidgets.QLabel(self, alignment=QtCore.Qt.AlignCenter)
self.label_data.setText('Pending')
self.grid = QGridLayout(self)
self.grid.addWidget(self.label_data)
self.grid.addWidget(self.button)
self.show()
### vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
self._worker = Worker()
self._worker.started.connect(self.on_started)
self._worker.finished.connect(self.on_finished)
self._worker.data.connect(self.update_label)
self._thread = QtCore.QThread(self)
self._thread.start()
self._worker.moveToThread(self._thread)
@QtCore.pyqtSlot()
def on_started(self):
self.label_data.setText("Start to read")
self.button.setText("TEXT Stop")
self.button.setEnabled(True)
self._worker.stop = 5
@QtCore.pyqtSlot()
def on_finished(self):
self.label_data.setText("Pending")
self.button.setText("TEXT Start")
self.button.setEnabled(True)
self.changeButtonColor("red") # <---
self._worker.running = False
self._worker.stop = 5
@QtCore.pyqtSlot(str)
def update_label(self, data):
self.label_data.setText(data)
### ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
def changeButtonColor(self, color):
self.button.setStyleSheet("QPushButton{ \
color: "+color+"; font: bold 18px;}")
def clicked(self):
self.changeButtonColor("blue") # nothing happening (this is my problem!)
# time.sleep(2) # reading ID from rfid card
# self.changeButtonColor("red")
# QtCore.QTimer.singleShot(2000, lambda: self.changeButtonColor("red")) # <---
### vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
if self._worker.running:
self._worker.running = False
else:
self._worker.running = True
QtCore.QTimer.singleShot(0, self._worker.read_data_from_sensor)
self.button.setEnabled(False)
### ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
def main():
app = QApplication(sys.argv)
window = MainScreen()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Upvotes: 1