How can I open multiple popups in succession? - python

I'm currently working on an application written in Kivy in python.
I have 2 multiprocessing.Processes running:
One is a process for the RFID-reader, I open a popup that lets me choose a screen to go to.
The second process is for Serial communication.
I use queues to communicate with my main thread, and that works great. The problem I'm facing at the moment is I need to introduce a learning process to my Kivy program.
I have 4 screens which I call
mainscreen
adminscreen
managementscreen
initialscreen <- This screen runs only once, once it's set, the screen won't be accessable anymore.
This is the function that gets called when I push the button inside the initialscreen:
def startLearningProcess(self, lockeramount):
self.lockeramount = int(lockeramount)
with Database(self.databasepath) as db:
db.truncate_table('LOCKERS')
# resetting primary key sequence to make the ids start from 1
db.reset_primary_key_sequence('LOCKERS')
for locker in range(int(lockeramount)):
# use a with statement to automatically close the db after the operations
with Database(self.databasepath) as db:
# inserting the given amount lockers to the database as rows.
db.insertLockerRow(locker+1,'Pieter')
if I add the following to the function, 5 popups get opened all at once:
while self.lockeramount != 0:
popup = Popup(title='Test popup', auto_dismiss=True, content=Label(text='Hello world'), size_hint=(None, None), size=(400, 400))
popup.open()
self.lockeramount -= 1
When I input the number 5 into my interface, I want to have 5 popups to open up for me one by one. How can I make it so when I push a button I open up 1 popup, instead of all 5 at once? I apologize for my grammar, english is not my first language.
EDIT:
while John's answer worked perfectly, I was looking for another solution that did not use threading. I solved it by doing the following:
In my class InitialScreen(Screen): I added 2 variables, a bool that starts out with False (booleanUp) and a int variable that starts at 0 (lockeramount).
When I enter my def startLearningProcess I set the lockeramount variable to the number I input into my screen. I added an interval to the startLearningProcess function: Clock.schedule_interval(lambda dt: self.schedule_popups(), 1). I then added the following functions:
def close_popup(self, instance):
self.booleanUp = False
def schedule_popups(self):
if self.lockeramount > 0 and not self.booleanUp:
print(f'opening up popup {self.lockeramount}')
popup = Popup(title='MyPopup', content=Label(text='Abba ' + str(self.lockeramount)), size_hint=(0.5, 0.5))
popup.bind(on_dismiss=self.close_popup)
self.lockeramount -= 1
self.booleanUp = True
popup.open()
else:
print('not opening another popup')
When I open a new popup, I set the boolean to true, so that with the next interval it won't open another interval. I made an on_dismiss event that resets the variable back to False and bound it to my popup.

You can use a Queue to make the Popups wait. Define a custom Popup that accepts a Queue in its __init__() method, and sends something (could even be None) to the Queue when it is dismissed. And your loop can use the Queue to wait for the Popups to be dismissed.
Here is a custom Popup that uses a Queue:
class MyPopup(Popup):
queue = ObjectProperty(None)
def dismiss(self, *_args, **kwargs):
super(MyPopup, self).dismiss(*_args, **kwargs)
if self.queue:
self.queue.put(None)
For this to work, you must run it in another thread. Otherwise, waiting for the Queue on the main thread will never end, because holding the main thread will prevent the Popup from dismissing. Here is some code that shows 5 Popups in succession, one at a time:
def doit(self):
threading.Thread(target=self.popup_thread).start()
def popup_thread(self):
self.queue = Queue()
for i in range(5):
Clock.schedule_once(self.show_popup)
self.queue.get()
def show_popup(self, dt):
popup = MyPopup(title='MyPopup', content=Label(text='Abba ' + str(dt)), size_hint=(0.5, 0.5), queue=self.queue)
popup.open()
To start the Popups, just call the doit() method, probably as an action associated with a Button.

Related

Perform modifications in the scene and update custom window from a QThread in Maya

