2

I designed a form in qtdesigner. It has 'on' and 'off' buttons. On button should start to blink led and off button should stop it.So, If the time.sleep duration is short there is no problem but when i write 10 seconds for sleep it doesn't stop instantly when i click on the off button. Program waits 10 seconds to stop Led blinking. So how can time.sleep be interrupted?


import time
import threading
import RPi.GPIO as GPIO
import sys
from time import sleep
from PyQt5.QtWidgets import QMainWindow, QPushButton, QApplication, QLabel
from PyQt5 import QtCore, QtGui, QtWidgets


GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)
GPIO.setup(17, GPIO.OUT)

switch = True  


def blink(self):  
        def run():  
            while (switch == True):
                print('BLINK...BLINK...')
                GPIO.output(17, GPIO.HIGH)
                time.sleep(10.0)
                GPIO.output(17, GPIO.LOW)
                time.sleep(10.0)
                if switch == False:  
                    break
        thread = threading.Thread(target=run)
        thread.start()



class Ui_Form(object):
    def setupUi(self, Form):
        Form.setObjectName("Form")
        Form.resize(400, 300)


        self.pshbttn1 = QtWidgets.QPushButton(Form)
        self.pshbttn1.setGeometry(QtCore.QRect(60, 170, 125, 50))
        font = QtGui.QFont()
        font.setPointSize(16)
        font.setBold(True)
        font.setWeight(75)
        self.pshbttn1.setFont(font)
        self.pshbttn1.setObjectName("pshbttn1")
        self.pshbttn1.clicked.connect(self.switchon)


        self.pshbttn2 = QtWidgets.QPushButton(Form)
        self.pshbttn2.setGeometry(QtCore.QRect(220, 170, 125, 50))
        font = QtGui.QFont()
        font.setPointSize(16)
        font.setBold(True)
        font.setWeight(75)
        self.pshbttn2.setFont(font)
        self.pshbttn2.setObjectName("pshbttn2")
        self.pshbttn2.clicked.connect(self.switchoff)


        self.pshbttn3 = QtWidgets.QPushButton(Form)
        self.pshbttn3.setGeometry(QtCore.QRect(140, 230, 125, 50))
        font = QtGui.QFont()
        font.setPointSize(16)
        font.setBold(True)
        font.setWeight(75)
        self.pshbttn3.setFont(font)
        self.pshbttn3.setObjectName("pshbttn3")
        self.pshbttn3.clicked.connect(app.exit)


        self.label = QtWidgets.QLabel(Form)
        self.label.setGeometry(QtCore.QRect(80, 80, 251, 51))
        font = QtGui.QFont()
        font.setPointSize(12)
        self.label.setFont(font)
        self.label.setObjectName("label")

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

    def retranslateUi(self, Form):
        _translate = QtCore.QCoreApplication.translate
        Form.setWindowTitle(_translate("Form", "LED"))
        self.pshbttn1.setText(_translate("Form", "ON"))
        self.pshbttn2.setText(_translate("Form", "OFF"))
        self.pshbttn3.setText(_translate("Form", "EXIT"))
        self.label.setText(_translate("Form", "LED\'i açmak için butonları kullanın"))

    def switchon(self):    
        global switch  
        switch = True  
        print ('switch on')
        blink(self)

    def switchoff(self):    
        print ('switch off') 
        global switch  
        switch = False 

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

eyllanesc
  • 235,170
  • 19
  • 170
  • 241
