Search code examples
pythontkinterscrollrssupdating

Tkinter RSS Ticker accelerating at every update


There's no help on google, I've asked some people as well but none of them seem to know how to answer my question.

I'm programming a GUI for a project, and it contains an RSS-Feed ticker. It scrolls through the news and when it updates (every 3 seconds for obvious debug reasons) it speeds up a bit. This means, if I run the program, after two hours the ticker is scrolling at a non-human readable speed.

The main code wasn't written by me, I modified it and added the update function.

main():

import tkinter as tk
from Press import RSSTicker

def displayRSSticker(win):
    # Place RSSTicker portlet
    tickerHandle = RSSTicker(win, bg='black', fg='white', highlightthickness=0, font=("avenir", 30))
    tickerHandle.pack(side='bottom', fill='x', anchor='se')

def main():
    # Set the screen definition, root window initialization
    root = tk.Tk()
    root.configure(background='black')
    width, height = root.winfo_screenwidth(), root.winfo_screenheight()
    root.geometry("%dx%d+0+0" % (width, height))
    label = tk.Label(root, text="Monitor Dashboard", bg='black', fg='red')
    label.pack(side='bottom', fill='x', anchor='se')

    # Display portlet
    displayRSSticker(root)
    # Loop the GUI manager
    root.mainloop(0)

###############################
#     MAIN SCRIPT BODY PART   #
###############################
if __name__ == "__main__":
    main()

RSSTicker class:

import feedparser
import tkinter as tk

class RSSTicker(tk.Text):
    # Class constructor
    def __init__(self, parent, **params):
        super().__init__(parent, height=1, wrap="none", state='disabled', **params)
        self.newsFeed = feedparser.parse('http://www.repubblica.it/rss/homepage/rss2.0.xml')
        self.update()

    # Class methods
    def update(self):
        self.headlineIndex = 0
        self.text = ''
        self.pos = 0
        self.after_idle(self.updateHeadline)
        self.after_idle(self.scroll)
        self.after(4000, self.update)

    def updateHeadline(self):
        try:
            self.text += '       ' + self.newsFeed['entries'][self.headlineIndex]['title']
        except IndexError:
            self.headlineIndex = 0
            self.text = self.feed['entries'][self.headlineIndex]['title']

        self.headlineIndex += 1
        self.after(5000, self.updateHeadline)

    def scroll(self):
        self.config(state='normal')
        if self.pos < len(self.text):
            self.insert('end', self.text[self.pos])
        self.pos += 1
        self.see('end')
        self.config(state='disabled')
        self.after(180, self.scroll)

I thought the problem lied in the self.pos variable, printing it out resulted in it counting up, resetting to 1 and counting up faster.. But it doesn't seem to be problem causing the acceleration of the ticker. From what I've understood tho, the problem must be in the scroll method.

If someone understand how to keep the original scroll speed when updated, thank you.


Solution

  • I think you can use a couple tracking variables to make sure that update only starts the loop once and then next time update is called it will just run scroll without starting a new loop. At the same time if scroll is not called by update then it will continue looping as needed.

    Change your RSSTicker class to the following:

    class RSSTicker(tk.Text):
        # Class constructor
        def __init__(self, parent, **params):
            self.scroll_started = False # Tracker for first update.
            super().__init__(parent, height=1, wrap="none", state='disabled', **params)
            self.newsFeed = feedparser.parse('http://www.repubblica.it/rss/homepage/rss2.0.xml')
            self.update()
    
        def update(self):
            self.headlineIndex = 0
            self.text = ''
            self.pos = 0
            self.after_idle(self.updateHeadline)
            self.after_idle(lambda: self.scroll('update'))
            self.after(4000, self.update)
    
        def updateHeadline(self):
            try:
                self.text += '       ' + self.newsFeed['entries'][self.headlineIndex]['title']
            except IndexError:
                self.headlineIndex = 0
                self.text = self.feed['entries'][self.headlineIndex]['title']
    
            self.headlineIndex += 1
            self.after(5000, self.updateHeadline)
    
        def scroll(self, from_after_or_update = 'after'):
            self.config(state='normal')
            if self.pos < len(self.text):
                self.insert('end', self.text[self.pos])
            self.pos += 1
            self.see('end')
            self.config(state='disabled')
            # Check if the loop started by after.
            if from_after_or_update != 'update':
                self.scroll_started = True
                self.after(180, self.scroll)
            # If not started by after check to see if it is the 1st time loop is started by "update".
            elif self.scroll_started is False and from_after_or_update == 'update':
                self.scroll_started = True
                self.after(180, self.scroll)
            # If neither of the above conditions then do not start loop to prevent multiple loops.
            else:
                print("ran scroll method without adding new after loop!")