I am using a GUI with a checkbox made with QT and python, when the checkbox is checked i want to display 'stackoverflow' every 5 seconds, when it is unchecked i want it to do nothing. I have tried to call the following function when it is checked:
def work ():
threading.Timer(5, work).start ()
print "stackoverflow"
def checkbox(self, state):
if state == QtCore.Qt.Checked:
print 'checked'
work()
else: print 'unchecked'
But it keeps on printing 'stackoverflow'. How can i stop this?
Here is a solution.
from threading import Thread
import time
class WorkerThread:
def __init__(self, timer=5):
self.timer = timer
self._alive = False
def work(self):
while self._alive:
time.sleep(self.timer)
print("Stack Overflow")
def start(self):
self._thread = Thread(target=self.work)
self._alive = True
self._thread.start()
def stop(self):
self._alive = False
worker_thread = WorkerThread()
def checkbox(self, state):
if state == QtCore.Qt.Checked:
worker_thread.start()
else:
worker_thread.stop()
You can use variable to control thread
running = False
def work ():
print "stackoverflow"
if running:
threading.Timer(5, work).start ()
def checkbox(self, state):
global running
if state == QtCore.Qt.Checked:
print 'checked'
running = True
work()
else:
print 'unchecked'
running = False
Related
I'm trying to write a PyQt5 program with start and stop buttons. For both the start and stop buttons, I am using two different functions that connect to the Twitter API. When I click on the start button, the GUI freezes and my cursor is constantly "processing" and does not let me click on the stop button. I tried multi-threading and this still occurs.
This is my program:
class TwitterMain(QDialog):
def __init__(self):
QtWidgets.QMainWindow.__init__(self)
loadUi('twittermain.ui', self)
self.thread = {}
self.startcollect.clicked.connect(self.start_worker_1)
self.startanalyze.clicked.connect(self.start_worker_2)
self.stopcollect.clicked.connect(self.stop_worker_1)
self.stopanalyze.clicked.connect(self.stop_worker_2)
def start_worker_1(self):
self.thread[1] = ThreadClass(parent=None, index=1)
self.thread[1].start()
self.thread[1].any_signal.connect(self.datacollection)
self.startcollect.setEnabled(False)
def start_worker_2(self):
self.thread[2] = ThreadClass(parent=None, index=2)
self.thread[2].start()
self.thread[2].any_signal.connect(self.datacollection)
self.startanalyze.setEnabled(False)
def stop_worker_1(self):
self.thread[1].stop()
self.stopcollect.setEnabled(True)
def stop_worker_2(self):
self.thread[2].stop()
self.stopanalyze.setEnabled(True)
def datacollection(self, counter):
#data collection from twitter API using tweepy
class ThreadClass(QtCore.QThread):
any_signal = QtCore.pyqtSignal(int)
def __init__(self, parent=None, index=0):
super(ThreadClass, self).__init__(parent)
self.index = index
self.is_running = True
def run(self):
print('Starting thread...', self.index)
cnt = 0
while (True):
cnt += 1
if cnt == 99: cnt = 0
time.sleep(0.01)
self.any_signal.emit(cnt)
def stop(self):
self.is_running = False
print('Stopping thread...', self.index)
self.terminate()
I'm not entirely sure why it isn't working. There is more code above and below it connecting it to the GUI but this is the main page. I'm only trying to make the start1 and stop1 work and will replicate it for the second set of start and stop. Any help is appreciated!
I'm working on a Tkinter Desktop Application project. I started stumbling when I need to use Threading which runs the backend code.
I know that in order to share the variables across threads we should be using a global variable. Below is the minimal code.
obj = None
class Frame:
def __init__(self, frame):
self.middle_frame = frame
self.start_button = ttk.Button(self.middle_frame, text='Start', command=self.start)
self.start_button.grid(row=0, column=0)
self.stop_button = ttk.Button(self.middle_frame, text='Stop', command=self.stop)
self.stop_button.grid(row=0, column=1)
self.stop_button.config(state='disabled')
def start(self):
self.thread = threading.Thread(target=self.start_connection)
self.thread.start()
self.start_button.config(state='disabled')
self.stop_button.config(state='normal')
def start_connection(self):
global obj
obj = MainManager() # Starts the Backend Loop
def stop(self):
global obj
obj.close_connection() # Want to break the loop here
self.thread.join()
self.stop_button.config(state='disabled')
self.start_button.config(state='normal')
While running this code, I get obj.close_connection() AttributeError:'NoneType' object has no attribute 'close_connection'. But I was expecting obj to become as an object of MainManager().
Where am I going wrong? Help me with this.
The problem with your code is nothing to do with tkinter or really with multithreading. The problem is that MainManager has the loop started inside the __init__ method, something like this:
class MainManager:
def __init__(self):
self.alive = True
# this part runs until stopped
self.start_doing_stuff()
def start_doing_stuff(self):
while self.alive:
sleep(1)
print('doing stuff')
def stop(self):
self.alive = False
Here is a code snippet with a similar error to yours:
from threading import Thread
from time import sleep
obj = None
class MainManager:
def __init__(self):
self.alive = True
self.start_doing_stuff()
def start_doing_stuff(self):
while self.alive:
sleep(1)
print('doing stuff')
def stop(self):
self.alive = False
def test():
global obj
print('about to assign to my_global')
obj = MainManager()
print('assigned to my_global')
print(f'{obj=}')
t = Thread(target=test)
t.start()
sleep(3)
print(f'{obj=}')
obj.stop()
t.join()
Because the __init__ method doesn't terminate until the stop method is called, the call to MainManager() in the statement obj = MainManager() never terminates, so obj is not assigned to. This means that when obj.stop() is called, obj is still None.
This can be fixed my making the __init__ method terminate, and putting the long-running code into a separate method that is called later:
from threading import Thread
from time import sleep
obj = None
class MainManager:
def __init__(self):
self.alive = True
def start_doing_stuff(self):
while self.alive:
sleep(1)
print('doing stuff')
def stop(self):
self.alive = False
def test():
global obj
print('about to assign to my_global')
obj = MainManager()
print('assigned to my_global')
obj.start_doing_stuff()
print(f'{obj=}')
t = Thread(target=test)
t.start()
sleep(3)
print(f'{obj=}')
obj.stop()
t.join()
Have you imported MainManager, have you run the start_connection()?
Also, you delegate the assignment to a thread. Are you sure this affects the same obj as the main thread? Maybe the main thread stays 'none' and the delegate changes the value in a copy. Add a log or print statement and make sure the obj gets assigned the MainManager() object and print the memory location of that object. In the main class print the memory location of the obj variable there as well. Check if it is the same.
This is how you print the memory address of a variable :
print(hex(id(your_variable)))
Good day everyone, Im trying to call a method inside a thread class which should set an event flag inside that thread, resulting in the thread to stop running while the event is set. The current code kind of works, and calls the function, but the event flag does not seem to trigger inside the thread.
The thread is responsible for an operation to run when a button is pressed on the GUI, but it shouldn't run if the event is set.
A minimal version of my code:
import threading
import time
GPIO.setup(18, GPIO.IN, pull_up_down=GPIO.PUD.UP)
global run = 0
class Pump_Thread(threading.Thread):
def __init__(self, interval=0.5):
super(Pump_Thread, self).__init__()
self._stop_event = threading.Event()
self.interval = interval
Pump_threads = threading.Thread(target=self.run, daemon=True)
Pump_threads.start()
def stop(self):
self._stop_event.set()
print("Things have stopped yo...")
def resume(self):
self._stop_event.clear()
print("And Now Things will resume")
def run(self)
while not self._stop_event.is_set():
if (run == 1):
#doing some stuff when bit set#
print("Yeah Yeah, I'm running")
class Page1(Page):
def __init__(self, *args, **kwargs):
#Some Initializing methods to load buttons and graphics
self.start_btn=tk.Button(self,height=32,width =80,command=self.Start)
self.start_btn.place(x=50, y=50)
self.reset_btn=tk.Button(self,height=32,width =80,command=self.Reset)
self.reset_btn.place(x=50, y=80)
def Start(self):
global run
run = 1 #<------Set Bit for code to run
def Reset(self):
d = Pump_Thread()
d.resume() #<-----Run Method inside thread
class Monitor_Thread(threading.Thread):
def __init__(self, interval=0.5):
self.interval = interval
Monitor_Threads = threading.Thread(target=self.Monitor_Thread, daemon=True)
Monitor_Threads.start()
def run(self)
while True:
if Condition == True:
d = Pump_Thread()
d.stop() #<-----Run Method inside thread
class Interrupt_Class(page):
def Input(self):
d = Pump_Thread()
d.stop() #<-----Run Method inside thread
GPIO.add_event_detect(18, GPIO.FALLING, callback=Input, bouncetime=300)
class MainView(tk.Frame):
def __init__(self, *args, **kwargs):
tk.Frame.__init__(self, *args, *kwargs)
super().__init__()
p1 = Page1(self) #Other Pages Added here with navigation buttons to raise the page to the front of the GUI#
p1.show()
if __name__ == "__main__":
root = tk.TK()
main = MainView(root)
main.pack(side="top", fill="both", expand=True)
root.wm_geometry("400x400")
root.attributes("-fullscreen", False)
Thread1 = Pump_Thread()
Thread2 = Monitor_Thread()
root.mainloop()
When the interrupt is triggered, "Things have stopped yo..." is printed, so the method is called, but the process still starts when the GUI Button pressed, meaning the event didn't set. What would be the reason for this?
You seem to have many mistakes piled on top of one another.
You need to look at the many examples on stackoverflow of how to create and manage threads.
You might try something like this:
class Pump_Thread(threading.Thread):
def __init__(self, interval=0.5):
super().__init__()
self._stop_event = threading.Event()
self.interval = interval
def stop(self):
self._stop_event.set()
print("Things have stopped yo...")
def resume(self):
self._stop_event.clear()
print("And Now Things will resume")
def run(self)
while not self._stop_event.is_set():
print("Yeah Yeah, I'm running")
# other parts elided
if __name__ == "__main__":
root = tk.TK()
main = MainView(root)
Thread1 = Pump_Thread()
Thread1.start()
but now all other places which need to start and stop Pump_Thread need access to Thread1. So you should pass this into objects that are created.
class Interrupt_Class(page):
def __init__(self, pump):
self.pump = pump
def Input(self):
self.pump.stop() #<-----Run Method inside thread
ic = Interrupt_Class(Thread1)
You are calling:
d = Pump_Thread()
each time before calling: d.stop() which only makes that thread stop.
How is Pump_Thread instantiated and where do you store the reference to it?
In fact, you don't show how any of your classes are instantiated.
I am wondering if I can run a while loop inside a method of a Python class that can be stopped from another method.
For example, something like this:
from time import sleep
class example():
global recording
def __init__(self):
pass
def start(self):
global recording
recording = True
while recording:
print(1)
def stop(self):
global recording
recording = False
print("SLEEEPINGGGGGGGGGGG")
a = example()
a.start()
sleep(0.5)
a.stop()
But, it does not work, the loop does not stop.
EDIT
Since I do not want to create a Thread outside the class, I just tried this, but it doesn't work either.
from time import sleep
import threading
class example():
def __init__(self):
self.is_running = False
def start(self):
self.is_running = True
self.loop_thread = threading.Thread(target=self.main_loop)
def main_loop(self):
while self.is_running:
sleep(0.5)
print(1)
def stop(self):
self.is_running = False
print("SLEEEPINGGGGGGGGGGG")
a = example()
a.start()
sleep(3)
a.stop()
a.start() is an infinite loop. Since nothing runs at the same time, it just runs and never reaches the next statements.
You need to create a thread, like this
import time,threading
class example():
def __init__(self):
self.__recording = False
def start(self):
self.__recording = True
while self.__recording:
time.sleep(1)
print(1)
def stop(self):
self.__recording = False
a = example()
t = threading.Thread(target=a.start)
t.start()
time.sleep(5)
a.stop()
t.join()
note that I have used a member variable instead of a global. When the start method sees that the variable is True, it quits the loop. job done.
That works because I'm using sleep(). If you're running a pure python CPU intensive job that won't work because of python GIL
As suggested in comments, you can go one step further and inherit from threading.Thread method instead:
import time,threading
class example(threading.Thread):
def __init__(self):
threading.Thread.__init__(self,target=self.run)
self.__recording = False
def run(self):
self.__recording = True
while self.__recording:
time.sleep(1)
print(1)
def join(self):
self.__recording = False
threading.Thread.join(self)
a = example()
a.start()
time.sleep(5)
a.join()
Note that stop method is now replaced by join which signals the recording thread to stop then calls the parent join method. So when you join you stop the loop automatically first.
You have to start your thread after initiating it:
from time import sleep
import threading
class example():
def __init__(self):
self.is_running = False
def start(self):
self.is_running = True
self.loop_thread = threading.Thread(target=self.main_loop)
self.loop_thread.start() # <--- you forgot to start your thread
def main_loop(self):
while self.is_running:
sleep(0.5)
print(1)
def stop(self):
self.is_running = False
print("SLEEEPINGGGGGGGGGGG")
a = example()
a.start()
sleep(3)
a.stop()
# -*- coding: utf-8 -*-
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.uic import loadUiType
import youtube_dl
import pafy
import urllib.request
import urllib.parse
from urllib.parse import *
import win32clipboard
import sys
import os
import humanize
import subprocess
import time
import shutil
import re
from pySmartDL import SmartDL
from os.path import splitext, basename
from os import path
Close = False
ShutDown = False
Sleep = False
Restart = False
Paused = False
Stopped = False
Start = True
Resume = True
if getattr(sys, 'frozen', False):
# frozen
dir_ = os.path.dirname(sys.executable)
else:
# unfrozen
dir_ = os.path.dirname(os.path.realpath(__file__))
FORM_CLASS, _ = loadUiType(path.join(dir_, "main.ui"))
class MainApp(QMainWindow, FORM_CLASS):
def __init__(self, parent=None):
super(MainApp, self).__init__(parent)
QMainWindow.__init__(self)
self.setupUi(self)
self.lineEdit.installEventFilter(self)
self.lineEdit_3.installEventFilter(self)
self.lineEdit_6.installEventFilter(self)
self.Handle_Ui()
self.Handle_Buttons()
def closeEvent(self, evnt):
if self._want_to_close:
super(MainApp, self).closeEvent(evnt)
sys.exit()
def Handle_Ui(self):
#self.lineEdit.setFocus()
self.setFixedSize(861,441)
def Handle_Buttons(self):
self.pushButton_3.clicked.connect(self.open_file_loction)
self.pushButton_2.clicked.connect(self.Start)
self.pushButton_13.clicked.connect(self.Pause)
self.pushButton_14.clicked.connect(self.Stop)
self.pushButton.clicked.connect(self.Handle_Browse)
self.pushButton_4.clicked.connect(self.Download_youtube_video)
self.pushButton_10.clicked.connect(self.get_quality)
self.pushButton_5.clicked.connect(self.Browse2)
self.pushButton_6.clicked.connect(self.open_file_location2)
self.pushButton_11.clicked.connect(self.Search_Qualities)
self.pushButton_7.clicked.connect(self.Browse3)
self.pushButton_9.clicked.connect(self.download_playlist)
self.pushButton_8.clicked.connect(self.open_file_location3)
self.pushButton_12.clicked.connect(self.open_video)
self.comboBox_2.currentIndexChanged.connect(self.Action_Happened)
self.comboBox_3.currentIndexChanged.connect(self.Action_Happened)
self.comboBox_4.currentIndexChanged.connect(self.Action_Happened)
def Start(self):
global Start
global Stopped
global Paused
Start = True
Stopped = False
Paused = False
self.Download()
def Pause(self):
global Paused
global Start
global Resume
if self.pushButton_13.text()=="Pause Downloading":
Paused = True
Start = False
Stopped = False
Resume = False
self.pushButton_13.setText("Resume Downloading")
QApplication.processEvents()
elif self.pushButton_13.text()=="Resume Downloading":
Start = True
Paused = False
Resume = True
Stopped = False
self.pushButton_13.setText("Pause Downloading")
QApplication.processEvents()
def Stop(self):
global Stopped
global Start
Stopped = True
Start = False
Paused = False
self.Download()
def Download(self):
directory = os.path.expanduser("~") + "\AppData\Local\Temp\pySmartDL"
if not os.path.exists(directory):
os.makedirs(directory)
try:
global Paused
global Stopped
global Start
global XX
url = self.lineEdit.text()
save_location = self.lineEdit_2.text()
obj = SmartDL(url, progress_bar=False)
if Start == True:
try:
obj.start(blocking=False)
while True:
self.progressBar.setValue(obj.get_progress()*100)
self.label_8.setText("Downloaded: " + str(obj.get_dl_size(human=True)))
self.label_38.setText("Speed: " + str(obj.get_speed(human=True)))
self.label_39.setText("Remaining Time: " + str(obj.get_eta(human=True)))
time.sleep(0.2)
QApplication.processEvents()
if Paused == True:
obj.pause()
QApplication.processEvents()
if Resume == True:
obj.unpause()
QApplication.processEvents()
if obj.isFinished():
break
if Stopped == True:
obj.stop()
self.progressBar.setValue(0)
break
if obj.isSuccessful():
#os.rename(obj.get_dest(), save_location)
shutil.move(obj.get_dest(), save_location)
if Close == True:
QApplication.quit()
elif ShutDown == True:
os.system('shutdown -s')
elif Sleep == True:
os.system("rundll32.exe powrprof.dll,SetSuspendState 0,1,0")
elif Restart == True:
subprocess.call(["shutdown", "/r"])
if Stopped == False:
QMessageBox.information(self, "Download Completed", "Your Download is Completed")
except:
QMessageBox.warning(self, "Download Error", "Download Failed")
pass
except Exception as e:
pass
def main():
app = QApplication(sys.argv)
window = MainApp()
window.show()
app.exec_()
if __name__ == '__main__':
main()
I've been looking around for help on how to Apply Qthread on the above piece of code to update the progress Bar Dynamically with no "Not Responding problem"
I've read many topics and I could understand the main concept of Qthread but still can't get the idea on how to Apply it to my Code taking in consideration that download function is connected with a download button not running infinitely.
should I make a sub Qthread class or what can I do ?
If You Can help me with an example on how to use it with the above piece of code and I will Apply it on the remaining code of my Gui App..
Thanks in advance.
When implementing a QThread the task to be performed should be done in the run () method, if you want to update the GUI with data provided by the thread should not be done directly but through signals since the GUI should only be updated in the main thread called by this the GUI thread.
class DownloadThread(QThread):
dataChanged = pyqtSignal(int, str, str, str)
Started, Paused, Resume, Stopped = range(4)
downloadError = pyqtSignal()
downloadFinished = pyqtSignal()
def __init__(self, parent=None):
QThread.__init__(self, parent)
self.state = DownloadThread.Stopped
self.params = {"url": "", "save_location": ""}
def setParams(self, params):
self.params = params
def setState(self, state):
self.state = state
def run(self):
obj = SmartDL(self.params["url"], progress_bar=False)
try:
obj.start(blocking=False)
while True:
self.dataChanged.emit(obj.get_progress() * 100,
str(obj.get_dl_size(human=True)),
str(obj.get_speed(human=True)),
str(obj.get_eta(human=True)))
time.sleep(0.2)
if self.state == DownloadThread.Paused:
obj.pause()
if self.state == DownloadThread.Resume:
obj.unpause()
self.state = DownloadThread.Started
if obj.isFinished():
break
if self.state == DownloadThread.Stopped:
obj.stop()
self.progressBar.setValue(0)
break
if obj.isSuccessful():
# os.rename(obj.get_dest(), save_location)
shutil.move(obj.get_dest(), self.params["save_location"])
if self.state == DownloadThread.Started:
self.downloadFinished.emit()
except:
self.downloadError.emit()
You should avoid using global variables in addition that you can reduce several variables that are all updated at once by only one variable so I had to modify the GUI code.
class MainApp(QMainWindow, FORM_CLASS):
def __init__(self, parent=None):
# ...
self.Handle_Buttons()
self.download = DownloadThread(self)
self.download.dataChanged.connect(self.onDataChanged)
self.download.downloadError.connect(self.errorDownload)
self.download.downloadFinished.connect(self.successfulDownload)
def closeEvent(self, evnt):
# ...
def Handle_Ui(self):
# self.lineEdit.setFocus()
self.setFixedSize(861, 441)
def Handle_Buttons(self):
# ...
def onDataChanged(self, progress, downloaded, speed, remain):
self.progressBar.setValue(progress)
self.label_8.setText("Downloaded: " + downloaded)
self.label_38.setText("Speed: " + speed)
self.label_39.setText("Remaining Time: " + remain)
def Start(self):
directory = os.path.expanduser("~") + "\AppData\Local\Temp\pySmartDL"
if not os.path.exists(directory):
os.makedirs(directory)
params = {"url": self.lineEdit.text(),
"save_location": self.lineEdit_2.text()}
self.download.setParams(params)
self.download.setState(DownloadThread.Started)
self.download.start()
def Pause(self):
if self.pushButton_13.text() == "Pause Downloading":
self.download.setState(DownloadThread.Paused)
self.pushButton_13.setText("Resume Downloading")
elif self.pushButton_13.text() == "Resume Downloading":
self.download.setState(DownloadThread.Resume)
self.pushButton_13.setText("Pause Downloading")
def Stop(self):
self.download.setState(DownloadThread.Stopped)
self.progressBar.setValue(0)
def errorDownload(self):
QMessageBox.warning(self, "Download Error", "Download Failed")
def successfulDownload(self):
QMessageBox.information(self, "Download Completed", "Your Download is Completed")