Issue
I am using a PySide (Qt) Gui that should exit a loop on a button click. A button click for PySide emits a signal and the signal calls the connected functions. However, when the signal calls the functions it uses it's own system for Error handling.
I don't really want to do the fix for that Signal Error handling.
Is there a way to break out of a "with" statement without raising errors.
import sys
import time
from PySide import QtGui, QtCore
class MyClassError(Exception): pass
class MyClass(QtGui.QProgressDialog):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Properties
self.setWindowFlags(QtCore.Qt.Dialog | QtCore.Qt.WindowTitleHint)
self.setWindowModality(QtCore.Qt.WindowModal)
self.setMinimumDuration(2000) # if progress is finished in 2 sec it wont show
self.setRange(0, 99)
# end Constructor
def breakOut(self):
print("break out")
self.__exit__(MyClassError, "User cancel!", "") # does not break out of "with"
raise MyClassError("User cancel!") # PySide just prints Exception
# end breakOut
def __enter__(self):
self.canceled.connect(self.breakOut)
return self
# end enter (with)
def __exit__(self, etype, value, traceback):
# self.canceled.disconnect(self.breakOut)
if etype is None or etype == MyClassError:
return True
raise
# end exit
# end class MyClass
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
window = QtGui.QMainWindow()
window.show()
myinst = MyClass()
window.setCentralWidget(myinst)
val = 0
with myinst:
for i in range(100):
myinst.setLabelText(str(i))
myinst.setValue(i)
val = i+1
time.sleep(0.1)
# end with
print(val)
sys.exit(app.exec_())
The fixes that I have seen for PySide/Qt error handling in signals doesn't look nice. It overrides QApplication which means that someone else using this class has to use the new QApplication.
Is there an easy way around PySide/Qt signal error handling or is there an alternative way of breaking out of a "with" statement.
Solution
Firstly, I've interpreted your comment on @dano's answer to mean you want to do some sort of long running operations within the with
statement.
I think you have a few fundamental misunderstandings.
Your
with
statement (and the code within) is before the call toapp.exec_()
. The Qt Event loop does not start running untilapp.exec_()
is called. So all yourwith
statement is doing is queuing up events to be processed by the event loop once it is started. Thus, a qt signal to cancel the code within thewith
statement will never work because the signal will not be processed until the event loop is started, which is after your loop has finished (at least in the test case you provide above(.The way to fix this is to place your
with
statement in a callback that is executed by the QT Event loop (eg a function that is queued up by aQTimer
or similar)Even assuming you fix the above, you will never be able to interrupt the long running task with a button click (or a signal of any kind) because the long running task is in the same thread as the Qt Event loop, and thus the Qt Event loop cannot process any new commands (like pressing the cancel button) until your long running task finishes. While your long running task is
To fix that, you need to put your long running task in a thread (python thread or
QThread
) or another process. However, these approaches come with their own difficulties, largely centred around the fact that you should never attempt to update the GUI directly from a thread. There are some options to get around this, see here and here for questions that update the GUI safely from a thread.. I've wrapped some of this stuff up in a library if it helps, called qtutils.
I realise this doesn't directly solve your question of how to interrupt a with
statement, however I hope I've made it clear that your current approach will not work. If you switch to threads or processes, you should be able to kill the thread/process immediately (if you wish), rather than waiting for the thread to read a condition and exit itself (though of course hard killing something may have unintended side effects, but since that is what you seem to want to do, I'll assume you've thought about this and have decided it will always be fine).
Answered By - three_pineapples
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.