Context
I'm creating a PySide2 tool running in Maya. The tool is executing a lot of long tasks, some modifying the scene (cleaning tasks), some creating files (exporting tasks).
Because this is a long task, I'd like to display feedback (progress bar) while it's running.
Problems
Unfortunately, so far, the whole UI does not seem to be updated during the executing.
Also, because I had odd behaviors (Maya freezing forever) in the real code, I'm guessing this is not a safe use of threads.
Example code
Here is a simplified bit of code showing where I am so far. Is this the right way to use QThread? I'm from a CG Artist background, not a professional programmer, so I'm probably misusing or misunderstanding the concepts I'm trying to use (threads, PySide...)
import time
from PySide2.QtGui import *
from PySide2.QtCore import *
from PySide2.QtWidgets import *
import maya.cmds as cmds
class Application(object):
def __init__(self):
self.view = View(self)
def do_something(self, callback):
start = int(cmds.playbackOptions(q=True, min=True))
end = int(cmds.playbackOptions(q=True, max=True))
# First operation
for frame in xrange(start, end + 1):
cmds.currentTime(frame, edit=True)
# Export ...
callback(33)
time.sleep(1)
# Second operation
for frame in xrange(start, end + 1):
cmds.currentTime(frame, edit=True)
# Export ...
callback(66)
time.sleep(1)
# Third operation
for frame in xrange(start, end + 1):
cmds.currentTime(frame, edit=True)
# Export ...
callback(100)
time.sleep(1)
class View(QWidget):
def __init__(self, controller):
super(View, self).__init__()
self.controller = controller
self.thread = None
self.setLayout(QVBoxLayout())
self.progress = QLabel()
self.layout().addWidget(self.progress)
self.button = QPushButton('Do something')
self.layout().addWidget(self.button)
self.button.clicked.connect(self.do_something)
self.show()
def do_something(self):
self.thread = DoSomethingThread(self.controller)
self.thread.updated.connect(lambda progress: self.progress.setText(str(progress) + '%'))
self.thread.run()
class DoSomethingThread(QThread):
completed = Signal()
updated = Signal(int)
def __init__(self, controller, parent=None):
super(DoSomethingThread, self).__init__(parent)
self.controller = controller
def run(self):
self.controller.do_something(self.update_progress)
self.completed.emit()
def update_progress(self, progress):
self.updated.emit(int(progress))
app = Application()
Threads are difficult to use correctly in Maya Python (you can see this from the number of questions listed here)
Generally there are two hard rules to observe:
all work that touches the Maya scene (say selecting or moving an object) has to happen in the main thread
all work that touches Maya GUI also has to happen in the main thread.
"main thread" here is the thread you get when you run a script from the listener, not on you're creating for yourself
This obviously makes a lot of things hard to do. Generally a solution will involve the a controlling operation running on the main thread while other work that does not touch Maya GUI or scene objects is happening elsewhere. A thread-safe container (like a python Queue can be used to move completed work out of a worker thread into a place where the main thread can get to it safely, or you can use QT signals to safely trigger work in the main thread.... all of which is a bit tricky if you're not far along in your programming career.
The good news is -- if all the work you want to do in Maya is in the scene you aren't losing much by not having threads. Unless the work is basically non-Maya work -- like grabbing data of the web using an HTTP request, or writing a non-Maya file to disk, or something else that does not deal with Maya-specific data -- adding threads won't get you any additional performance. It looks like your example is advancing the time line, doing work, and then trying to update a PySide GUI. For that you don't really need threads at all (you also don't need a separate QApplication -- Maya is already a QApplication)
Here's a really dumb example.
from PySide2.QtCore import *
from PySide2.QtGui import *
from PySide2.QtWidgets import *
import maya.cmds as cmds
class DumbWindow(QWidget):
def __init__(self):
super(DumbWindow, self).__init__()
#get the maya app
maya_app = QCoreApplication.instance()
# find the main window for a parent
for widget in maya_app.topLevelWidgets():
if 'TmainWindow' in widget.metaObject().className():
self.setParent(widget)
break
self.setWindowTitle("Hello World")
self.setWindowFlags(Qt.Window)
self.layout = QVBoxLayout()
self.setLayout(self.layout)
start_button = QPushButton('Start', self)
stop_button = QPushButton('Stop', self)
self.layout.addWidget(start_button)
self.layout.addWidget(stop_button)
self.should_cancel = False
self.operation = None
self.job = None
# hook up the buttons
start_button.clicked.connect(self.start)
stop_button.clicked.connect(self.stop)
def start(self):
'''kicks off the work in 'this_is_the_work'''
self.operation = self.this_is_the_work()
self.should_cancel = False
self.job = cmds.scriptJob(ie=self.this_makes_it_tick)
def stop(self):
''' cancel before the next step'''
self.should_cancel = True
def this_is_the_work(self):
print "--- started ---"
for frame in range(100):
cmds.currentTime(frame, edit=True)
yield "advanced", frame
print "--- DONE ----"
def bail(self):
self.operation = None
def kill_my_job():
cmds.scriptJob(k=self.job)
print "job killed"
cmds.scriptJob(ie = kill_my_job, runOnce=True)
def this_makes_it_tick(self):
'''
this is called whenever Maya is idle and thie
'''
# not started yet
if not self.operation:
return
# user asked to cancel
if self.should_cancel:
print "cancelling"
self.bail()
return
try:
# do one step. Here's where you can update the
# gui if you need to
result = next(self.operation)
print result
# example GUI update
self.setWindowTitle("frame %i" % result[-1])
except StopIteration:
# no more stpes, we're done
print "completed"
self.bail()
except Exception as e:
print "oops", e
self.bail()
test = DumbWindow()
test.show()
Hitting start creates a maya scriptJob that will try to run whatever operation is in the function called this_is_the_work(). It will run to the next yield statement and then check to make sure the user hasn't asked to cancel the job. Between yields Maya will be busy (just as it would if you entered some lines in the listener) but if you're interacting with Maya when a yield comes up, the script will wait for you instead. This allows for safe user interaction without a separate thread, though of course it's not as smooth as a completely separate thread either.
You'll notice that this kicks off a second scriptJob in the bail() method -- that's because a scriptJob can't kill itself, so we create another one which will run during the next idle event and kill the one we don't want.
This trick is basically how most of the Maya's MEL-based UI works under the hood -- if you run cmds.scriptJob(lj=True) in the listener you'll usually see a lot of scriptJobs that represent UI elements keeping track of things.

