Updating progress bar in Tkinter simultaneously while recording audio Python3 - multithreading

My goal for this simple program is to be able to show a progress bar of time the mic has been recording(range from 0 - 30 seconds). I am using the Tkinter library, Python 3.6, and PyAudio to record.
I finished writing the code for the individual parts(recording + updating progress bar) but I don't know how to change the code so that they happen at the same time(not one after the other). Also, I've realized that while I am recording, the GUI freezes(I can't click or resize buttons), I am also wondering about how to fix this issue. After doing some more research, I have a feeling that the answer lies with multi-threading or root.update() but I am not sure.
I am looking for a code example of recording audio while I simultaneously update the progress bar.
Here is an example:
from tkinter import *
from tkinter import ttk
import tkinter as tk
import pyaudio
import numpy as np
np.random.seed(123) # for reproducibility
import matplotlib
matplotlib.use('Agg') # No pictures displayed
class Test_GUI(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
root = self
label_title = Label(root, text="Progress Bar Recording Sample")
label_title.pack(fil="x")
topFrame = Frame(root)
topFrame.pack(side=TOP)
self.recordButton = Button(topFrame, text="Record", command=self.start)
self.recordButton.pack(side=LEFT)
self.progress = ttk.Progressbar(topFrame, orient="horizontal",
length=200, mode="determinate")
self.min = 0
self.maxMin = 0
self.progress.pack(side=RIGHT)
# recording attributes.
self.CHUNK = 1024
self.FORMAT = pyaudio.paInt16
self.CHANNELS = 2
self.RATE = 44100
self.RECORD_SECONDS = 15
self.WAVE_OUTPUT_FILENAME = "/Users/Sree/Desktop/PythonPrograms/audio_output_test.wav"
def start(self):
print("here")
self.progress["value"] = 0
self.maxMin = 30
self.progress["maximum"] = 30
'''
I need the following 2 lines to happen at the same time
the progress bar needs to update from 1-30 seconds(which is the time I am recording)
'''
self.record()
self.update_progress() #The progress bar is from 0 sec to 30 sec indicating what percentage of the time the mic has been recording for.
def update_progress(self):
self.min += 0.1
self.progress["value"] = self.min
if self.min < self.maxMin:
# update after 100 ms
self.after(100, self.update_progress)
def record(self):
p = pyaudio.PyAudio()
stream = p.open(format=self.FORMAT,
channels=self.CHANNELS,
rate=self.RATE,
input=True,
frames_per_buffer=self.CHUNK)
print("* recording")
frames = []
for i in range(0, int(self.RATE / self.CHUNK * self.RECORD_SECONDS)):
data = stream.read(self.CHUNK)
frames.append(data)
print("* done recording")
stream.stop_stream()
stream.close()
p.terminate()
wf = wave.open(self.WAVE_OUTPUT_FILENAME, 'wb')
wf.setnchannels(self.CHANNELS)
wf.setsampwidth(p.get_sample_size(self.FORMAT))
wf.setframerate(self.RATE)
wf.writeframes(b''.join(frames))
wf.close()
app = Test_GUI()
app.mainloop()

To prevent the GUI from freezing while recording, you need to launch the recording in a separate thread. To do so, you can use the threading module for instance and replace self.record() in your start method by:
record_thread = threading.Thread(target=self.record, daemon=True)
record_thread.start()

Related

Multi-threaded PyOpenCV display [duplicate]

I have to stitch the images captured from many (9) cameras. Initially, I tried to capture the frames from 2 cameras with rate 15 FPS. Then, I connected 4 cameras (I also used externally powered USB hub to provide enough power) but I could only see only one stream.
For testing, I used the following script:
import numpy as np
import cv2
import imutils
index = 0
arr = []
while True:
cap = cv2.VideoCapture(index)
if not cap.read()[0]:
break
else:
arr.append(index)
cap.release()
index += 1
video_captures = [cv2.VideoCapture(idx) for idx in arr]
while True:
# Capture frame-by-frame
frames = []
frames_preview = []
for i in arr:
# skip webcam capture
if i == 1: continue
ret, frame = video_captures[i].read()
if ret:
frames.append(frame)
small = cv2.resize(frame, (0, 0), fx=0.25, fy=0.25)
frames_preview.append(small)
for i, frame in enumerate(frames_preview):
cv2.imshow('Cam {}'.format(i), frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# When everything is done, release the capture
for video_capture in video_captures:
video_capture.release()
cv2.destroyAllWindows()
Is there any limit for the number of cameras? Does anyone know what is the right way to capture frames from multiple cameras?
To capture multiple streams with OpenCV, I recommend using threading which can improve performance by alleviating the heavy I/O operations to a separate thread. Since accessing the webcam/IP/RTSP stream using cv2.VideoCapture().read() is a blocking operation, our main program is stuck until the frame is read from the camera device. If you have multiple streams, this latency will definitely be visible. To remedy this problem, we can use threading to spawn another thread to handle retrieving the frames using a deque in parallel instead of relying on a single thread to obtain the frames in sequential order. Threading allows frames to be continuously read without impacting the performance of our main program. The idea to capture a single stream using threading and OpenCV, is from a previous answer in Python OpenCV multithreading streaming from camera.
But if you want to capture multiple streams, OpenCV alone is not enough. You can use OpenCV in combination with a GUI framework to stitch each image onto a nice display. I will use PyQt4 as the framework, qdarkstyle for GUI CSS, and imutils for OpenCV convenience functions.
Here is a very stripped down version of the camera GUI I currently use without the placeholder images, credential admin login page, and camera switching ability. I've kept the automatic camera reconnect feature incase the internet dies or the camera connection is lost. I only have 8 cameras as shown in the image above, but it is very simple to add in another camera and should not impact performance. This camera GUI currently performs at about ~60 FPS so it is real-time. You can easily rearrange the layout using PyQt layouts so feel free to modify the code! Remember to change the stream links!
from PyQt4 import QtCore, QtGui
import qdarkstyle
from threading import Thread
from collections import deque
from datetime import datetime
import time
import sys
import cv2
import imutils
class CameraWidget(QtGui.QWidget):
"""Independent camera feed
Uses threading to grab IP camera frames in the background
#param width - Width of the video frame
#param height - Height of the video frame
#param stream_link - IP/RTSP/Webcam link
#param aspect_ratio - Whether to maintain frame aspect ratio or force into fraame
"""
def __init__(self, width, height, stream_link=0, aspect_ratio=False, parent=None, deque_size=1):
super(CameraWidget, self).__init__(parent)
# Initialize deque used to store frames read from the stream
self.deque = deque(maxlen=deque_size)
# Slight offset is needed since PyQt layouts have a built in padding
# So add offset to counter the padding
self.offset = 16
self.screen_width = width - self.offset
self.screen_height = height - self.offset
self.maintain_aspect_ratio = aspect_ratio
self.camera_stream_link = stream_link
# Flag to check if camera is valid/working
self.online = False
self.capture = None
self.video_frame = QtGui.QLabel()
self.load_network_stream()
# Start background frame grabbing
self.get_frame_thread = Thread(target=self.get_frame, args=())
self.get_frame_thread.daemon = True
self.get_frame_thread.start()
# Periodically set video frame to display
self.timer = QtCore.QTimer()
self.timer.timeout.connect(self.set_frame)
self.timer.start(.5)
print('Started camera: {}'.format(self.camera_stream_link))
def load_network_stream(self):
"""Verifies stream link and open new stream if valid"""
def load_network_stream_thread():
if self.verify_network_stream(self.camera_stream_link):
self.capture = cv2.VideoCapture(self.camera_stream_link)
self.online = True
self.load_stream_thread = Thread(target=load_network_stream_thread, args=())
self.load_stream_thread.daemon = True
self.load_stream_thread.start()
def verify_network_stream(self, link):
"""Attempts to receive a frame from given link"""
cap = cv2.VideoCapture(link)
if not cap.isOpened():
return False
cap.release()
return True
def get_frame(self):
"""Reads frame, resizes, and converts image to pixmap"""
while True:
try:
if self.capture.isOpened() and self.online:
# Read next frame from stream and insert into deque
status, frame = self.capture.read()
if status:
self.deque.append(frame)
else:
self.capture.release()
self.online = False
else:
# Attempt to reconnect
print('attempting to reconnect', self.camera_stream_link)
self.load_network_stream()
self.spin(2)
self.spin(.001)
except AttributeError:
pass
def spin(self, seconds):
"""Pause for set amount of seconds, replaces time.sleep so program doesnt stall"""
time_end = time.time() + seconds
while time.time() < time_end:
QtGui.QApplication.processEvents()
def set_frame(self):
"""Sets pixmap image to video frame"""
if not self.online:
self.spin(1)
return
if self.deque and self.online:
# Grab latest frame
frame = self.deque[-1]
# Keep frame aspect ratio
if self.maintain_aspect_ratio:
self.frame = imutils.resize(frame, width=self.screen_width)
# Force resize
else:
self.frame = cv2.resize(frame, (self.screen_width, self.screen_height))
# Add timestamp to cameras
cv2.rectangle(self.frame, (self.screen_width-190,0), (self.screen_width,50), color=(0,0,0), thickness=-1)
cv2.putText(self.frame, datetime.now().strftime('%H:%M:%S'), (self.screen_width-185,37), cv2.FONT_HERSHEY_SIMPLEX, 1.2, (255,255,255), lineType=cv2.LINE_AA)
# Convert to pixmap and set to video frame
self.img = QtGui.QImage(self.frame, self.frame.shape[1], self.frame.shape[0], QtGui.QImage.Format_RGB888).rgbSwapped()
self.pix = QtGui.QPixmap.fromImage(self.img)
self.video_frame.setPixmap(self.pix)
def get_video_frame(self):
return self.video_frame
def exit_application():
"""Exit program event handler"""
sys.exit(1)
if __name__ == '__main__':
# Create main application window
app = QtGui.QApplication([])
app.setStyleSheet(qdarkstyle.load_stylesheet_pyqt())
app.setStyle(QtGui.QStyleFactory.create("Cleanlooks"))
mw = QtGui.QMainWindow()
mw.setWindowTitle('Camera GUI')
mw.setWindowFlags(QtCore.Qt.FramelessWindowHint)
cw = QtGui.QWidget()
ml = QtGui.QGridLayout()
cw.setLayout(ml)
mw.setCentralWidget(cw)
mw.showMaximized()
# Dynamically determine screen width/height
screen_width = QtGui.QApplication.desktop().screenGeometry().width()
screen_height = QtGui.QApplication.desktop().screenGeometry().height()
# Create Camera Widgets
username = 'Your camera username!'
password = 'Your camera password!'
# Stream links
camera0 = 'rtsp://{}:{}#192.168.1.43:554/cam/realmonitor?channel=1&subtype=0'.format(username, password)
camera1 = 'rtsp://{}:{}#192.168.1.45/axis-media/media.amp'.format(username, password)
camera2 = 'rtsp://{}:{}#192.168.1.47:554/cam/realmonitor?channel=1&subtype=0'.format(username, password)
camera3 = 'rtsp://{}:{}#192.168.1.40:554/cam/realmonitor?channel=1&subtype=0'.format(username, password)
camera4 = 'rtsp://{}:{}#192.168.1.44:554/cam/realmonitor?channel=1&subtype=0'.format(username, password)
camera5 = 'rtsp://{}:{}#192.168.1.42:554/cam/realmonitor?channel=1&subtype=0'.format(username, password)
camera6 = 'rtsp://{}:{}#192.168.1.46:554/cam/realmonitor?channel=1&subtype=0'.format(username, password)
camera7 = 'rtsp://{}:{}#192.168.1.41:554/cam/realmonitor?channel=1&subtype=0'.format(username, password)
# Create camera widgets
print('Creating Camera Widgets...')
zero = CameraWidget(screen_width//3, screen_height//3, camera0)
one = CameraWidget(screen_width//3, screen_height//3, camera1)
two = CameraWidget(screen_width//3, screen_height//3, camera2)
three = CameraWidget(screen_width//3, screen_height//3, camera3)
four = CameraWidget(screen_width//3, screen_height//3, camera4)
five = CameraWidget(screen_width//3, screen_height//3, camera5)
six = CameraWidget(screen_width//3, screen_height//3, camera6)
seven = CameraWidget(screen_width//3, screen_height//3, camera7)
# Add widgets to layout
print('Adding widgets to layout...')
ml.addWidget(zero.get_video_frame(),0,0,1,1)
ml.addWidget(one.get_video_frame(),0,1,1,1)
ml.addWidget(two.get_video_frame(),0,2,1,1)
ml.addWidget(three.get_video_frame(),1,0,1,1)
ml.addWidget(four.get_video_frame(),1,1,1,1)
ml.addWidget(five.get_video_frame(),1,2,1,1)
ml.addWidget(six.get_video_frame(),2,0,1,1)
ml.addWidget(seven.get_video_frame(),2,1,1,1)
print('Verifying camera credentials...')
mw.show()
QtGui.QShortcut(QtGui.QKeySequence('Ctrl+Q'), mw, exit_application)
if(sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()
Related camera/IP/RTSP, FPS, video, threading, and multiprocessing posts
Python OpenCV streaming from camera - multithreading, timestamps
Video Streaming from IP Camera in Python Using OpenCV cv2.VideoCapture
How to capture multiple camera streams with OpenCV?
OpenCV real time streaming video capture is slow. How to drop frames or get synced with real time?
Storing RTSP stream as video file with OpenCV VideoWriter
OpenCV video saving
Python OpenCV multiprocessing cv2.VideoCapture mp4

Separating Tkinters GUI and control of the application

1) What is my goal:
I’m creating an application that should read data every 60s from ModBusServer, append those data to Graphs and then when the app is closed save the data to excel file.
Site note:
The process of reading data from ModBusServer and appending them to graphs should start after a start button is pressed.
And end after stop button is pressed OR when ModBusServer sends a request to stop.
2) What I have so far:
I created the GUI without any major problems as a class “GUI_komora”.
Everything there works just fine.
3) What is the problem:
But now I’m lost on how to approach the “read data every 60 seconds”, and overall how to control the application.
I did some research on threading but still I’m confused how to implement this to my application.
I learned how to make functions run simultaneously in this tutorial.
And also how to call a function every few seconds using this question.
But none of them helped me to learn how to control the overall flow of the application.
If you could redirect me somewhere or tell me about a better approach I would be really glad.
Some of my code:
from tkinter import *
from GUI_komora import GUI
root = Tk()
my_gui = GUI(root) #my GUI class instance
#main loop
root.mainloop()
"""
How do I achieve something like this???
whenToEnd = False
while whenToEnd:
if step == "Inicialzation":
#inicializace the app
if step == "ReadData":
#read data every 60 seconds and append them to graphs
if step == "EndApp"
#save data to excel file and exit app
whenToEnd = True
"""
Here is an example of a loop that takes a decision (every 60 sec in your case) and pushes the outcome of the decision to tkinter GUI: https://github.com/shorisrip/PixelescoPy/blob/master/base.py
Parts:
main thread - starts tkinter window
control thread - reads some data and decides what to show in GUI
GUI class - has a method "add_image" which takes input an image and displays on GUI.(add_data_to_graph maybe in your case). This method is called everytime by the control thread.
Snippets:
def worker(guiObj, thread_dict):
# read some data and make decision here
time.sleep(60)
data_to_show = <outcome of the decision>
while some_logic:
pictureObj = Picture(chosen, timer)
pictureObj.set_control_thread_obj(thread_dict["control_thread"])
guiObj.set_picture_obj(pictureObj)
pictureObj.display(guiObj)
# do post display tasks
guiObj.quit_window()
# Keep GUI on main thread and everything else on thread
guiObj = GuiWindow()
thread_list = []
thread_dict = {}
thread_for_image_control = threading.Thread(target=worker, args=(guiObj,
thread_dict))
thread_dict["control_thread"] = thread_for_image_control
thread_list.append(thread_for_image_control)
thread_for_image_control.start()
guiObj.run_window_on_loop()
# thread_for_image_control.join()
Code for Picture class:
class Picture:
def __init__(self, path, timer):
self.path = path
self.timer = timer
self.control_thread_obj = None
def set_control_thread_obj(self, thread_obj):
self.control_thread_obj = thread_obj
def display(self, guiObj):
image_path = self.path
guiObj.add_image(image_path)
time.sleep(self.timer)
Code for GUI class
class GuiWindow():
def __init__(self):
self.picture_obj = None
self.root = Tk()
self.image_label = None
self.image = None
self.folder_path = None
self.timer = None
self.root.protocol("WM_DELETE_WINDOW", self.exit_button)
def add_image(self, image_path):
resized_img = self.resize(image_path)
image_obj = ImageTk.PhotoImage(resized_img)
image_label = Label(self.root, image=image_obj,
height=resized_img.height,
width=resized_img.width)
self.image = image_obj # DO NOT REMOVE - Garbage collector error
if self.image_label is not None:
self.remove_image()
image_label.grid(row=0, column=0, columnspan=3)
self.image_label = image_label
Here based on my control loop thread I am changing image (in your case graph data) of the tkinter GUI.
Does this help?

faster FFT issue in Python using pyqtgraph

As you can see below I have a program that uses pyqtgraph to display a spectrum of two signals on some noise using numpy. It may well be I am pushing the limits here. I am using a sample rate of 300000 to put some pressure on things. None-the-less I saw no improvements in splitting up this app using multiprocess or threading from putting the code lines in the signal() function from having it all under the update() function. I also tried pyfftw, which showed no improvement when substituting that in for np.fft. I show 1 core always at 100% so I suspect multiprocess (or threading) may not really be working the way I expect it either. Depending on your computer at aa rate 300000 it updates and pauses and updates and pauses. I would like to hit something like 2400000 smoothly without the pauses between updates. Anyone know how I can speed this up please?
# -*- coding: utf-8 -*-
from pyqtgraph.Qt import QtGui, QtCore
import numpy as np
from threading import Thread
from queue import Queue
from numpy import arange, sin, cos, pi
from scipy.fftpack import fft, rfft
import pyqtgraph as pg
import sys
import multiprocessing
class Plot2D():
def __init__(self):
self.traces = dict()
#QtGui.QApplication.setGraphicsSystem('raster')
self.app = QtGui.QApplication([])
#mw = QtGui.QMainWindow()
#mw.resize(800,800)
self.win = pg.GraphicsWindow(title="Basic plotting examples")
self.win.resize(1000,600)
self.win.setWindowTitle('pyqtgraph example: Plotting')
# Enable antialiasing for prettier plots
pg.setConfigOptions(antialias=True)
self.canvas = self.win.addPlot(title="Pytelemetry")
self.canvas.setYRange(-10, 100, padding=0)
def start(self):
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()
def trace(self,name,dataset_x,dataset_y):
if name in self.traces:
self.traces[name].setData(dataset_x,dataset_y)
else:
self.traces[name] = self.canvas.plot(pen='y')
## Start Qt event loop unless running in interactive mode or using pyside.
if __name__ == '__main__':
p = Plot2D()
i = 0
def signal():
rate = 300000 # sampling rate
t = np.arange(0, 10, 1/rate)
sig = np.sin(2000*np.pi*4*t) + np.sin(2000*np.pi*7*t) + np.random.randn(len(t))*0.02 #4k + 7k tone + noise
return sig
def update():
rate = 300000 # sampling rate
z = 20*np.log10(np.abs(np.fft.rfft(signal()))) #rfft trims imag and leaves real values
f = np.linspace(0, rate/2, len(z))
p.trace("Amplitude", f, z)
timer = QtCore.QTimer()
timer.timeout.connect(lambda: update())
timer.start(10)
p.start()
t1 = multiprocessing.Process(target=signal)
t1.start

Timed Availability of controls in tkinter GUI

I'm working on a program that will stop users from changing a label after a random amount of time. There are two buttons, start and next, when the user presses start the start button is destroyed but is supposed to come back after a randomly selected amount of time. I tried to have the start button trigger a flag that starts a timer. When the timer reaches a certain value (count_to+1) the flag should go to zero, the start button should reappear, and the label should read end. The flag never seems to switch and the timer never initiates though. Can anyone tell me what I did wrong? and maybe point me towards a solution? Hear is the code:
import sys
from tkinter import *
import random
import time
mGui = Tk()
mGui.geometry('450x450+200+200')
mGui.title('Letters')
stored = ['A', 'b', 'c', 'd']
count_down = [10,20,30,40,50,60]
global count_to
global countFlag
count_to = IntVar()
countFlag = 0
Sec = 0
def run_counter():
count_to = random.choice(count_down)
while countFlag == 1:
Sec+=1
print(sec)
if Sec == count_to+1:
countFlag = 0
newbutton.destroy()
startbutton.grid(row=2,column=1)
phrase.configure(text='End')
return
def change_phrase():
fish = StringVar()
fish = random.choice(stored)
stored.remove(fish)
phrase.configure(text=fish)
#to help with debug
print(countFlag)
print(Sec)
print(count_to)
return
def start_count():
countFlag = True
count_to = random.choice(count_down)
print(countFlag)
startbutton.destroy()
run_counter
return
phrase = Label(mGui,text='Letter',fg='red',bg='blue')
phrase.grid(row=0,column=0, sticky=S,columnspan=2)
startbutton =Button(mGui, text='start',fg='black',bg='green',command=start_count)
startbutton.grid(row=2,column=1)
newbutton = Button(mGui,text='NEXT',fg='black',bg='red',command=change_phrase)
newbutton.grid(row=2,column=0)
#mEntry = Entry(mGui,textvariable=ment)
#mEntry.grid(row=3,column=0)
mGui.mainloop()
Tkinter programming becomes much less messy and confusing once you learn to use classes. Use Tkinter's after() method to call a function every "x" amount of time until the allotted time has elapsed.
import random
import sys
if sys.version_info[0] < 3:
import Tkinter as tk ## Python 2.x
else:
import tkinter as tk ## Python 3.x
class ButtonDisappear():
def __init__(self, root):
self.root=root
self.startbutton=tk.Button(root, text='disappear', fg='black',
bg='green', command=self.disappear)
self.startbutton.grid(row=2,column=1)
self.lab=tk.Label(self.root, text="", bg="lightblue")
def disappear(self):
## remove button
self.startbutton.grid_forget()
## grid label for time
self.lab.grid(row=0, column=0)
## "random" number
self.stop_time=random.choice([1, 2, 3, 4, 5])*1000
self.elapsed=0
self.root.after(100, self.time_it)
def time_it(self):
self.elapsed += 100
## function calls itself until time has finished
if self.elapsed < self.stop_time:
self.lab["text"]="%d of %d" % (self.elapsed, self.stop_time)
self.root.after(100, self.time_it)
## time elapsed so remove label and restore button
else:
self.lab.grid_forget()
self.startbutton.grid(row=2,column=1)
m_gui = tk.Tk()
m_gui.geometry('450x450+200+200')
m_gui.title('Letters')
B=ButtonDisappear(m_gui)
m_gui.mainloop()

Looping QProgressBar gives error >> QObject::installEventFilter: Cannot filter events for objects in a different thread

This question seems to have been asked many times in many different forms but I haven't managed to find one with a -relevant to my code- solution.
When I run the program it shows
QObject::installEventFilter: Cannot filter events for objects in a different thread.
Despite this the code works initially but after a while it bombs and python gives an error saying its stopped working.
My code is as follows:
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from xml.etree import ElementTree as ET
import os , time
class LayoutCreator(QDialog):
def __init__(self , parent=None):
super(LayoutCreator, self).__init__(parent)
self.Cameras_Update()
def Cameras_Update( self ): # Get all shots with camera plots and add them to the cameras_tree
busyBar = sqrl_QtTools.BusyBar( text = "Gathering Camera Data" ) # Looping progress bar
busyBar.start()
# loop through folder structure storing data
busyBar.Kill() # Close looping progress bar
class BusyBar(QThread): # Looping progress bar
def __init__(self, text = "" ):
QThread.__init__(self)
self.text = text
self.stop = False
def run( self ):
self.proBar = QProgressBar()
self.proBar.setWindowFlags(Qt.WindowStaysOnTopHint | Qt.SplashScreen )
self.proBar.setMinimum( 0 )
self.proBar.setMaximum( 100 )
self.proBar.setTextVisible( True )
self.proBar.setFormat( self.text )
self.proBar.setValue( 0 )
self.proBar.setFixedSize( 500 , 50 )
self.proBar.setAlignment(Qt.AlignCenter)
self.proBar.show()
while not self.stop: # keep looping while self is visible
# Loop sending mail
for i in range(100):
progress = self.proBar.value()
progress = progress + 1
self.proBar.setValue( progress )
time.sleep(0.05)
self.proBar.setValue( 0 )
self.proBar.hide()
def Kill(self):
self.stop = True
You can't create or access any QWidget outside the main thread.
You can use signals and slots to indirectly access widgets from the other thread:
from PyQt4.QtCore import *
from PyQt4.QtGui import *
import sys, time
class BusyBar(QThread): # Looping progress bar
# create the signal that the thread will emit
changeValue = pyqtSignal(int)
def __init__(self, text = "" ):
QThread.__init__(self)
self.text = text
self.stop = False
self.proBar = QProgressBar()
self.proBar.setWindowFlags(Qt.WindowStaysOnTopHint | Qt.SplashScreen )
self.proBar.setRange( 0, 100 )
self.proBar.setTextVisible( True )
self.proBar.setFormat( self.text )
self.proBar.setValue( 0 )
self.proBar.setFixedSize( 500 , 50 )
self.proBar.setAlignment(Qt.AlignCenter)
self.proBar.show()
self.changeValue.connect(self.proBar.setValue, Qt.QueuedConnection)
# Make the Busybar delete itself and the QProgressBar when done
self.finished.connect(self.onFinished)
def run( self ):
while not self.stop: # keep looping while self is visible
# Loop sending mail
for i in range(100):
# emit the signal instead of calling setValue
# also we can't read the progress bar value from the thread
self.changeValue.emit( i )
time.sleep(0.05)
self.changeValue.emit( 0 )
def onFinished(self):
self.proBar.deleteLater()
self.deleteLater()
def Kill(self):
self.stop = True
class LayoutCreator(QDialog):
def __init__(self , parent=None):
super(LayoutCreator, self).__init__(parent)
self.Cameras_Update()
def Cameras_Update( self ):
# Looping progress bar
self.busyBar = BusyBar( text = "Gathering Camera Data" )
self.busyBar.start()
# loop through folder structure storing data
# Simulate async activity that doesn't block the GUI event loop
# (if you do everything without giving control back to
# the event loop, you have to call QApplication.processEvents()
# to allow the GUI to update itself )
QTimer.singleShot(10000, self.stopBar)
def stopBar(self):
self.busyBar.Kill() # Close looping progress bar
app = QApplication(sys.argv)
win = LayoutCreator()
win.show();
sys.exit(app.exec_())
Or
If you only want a busy indicator, you can simply set both the minimum and maximum of the QProgressBar to 0, and you won't need a thread, as indicated in the documentation.

Resources