0

I'm currently attempting to create a threaded timer application using PyQt. Simple, right? I thought so too. However, after spending all day trying to figure out what's going wrong, I still have absolutely no idea. In all of my immense stubbornness, I have refused to give up on what was supposed to be a 15-minute project.

Heres mah codez:

__author__ = 'Darth Vader'

from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import QMessageBox, QApplication, QDialog
from PyQt5.QtCore import QThread
from timerui import Ui_Form
import sys
import ctypes
import time
import threading

class Timer(QThread):
    def makeNoise(self):
        pass

    def run(self):

        self.ui.startButton.setStyleSheet('''QPushButton {color: red;font: bold 15px;}''')

        self.ui.startButton.setEnabled(False)

        self.hour = int(self.ui.spinBoxHrs.value())
        self.min = int(self.ui.spinBoxMin.value())
        self.sec = int(self.ui.spinBoxSec.value())

        if self.sec:
            self.countSec = self.sec
        elif self.min and not self.sec:
            self.countSec = 60
            self.min -= 1
        elif self.hour and not self.min and not self.sec:
            self.min = 59
            self.countSec = 60
        print(self.countSec)
        while self.countSec or self.hour or self.min:
            if not self.countSec and self.min:
                self.min -= 1
                self.countSec = 60
            elif not self.countSec and not self.min and self.hour:
                self.hour -= 1
                self.min = 59
                self.sec = 60
            elif not self.countSec and not self.min and not self.hour:
                self.makeNoise()
                break

            time.sleep(1)
            self.countSec -= 1
            self.ui.startButton.setText("%dh %dm %ds" % (self.hour, self.min, self.sec))

        self.ui.startButton.setEnabled(True)
        self.ui.startButton.setText("Start")
        self.ui.startButton.setStyleSheet('QPushButton{}')

    def setup(self, gui):
        self.ui = gui

    def __init__(self):
        QThread.__init__(self)


def start():
    t = Timer()
    t.start()




if __name__ == '__main__':
    myappid = u'org.ayoung.timer'
    ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(myappid)

    app = QApplication(sys.argv)
    app.setWindowIcon(QtGui.QIcon('res/favicon.png'))

    window = QDialog()

    ui = Ui_Form()
    ui.setupUi(window)
    ui.startButton.clicked.connect(start)

    window.show()
    sys.exit(app.exec_())

And the error:

QThread: Destroyed while thread is still running
QMutex: destroying locked mutex

From what I've read, these two errors have something to do with garbage collection, but I have absolutely no idea how to fix them.

Thanks!

Darth Vader
  • 383
  • 3
  • 12

2 Answers2

1

There are three issues with your code. You need to solve the first one before you can solve the third one (the second issue should go away once you solve the first issue)

  1. You are confusing functions and methods. I can tell this because you have a function called start, but the fact that the first argument in the function signature is called self indicates that you want it to be a method of an object. You might want to read this for a good explanation of the difference between a function and a method.

  2. As a consequence of the previous point, and the fact that the QPushButton.clicked signal emits a boolean, means that when start is called, the self variable contains True (rather than a reference to the instance of a class (otherwise known as an object) - which is what would happen is start was a method rather than a function)

  3. The line self.t = Timer().start() does the following:

    • Timer() creates an instance of the Timer class
    • The start method of this instance is called
    • The return value of the start() method is stored in self.t (we're ignoring here the problem that self is not a reference to an object).

    What you want to do is instead create the instance of Timer and assign it to self.t. Then call start() on self.t. For example:

    self.t = Timer()
    self.t.start()
    

    This ensures that the Timer object is saved somewhere and does not get garbage collected.

Community
  • 1
  • 1
three_pineapples
  • 11,579
  • 5
  • 38
  • 75
  • I had the start method encased in a container class, hoping that having a class instance storing the variable might prevent it from being garbage collected. It didn't, so I removed the class, but forgot to remove self. I've edited the op to reflect what my code looks like now, and the consequent error. – Darth Vader Feb 24 '15 at 20:28
  • @DarthVader Can you attempt to address point 1 of my post by putting the `start` method back inside of a class, creating an instance of that class and connecting the `clicked` signal of the button to the `start` method that belongs to the instance. If you still see the thread being garbage collected in that situation, then maybe your container object is being garbage collected. Either way, that is the crux of the problem and I need to see what you tried with classes to diagnose/explain why it isn't working. – three_pineapples Feb 25 '15 at 10:37
0
def start(self):
    self.t = Timer().start()


...    
    ui.startButton.clicked.connect(start)

Here you are connecting a signal to a slot. The parameter passed to the slot is the button state, which is a bool. So self is True in your case.

warvariuc
  • 57,116
  • 41
  • 173
  • 227
  • In addition to which, QThread.start() returns None, and the thread is not being assigned to a variable, which is why it gets destroyed. Honestly, there are so many things wrong with this code it's hard to know where to start. – Dan Milburn Feb 24 '15 at 09:02
  • Gimme a break, it's my first time using PyQT. Besides, the same thing happened when I had two separate statements. @warvariuc Tried that, now I just get the "QThread: Destroyed while thread is still running" error. – Darth Vader Feb 24 '15 at 09:25
  • @DarthVader I know you have other issues with this code, but I responded to your main question. Stackoverflow tries to answer one question a time, otherwise you end up discussing code instead of answering small and well defined questions. You could discuss you code [here](http://chat.stackoverflow.com/rooms/6/python) – warvariuc Feb 24 '15 at 10:10