how to set timer properly in wxpython

My problem is: how to use Timer properly? Sometimes i missed the data.
part of the code:
import wx
import serial
store=[]
class all(wx.panel):
def __init__(self, parent):
…
self.SetSizer( bSizer17 )
self.Layout()
self.timer1 = wx.Timer()
self.timer1.SetOwner( self, 1 )
self.timer2 = wx.Timer()
self.timer2.SetOwner( self, 2 )
self.timer2.Start( 500 ) ### running when app begins
self.timer3 = wx.Timer()
self.timer3.SetOwner( self, 3 )
self.timer3.Start( 401) ### running when app begins
def timer1(self,event):
if self.timer1.IsRunning():
self.timer1.Stop()
else:
self.timer1.Start( 100 )
def timer2(self, event):
event.Skip()
def timer3(self, event):
event.Skip()
def timer1_plot(self, event)
…
plt.plot(x,y)
def timer2_store( self, event ):
for line in ser:
store.append(line)
with open("C:\\Users\\Desktop\\saved_data.txt","a") as f:
for line in store:
f.writelines(str(line)+ "\n")
def timer3_del( self, event ):
del store[:]
I have coming data from serial port per second. I use data for plotting and saving background(independent from plotting). For this i used wxTimer.
I have 3 buttons(bind with timers), 2 of them are hidden(2 timers run automatically).
1.button is for plotting. When i press, timer run.
2.button is for storing and writing to .txt file and delete from the list
3.button is for deleting the stored data in the list
self.timer1.Start( 100 ) # running when button press
self.timer2.Start( 500 ) # running when app begins
self.timer3.Start( 401) # running when app begins
range between timer is good? Should i run 2 of them automatically? What can you suggest? Any help would be appreciated.
This is pretty old, so you may have already found a solution... but here is my advice.
My project monitors 2 serial ports, one has a scale that sends a data packet every second, the other has a barcode scanner - very random when data arrives.
I use 1 thread per serial port, that just timer.sleep's waiting for serial input, and stores it in a shared object (1 object per thread). Then I have one timer that updates the state machine, it looks at each shared object, and updates the GUI accordingly.
If this was my project, I would suggest the following changes:
Don't call timer event .Skip() nothing else should be chained after your callback.
Create a thread for each serial port or other async communication source.
Be sure to mark the threads as daemon's or explicitly kill them when shutting down.
Use one timer that updates everything, use the lowest frequency that gives your users acceptable response.
Good luck.

