2

I am attempting to convert code from Summerfield's article on (old-style) PyQt Signals/Slots to new-style PySide code. One example is a pure console application, which I have never worked with before. Unfortunately, when I try to run it multiple times, I am told that the previous application is still running.

It is a simple app: it basically lets you set a number, and reports back if the number is new:

from PySide import QtCore
import sys

class TaxRate(QtCore.QObject):
    rateChangedSig=QtCore.Signal(float)

    def __init__(self):
        QtCore.QObject.__init__(self)
        self.rate = 17.5

    def getRate(self):
        return self.rate

    def setRate(self, newRate):
        if newRate != self.rate:
            self.rate = newRate
            self.rateChangedSig.emit(self.rate)  #was self.emit(SIGNAL("rateChanged"), self.rate)

@QtCore.Slot() #technically not really needed
def rateChangedSlot(value):
    print("Tax rate changed to {0:.2f} %".format(value))


if __name__=="__main__":
    qtApp = QtCore.QCoreApplication(sys.argv)  #origional had QtGui.QApplication, but there is no GUI
    vat = TaxRate()
    vat.rateChangedSig.connect(rateChangedSlot) #was vat.connect(vat, SIGNAL("rateChanged"), rateChanged)
    vat.setRate(8.5)     # A change will occur (new rate is different)

    qtApp.quit() 
    sys.exit(qtApp.exec_())

Overall, it works as expected, except the final two lines do not kill the process. When I try to run the program twice, the second time my IDE (Spyder) always tells me that it is already running in a separate process. If I try running it from the command line, the window just hangs.

Strangely, when I comment out the last two lines I do not get this warning. This is the opposite of what I expect (based on previous experience with PySide GUI applications and the documentation for quit()).

Following the Closing a window example at Zetcode, I tried replacing qtApp.quit() with qtApp.instance().quit(), which yielded the same non-killing result.

So, how do I kill this thing?

One idea is that I shouldn't have even started it in the first place (as suggested here). Even though it is a pure console app, Summerfield's original program initializes with app=QtGui.QApplication(sys.argv), and it does not contain the last two lines. Things run fine, multiple times. However, isn't there a concern that each run would create a new process, so his program seems to be effectively multiplying processes without warning? (Note in practice I don't think this is happening on my system, so the answer seems to be 'No' for reasons I don't understand).

What is the correct way to control/initialize/kill a console app using PySide?

(This is ignoring the question, for the time being, why one would ever use PySide for a pure console application in Python as has been pointed out previously. But if anyone were to be interested in answering that separate question, I could start a separate question for it).


Potentially relevant post:

Community
  • 1
  • 1
eric
  • 7,142
  • 12
  • 72
  • 138

1 Answers1

3

The problem is because you call QCoreApplication.quit() before you call QCoreApplication.exec_(). The call to quit is not queued up in the event loop, it happens immediately. The call to QCoreApplication.exec_() starts the event loop which only ends when a call to QCoreApplication.exit() (or QCoreApplication.quit()) is made while the event loop is running.

This is somewhat explained in the Qt documentation of QCoreApplication but it is very easy to miss.

I imagine you don't really need to call exec_() as you aren't using any events in your current code (most events are to do with window/mouse/keyboard though you might conceivably use some in the future like those generated by QTimer). It really depends what you want to do with the program in the future. If you don't call exec_(), then your script will exit as you would normally expect any Python script to do (the only blocking function in your code is the call to exec_(), remove that and nothing keeps it running.)

three_pineapples
  • 11,579
  • 5
  • 38
  • 75
  • 1
    I didn't originally have the quit in there: without it, the process still stays alive (basically, as long as I call exec_(), it seems to stay alive no matter what I do). As you guessed, I added `quit` hoping it would go into the event queue. Based on your comment, I also tried splitting thing up: `qtApp.exec_(); qtApp.instance.quit(); sys.exit();`. No luck, process still alive. How do I kill this thing? What if it was actually crucial to call exec_()? Is the point that for a pure console app, it is not only not crucial, it should never be? Is this documented, or is it acquired common sense? – eric Jul 28 '14 at 01:47
  • 1
    `qtApp.exec_()` is a blocking function. It starts an event loop that does not end until something posts an event to it. So any code after that call never runs (because the event loop never finishes). Most events are mouse clicks, etc, but you obviously don't have those. A `QTimer` is one of the few things that will post an Event (but you obviously need to make the code which does that occur before the event loop is started - Events will be placed in a queue until such time as the loop starts). Others probably include the Qt networking libraries (but to be honest I don't have a complete list!) – three_pineapples Jul 28 '14 at 02:23