0

I'm trying to make a simple Weather app that also shows the current time. However, after multiple trial and error i am asking for help. I've come to the conclusion that i must commit to threading where the clock is continously run in the background to my PyQt UI. Although, it only freezes and crashes, and i can't understand why. As you can understand i've checked multiple posts already on this issue such as, [1] and [2]. But i'm none the wiser...

This is the minimal reproducible code:


class Clock(QObject):
    updated_time = pyqtSignal()
    
    def __init__(self, parent=None):
        QObject.__init__(self,parent=parent)

    def get_time(self):
        #This code is meant to be run continously
        #For troubleshooting, ive removed the while-loop momentarily

        QThread.sleep(1)
        now = datetime.now()
        current_time = now.strftime("%H:%M")
        self.updated_time.emit()

class MainWindow(QMainWindow):
    
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        self.initUI()


    def initUI(self):
        self.setWindowTitle('WeatherApp')
        self.resize(700, 500)
        self.clock_label = QLabel(self)
        self.clock_label.setText('make me change')

        self.show()

        
        thread = QThread()
        worker = Clock()
        worker.moveToThread(thread)
        thread.started.connect(lambda: worker.get_time())
        worker.updated_time.connect(self.update_clock())

        thread.start()  
        
    def update_clock(self):
        self.clock_label.setText(current_time)

def main():

    app = QApplication(sys.argv)
    mw = MainWindow()
    sys.exit(app.exec_())


if __name__ == '__main__':
    main()

The current code creates the window with the clock label taking the value of the intially declard variable, current_time. In the background, the function from the worker (do_work) is running in the background, iterating continously with a 1 second delay. As i understand from post 1, i should not update the UI from a thread, but even with the invokeMethod i am unable to achieve any success.. Shouldn't the problem be as easy as to emit a signal from the function do_work back to the App? I've adding one but i receive the error: "AttributeError: 'QThread' object has no attribute ".

hanmic-6
  • 81
  • 5
  • Just to clarify, is your worker function just an example and in reality it could take an unspecified amount of time, or you actually just need to update the UI every second? – musicamante Apr 12 '21 at 15:00
  • Hello! It's meant to update a clock, preferrably in real-time, but a 5-10 seconds delay would not be troublesome. This is the code the function 'do_work()' is meant to run: now = datetime.now() current_time = now.strftime("%H:%M") where current_time is supposed to be posted to the UI. – hanmic-6 Apr 12 '21 at 15:10
  • If you only need to update a clock, you don't need threading, just use a [QTimer](https://doc.qt.io/qt-5/qtimer.html): `self.timer = QtCore.QTimer(interval=1000, timeout=self.updateTime)` and in `updateTime()` update the label with the current time. – musicamante Apr 12 '21 at 15:18
  • Ah yes, but wouldnt that produce a timer and not produce the actual time of day? Nonetheless, I want to call a weather API which also runs in the background that updates the temperature as well. Which is why getting this to work and making me understand the mistake I’m doing would help me for other implementations. – hanmic-6 Apr 12 '21 at 15:23
  • 1
    The timer is just to call a function at specific interval, in the `updateTime` function (or whatever name you decide) you need to use `setText()` with the time you need to write. That said, how is the temperature detected? – musicamante Apr 12 '21 at 15:45
  • If thats the case it sounds suitable. I’m not feeling like threading and I get along very well, hehe. The weather details are gathered from ‘open weather map’ through a simple get request where I receive a JSON response. – hanmic-6 Apr 12 '21 at 16:25
  • @hanmic-6 please provide a real [mre] – eyllanesc Apr 12 '21 at 17:51
  • 2
    Network requests are blocking (even if they answer fast, there could always be a problem, like DNS or database requests delays), so you could opt for a QNetworkManager instead (which is thread safe). Otherwise, use the QThread, but create a signal in the Worker class, emit that signal with the arguments you need when you get the response, and in the main class connect that signal with the function that actually updates the ui. Do some research, as there are hundreds of posts about this kind of topic. – musicamante Apr 12 '21 at 17:53
  • @musicamante Thanks! ill try out both of your proposals later this evening. – hanmic-6 Apr 12 '21 at 18:07
  • I looked up som examples from your first tip @musicamante and to say the least, it manages the clock problem like a charm. I followed this link: https://linuxhint.com/use-pyqt-qtimer/ --> Example 2, for having a digital clock. Wish i saw it sooner. Thanks – hanmic-6 Apr 12 '21 at 18:43

0 Answers0