How can I thread this code so late in development?

I have been making a GUI for a genetic algorithm I am working on and I made the mistake of leaving the threading so late simply because I did not (and still don't) know how to do it. So essentially when the start button is clicked the function 'run' starts the whole infinite loop process which actually happens in generation_loop. Each generation the loop checks to see if it should still be running. The idea is that if the stop or pause button has been clicked it will stop looping (with the stop button all the data is cleared with the pause button it remains and the unpause button just sets running to True and calls generation_loop)
So I need to work out a way to make my GUI responsive while generation_loop is running. Here is my code, I tried to minimise it but I am unsure what is important information for threading:
class Window(main_window, QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
main_window.__init__(self)
self.setupUi(self)
self.scene = QGraphicsScene()
self.im_view.setScene(self.scene)
self.setWindowTitle('Fantasy Generator')
self.running = False
self.first_run = True
self.im = Image.new('RGBA', (400, 400), (0, 0, 0, 255))
self.saved_gens = deque([('A', self.im, self.im, self.im)])
self.set_save_amount(self.sb_saveamt.value())
self.population = []
self.btn_exit.clicked.connect(self.close)
self.actionQuit.triggered.connect(self.close)
self.btn_pauser.clicked.connect(self.pause_button)
self.sb_saveamt.valueChanged[int].connect(self.set_save_amount)
self.btn_restart.clicked.connect(self.start_button)
self.btn_loadimage.clicked.connect(self.get_image)
self.actionLoad_Image.triggered.connect(self.get_image)
self.gen_sldr.valueChanged[int].connect(self.display_gen)
self.cb_display.currentIndexChanged.connect(self.change_quality)
self.has_image = True
self.display_gen(0)
def get_image(self):
pass
# To save you time I removed the code here. It just sets self.im using a file dialog basically
def set_save_amount(self, amt):
if amt == -1:
self.saved_gens = deque(self.saved_gens)
else:
self.saved_gens = deque(self.saved_gens, amt + 1)
def pause_button(self):
if self.first_run:
self.run()
elif self.running:
self.running = False
self.btn_pauser.setText('Resume Execution')
# pause stuff goes here
else:
self.running = True
self.btn_pauser.setText('Pause Execution')
self.generation_loop()
# resume from pause stuff goes here
def start_button(self):
if self.first_run:
self.run()
else:
self.end()
# The run function should start the actual process
def run(self):
self.btn_restart.setText('End')
self.btn_pauser.setText('Pause Execution')
self.first_run = False
self.running = True
settings = dict(ind_per_gen=self.sb_ipg.value(), shapes_per_im=self.sb_spi.value(),
complexity=self.sb_complexity.value(), mut_rate=self.sb_mutation.value(),
cross_chance=self.sb_cross.value(), seed=self.sb_seed.value())
self.population = Population(self.im, **settings)
self.generation_loop()
# This is the loop I want to be able to exit out of using buttons
def generation_loop(self):
while self.running:
if self.first_run:
break
self.add_generation_data(self.population.next_gen())
def end(self):
self.btn_restart.setText('Start')
self.btn_pauser.setText('Start Execution')
self.first_run = True
self.running = False
self.saved_gens = deque([('A', self.im, self.im, self.im)])
self.set_save_amount()
self.display_gen(0)
def add_generation_data(self, data):
self.saved_gens.append(data)
self.gen_sldr.setMaximum(len(self.saved_gens) - 1)
self.gen_sldr.setValue(len(self.saved_gens) - 1)
self.display_gen(data[0] + 1)
def change_quality(self):
self.display_gen(self.gen_sldr.value())
def resizeEvent(self, e):
if self.has_image:
self.im_view.fitInView(QRectF(0, 0, self.width, self.height), Qt.KeepAspectRatio)
self.scene.update()
def display_image(self, image):
self.scene.clear()
if image.mode != 'RGBA':
image = image.convert('RGBA')
self.width, self.height = image.size
qim = ImageQt.ImageQt(image)
pixmap = QPixmap.fromImage(qim)
self.scene.addPixmap(pixmap)
self.im_view.fitInView(QRectF(0, 0, self.width, self.height), Qt.KeepAspectRatio)
self.scene.update()
def display_gen(self, index):
self.lcd_cur_gen.display(self.saved_gens[index][0])
if self.cb_display.currentIndex() == 0:
self.display_image(self.saved_gens[index][1])
elif self.cb_display.currentIndex() == 1:
self.display_image(self.saved_gens[index][2])
else:
self.display_image(self.saved_gens[index][3])
if __name__ == '__main__':
app = QApplication(sys.argv)
w = Window()
w.show()
sys.exit(app.exec_())
EDIT: I also just found at that I can't even change the graphics view from within the generation_loop but it works and changes if I limit the loop
In order to move your long running code to a thread, you need to first identify which parts of the long running code interact with the GUI and which parts don't. The key reason for this is that interacting with the GUI from a secondary thread is forbidden, and will lead to segfaults.
It looks like self.population.next_gen() is the long running bit of the code and doesn't interact with the GUI (although what this does is not provided so I can't be sure) while self.add_generation_data(...) updates the GUI which should be reasonably fast.
As such, this makes it reasonably simple to separate, which I'll show below.
Now, about threads. Python provides threads through the threading module (as the other answers show), however these not are recommended for use with a PyQt application if you want your thread to have any relation to the GUI (see here). PyQt also provides threading via the QThread object, which integrates support for sending and receiving Qt signals (which are thread safe). In short, the QThread has a separate event loop, and processes signals received asynchronously to the main thread, thus leaving the event loop in the main thread to process GUI events (like button clicks).
Typically you create a new class that inherits from QObject, instantiate it and move it to a QThread. Slots (aka methods) in the object that are triggered by a signal emission, then run in the thread.
So you'll want to do something like this
class MyWorker(QObject):
done = pyqtSignal(object) # you may need to update "object" to the type returned by Population.next_gen()
def __init__(self, settings):
# create the population object with whatever settings you need
# Note that this method runs in the parent thread as you have
# yet to move the object to a new thread. It shouldn't cause any
# problems, but may depend on what the Population class is/does.
# TODO: I've removed the reference to an image here...
#it may or may not be thread safe. I can't tell from your code.
self.population = Population(..., settings)
#pyqtSlot()
def next_gen(self):
new_gen = self.population.next_gen()
self.done.emit(new_gen)
class Window(....):
make_next_generation = pyqtSignal()
....
def run(self):
self.btn_restart.setText('End')
self.btn_pauser.setText('Pause Execution')
self.first_run = False
self.running = True
settings = dict(ind_per_gen=self.sb_ipg.value(), shapes_per_im=self.sb_spi.value(),
complexity=self.sb_complexity.value(), mut_rate=self.sb_mutation.value(),
cross_chance=self.sb_cross.value(), seed=self.sb_seed.value())
self.setupThread(settings)
def setupThread(self, settings):
self.thread = QThread()
self.worker = MyWorker(settings)
self.worker.moveToThread(self.thread)
# connect a signal in the main thread, to a slot in the worker.
# whenever you emit the signal, a new generation will be generated
# in the worker thread
self.make_next_generation.connect(self.worker.next_gen)
# connect the signal from the worker, to a slot in the main thread.
# This allows you to update the GUI when a generation has been made
self.worker.done.connect(self.process_generation)
# Start thread
self.thread.start()
# emit the signal to start the process!
self.make_next_generation.emit()
def process_generation(new_gen):
# run the GUI side of the code
# ignore the new generation if the "end" button was clicked
if not self.first_run:
self.add_generation_data(new_gen)
if self.running:
# make another generation in the thread!
self.make_next_generation.emit()
def pause_button(self):
if self.first_run:
self.run()
elif self.running:
self.running = False
self.btn_pauser.setText('Resume Execution')
# pause stuff goes here
else:
self.running = True
self.btn_pauser.setText('Pause Execution')
# make another generation in the thread!
self.make_next_generation.emit()
Things to note:
I haven't included all of your code in my answer. Merge as appropriate.
I'm unsure what self.im is. It's passed to Population so there might be some thread unsafe behaviour in your code that I can't see. I've left it to you to fix
I'm familiar with PyQt4, not PyQt5, so there is a possibility some things I've done don't work quite right. It should be easy for you to work out what to change from any error messages that are raised.
It's a bit messy recreating the thread and worker each time it is started from scratch. You might want to consider moving the instantiation of Population to a method in the worker (one that isn't __init__ and invoking it each time you want to start from scratch (in the same way we trigger a new generation). This would allow you to move pretty much all of setupThread to the Window.__init__ method and then when the start button was clicked, you'd just emit a signal to recreate Population followed by one to make the first generation.
You can use Threading events here.
from threading import Thread, Event
Once you detect the button click,
class MyThread(Thread):
def __init__(self, the_function, <any input param you want to provide>):
Thread.__init__(self)
self.stop_event = Event()
self.exec_func = the_function
def set_stop_flag(self, value):
if value:
self.stop_event.set()
else:
self.stop_event.clear()
def run(self):
while True:
try:
if not self.stop_event.is_set()
self.exec_func()
else:
break # once the event is set, you can break which will kill this thread.
# To stop busy waiting you can make this thread sleep for some seconds after each cycle.
import time
time.sleep(10) # 10 seconds wait before the next cycle.
except Exception, excep:
print "Got exception > ", str(excep)
Now in your code you embed this code piece and keep a reference for this thread.
Let's say
self.my_thread = MyThread(self.function_to_perform, <blabla>)
self.my_thread.setDaemon(True) # So that you don't have to worry about it when the Main process dies!
self.my_thread.start()
Now once you get a STOP button click event you call
self.my_thread.set_stop_flag(True) # Bingo! Your thread shall quit.

how to create an extra infinite loop in an gtk/python program?

New at this, I know that the program is supposed to enter an infinite loop when you call gtk.main. The loop in gtk.main will break when self.quit is called.
But I need another loop active that would check a log file for changes, and keep updating the changes, into a gtk.Textbuffer, to be shown in a gtk.Textbox. So where can i add this loop in the following code.
class MessageBox:
def __init__(self):
builder = gtk.Builder()
builder.add_from_file("mbx.glade")
self.window = builder.get_object("window")
dic = { "on_buttonSend_clicked" : self.sendmsg,
"on_entry_activate" : self.sendmsg,
"on_buttonWhois_clicked" : self.sendwhois,
"on_buttonIdleTime_clicked" : self.sendidletime,
"on_window_destroy" : self.exitfunc}
builder.connect_signals(dic)
self.entry = builder.get_object("entry")
self.display = builder.get_object("display")
self.displaybuff=self.display.get_buffer()
def exitfunc(self, widget):
def sendmsg(self, widget):
def sendwhois (self, widget):
def sendidletime (self, widget):
if __name__ == "__main__":
msgbox = MessageBox()
msgbox.window.show()
gtk.main()
Only exists one mainloop in gtk at the same time, that is, gtk.main enters into a new level and gtk.main_quit exists from that level.
Usually, what you'd need to do is is a create worker thread that keeps working in parallel and updates the widgets when it gets new information. However, according to the problem you describe, I believe you just need to monitor a file for changes with gio as follows:
monitor = gio.File(filename).monitor()
monitor.connect('changed', file_changed_cb)
Whenever the file changes a signal will be emitted and the file_changed_cb callback method will be executed. There you can update the widgets to match the new information from the file that is being monitored. However, note that if your callback method takes too long to complete, your application might look unresponive, then you have to go for the thread strategy.

How to start a "drawing loop" in PyQt?

Often times when we're drawing a GUI, we want our GUI to update based on the data changing in our program. At the start of the program, let's say I've drawn my GUI based on my initial data. That data will be changing constantly, so how can I redraw my GUI constantly?
The best way that I have found to do this is to run your core program in a QThread and use signals to communicate with your gui. Here is an example where I update a progress dialog as my main program does some stuff.
Here is a code excerpt from a project that I was working on. The basic idea is that I am adding a number of files to a library object and updating the progress as the files are added.
The action is started by the Library class. The tread that does the actual work is in the AddFilesThread.
Let me know if this is helpful. If you need I can try to put together a working example instead of a code excerpt.
If you want to see the full code that I used go here: hystrix_library.py. The diaglog class that I used is in that file. I can't say that this is necessarily the best way to do things, but it works well and is fairly easy to read.
class Library(QtCore.QObject):
"""
This class is used to store information on the libraries.
"""
def __init__(self):
QtCore.QObject.__init__(self)
def importUrls(self, url_list):
# Create a progress dialog
self.ui_progress = AddUrlsProgressDialog()
self.ui_progress.show()
self.ui_progress.raise_()
# Create the add files thread object.
self.add_files_thread = AddFilesThread()
# Connect the thread to the dialog.
self.connect(self.add_files_thread
,QtCore.SIGNAL('updateDialog')
,self.ui_progress.setDialog)
self.connect(self.add_files_thread
,QtCore.SIGNAL('updateValue')
,self.ui_progress.setValue)
self.connect(self.add_files_thread
,QtCore.SIGNAL('finished')
,self.ui_progress.setFinished)
self.connect(self.add_files_thread
,QtCore.SIGNAL('canceled')
,self.ui_progress.closeNow)
# Connect the dialog to the thread
self.connect(self.ui_progress
,QtCore.SIGNAL('cancel')
,self.add_files_thread.cancelRequest)
# Start the thread
self.add_files_thread.start()
class AddFilesThread(QtCore.QThread):
def __init__(self, parent=None):
QtCore.QThread.__init__(self, parent)
self.cancel_request = False
def __del__(self):
self.wait()
def run(self):
try:
self.main()
except:
print 'AddFilesThread broke yo.'
self.cancelNow(force=True)
traceback.print_exc()
def main(self):
num_added = 0
for local_path in self.path_list:
# First Setup the dialog
status_label = 'Finding files to add . . .'
dialog_update = (status_label, (0,0), 0)
self.emit(QtCore.SIGNAL('updateDialog'), dialog_update)
# Do a recursive search.
all_files = hystrix_file.getFiles()
num_files = len(all_files)
if self.cancelNow():
return
status_label = '%d files found.\nExtracting tags . . .' %(num_files)
dialog_update = (status_label, (0,num_files), 0)
self.emit(QtCore.SIGNAL('updateDialog'), dialog_update)
num_added = 0
for index, filename in enumerate(all_files):
try:
metadata = hystrix_tags.getMetadata(filename)
# Here I would add the metadata to my library.
except:
traceback.print_exc()
print('Could not extract Metadata from file.')
continue
# This should be sent to a progress widget
if index % 1 == 0:
self.emit(QtCore.SIGNAL('updateValue'), index)
# Check if a cancel signal has been recieved
if self.cancelNow():
return
status_label = 'Finished. Added %d files.' %(num_added)
dialog_update = ( status_label, (0,num_added), num_added)
self.emit(QtCore.SIGNAL('updateDialog'), dialog_update)
self.emit(QtCore.SIGNAL('finished'))
def cancelRequest(self):
self.cancel_request = True
def cancelNow(self, force=False):
if self.cancel_request or force:
self.emit(QtCore.SIGNAL('canceled'))
return True
else:
return False
You could create a thread to update the GUI constantly, just pass to it references to the graphical widgets that need to be updated

Categories