nyiragongo
  • 23
  • 4
  • Hi, and welcome to OS. I would suggest to simply use a shorter interval, but take a look at this [post](https://stackoverflow.com/questions/5114292/break-interrupt-a-time-sleep-in-python/46346184) in order to understand how to interrupt a `time.sleep()` in Python. – Daniel Ocando Dec 20 '19 at 22:10

1 Answers1

1

In this case it is not necessary to use sleep, just use a QTimer. To simplify the task I created a class that handles the pin. In addition, PyQt5 recommends not modifying the class generated by Qt Designer.

import sys

from PyQt5 import QtCore, QtGui, QtWidgets

import RPi.GPIO as GPIO


class Led:
    def __init__(self, pin, timeout=1000):
        self._state = False
        self._pin = pin
        self._timeout = timeout

        GPIO.setwarnings(False)
        GPIO.setmode(GPIO.BCM)
        GPIO.setup(self.pin, GPIO.OUT)

        self.blink_timer = QtCore.QTimer(
            interval=self.timeout, timeout=self._on_blink_timeout
        )

    def _on_blink_timeout(self):
        self.state = not self.state

    def _update_internal_state(self):
        GPIO.output(self.pin, GPIO.HIGH if self._state else GPIO.LOW)

    @property
    def pin(self):
        return self._pin

    @property
    def timeout(self):
        return self._timeout

    @timeout.setter
    def timeout(self, v):
        self._timeout = v
        is_active = self.blink_timer.isActive()
        self.blink_timer.setInterval(self.timeout)
        if is_active:
            self.blink_timer.start()

    def on(self):
        self.state = True

    def off(self):
        self.state = False

    @property
    def state(self):
        return self._state

    @state.setter
    def state(self, s):
        self._state = s
        self._update_internal_state()

    def start(self):
        self.state = True
        self.blink_timer.start()

    def stop(self):
        self.state = False
        self.blink_timer.stop()


class Ui_Form(object):
    def setupUi(self, Form):
        Form.setObjectName("Form")
        Form.resize(400, 300)

        self.pshbttn1 = QtWidgets.QPushButton(Form)
        self.pshbttn1.setGeometry(QtCore.QRect(60, 170, 125, 50))
        font = QtGui.QFont()
        font.setPointSize(16)
        font.setBold(True)
        font.setWeight(75)
        self.pshbttn1.setFont(font)
        self.pshbttn1.setObjectName("pshbttn1")
        self.pshbttn2 = QtWidgets.QPushButton(Form)
        self.pshbttn2.setGeometry(QtCore.QRect(220, 170, 125, 50))
        font = QtGui.QFont()
        font.setPointSize(16)
        font.setBold(True)
        font.setWeight(75)
        self.pshbttn2.setFont(font)
        self.pshbttn2.setObjectName("pshbttn2")
        self.pshbttn3 = QtWidgets.QPushButton(Form)
        self.pshbttn3.setGeometry(QtCore.QRect(140, 230, 125, 50))
        font = QtGui.QFont()
        font.setPointSize(16)
        font.setBold(True)
        font.setWeight(75)
        self.pshbttn3.setFont(font)
        self.pshbttn3.setObjectName("pshbttn3")
        self.pshbttn3.clicked.connect(app.exit)

        self.label = QtWidgets.QLabel(Form)
        self.label.setGeometry(QtCore.QRect(80, 80, 251, 51))
        font = QtGui.QFont()
        font.setPointSize(12)
        self.label.setFont(font)
        self.label.setObjectName("label")

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

    def retranslateUi(self, Form):
        _translate = QtCore.QCoreApplication.translate
        Form.setWindowTitle(_translate("Form", "LED"))
        self.pshbttn1.setText(_translate("Form", "ON"))
        self.pshbttn2.setText(_translate("Form", "OFF"))
        self.pshbttn3.setText(_translate("Form", "EXIT"))
        self.label.setText(_translate("Form", "LED'i açmak için butonları kullanın"))


class Widget(QtWidgets.QWidget, Ui_Form):
    def __init__(self, parent=None):
        super(Widget, self).__init__(parent)
        self.setupUi(self)
        self.led = Led(17, timeout=10000)
        self.pshbttn1.clicked.connect(self.led.start)
        self.pshbttn2.clicked.connect(self.led.stop)


if __name__ == "__main__":
    import sys

    app = QtWidgets.QApplication(sys.argv)
    w = Widget()
    w.show()
    sys.exit(app.exec_())
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • First, code works smoothly so many thanks for this. However code is too complicated for me to understand for now and it is not exactly the way i want to solve this problem. Actually if there is any solution with thread, can you help me to write the code with threads. – nyiragongo Dec 21 '19 at 22:06
  • @nyiragongo Well, threads are unnecessary. Threads introduce unnecessary complexity in addition to other unnecessary risks. Instead of complicating yourself too much in looking for "the magic solution of threads" you should take a little time and analyze my solution that is stable and that the base point is the use of QTimer. So my answer is: No, I will not invest my time in implementing a solution with threads because the possible answer would not be better than the one I have proposed, the solution with QTimer is the best – eyllanesc Dec 21 '19 at 22:10
  • I marked your solution.Well, I will try to comprehend this one. Eventually it is stable as you mentioned. Mine is just curiosity and stubernness. I started with thread and I really want to solve it with threads. Anyway I really apprecite for your help. – nyiragongo Dec 21 '19 at 22:26
  • 1
    @nyiragongo From the experience I have with Qt I always use threads as a last option, I only use it in the case that there is a task that consumes a lot of time (and in the case of changing the state of a led clearly it is not), in In general, in Qt, tasks must be executed asynchronously (for example, QTimer), and only threads are used to prevent time-consuming tasks from blocking the main thread. In conclusion: when there is no Qt-solution (signals and slots) then just try threads. – eyllanesc Dec 21 '19 at 22:31