all! I've been banging my head against a wall for days trying to figure this out. I pulled the Marquee class from How to make marquee on tkinter in label? (from the first answer) and am trying to dynamically update the marquee with new messages as needed. Anytime I try to access inside of that class, however, it tells me that 'NoneType' object has no attribute 'att name here'. I understand this has to do with init returning None. Changing it to new throws more errors, such as '_tkinter.tkapp' object has no attribute '_setup'. Any ideas or other help would be great.
import tkinter as tk
from tkinter import *
#Pulled from: https://stackoverflow.com/questions/47224061/how-to-make-marquee-on-tkinter-in-label
class Marquee(tk.Canvas):
def __init__(self, parent, text, margin=2, borderwidth=1, relief='flat', fps=30):
super().__init__(parent, borderwidth=borderwidth, relief=relief)
self.fps = fps
# start by drawing the text off screen, then asking the canvas
# how much space we need. Use that to compute the initial size
# of the canvas.
text = self.create_text(0, -1000, text=text, anchor="w", tags=("text",))
(x0, y0, x1, y1) = self.bbox("text")
width = (x1 - x0) + (2*margin) + (2*borderwidth)
height = (y1 - y0) + (2*margin) + (2*borderwidth)
self.configure(width=325, height=height)
# start the animation
self.animate()
def animate(self):
(x0, y0, x1, y1) = self.bbox("text")
if x1 < 0 or y0 < 0:
# everything is off the screen; reset the X
# to be just past the right margin
x0 = self.winfo_width()
y0 = int(self.winfo_height()/2)
self.coords("text", x0, y0)
else:
self.move("text", -1, 0)
# do again in a few milliseconds
self.after_id = self.after(int(1000/self.fps), self.animate)
#added this to try and update the text
def update_Marquee(self, marqueeText):
if marqueeText == "" or None:
marqueeText = marqueeTextDefault
marquee.itemconfig("text", marqueeText)
#Info on Tkinter: https://tkdocs.com/shipman/index-2.html
root = tk.Tk()
root.title("Testing, Testing, Testing")
root.maxsize(width=340,height=393)
marquee = Marquee(root, text=testing, , borderwidth=1, relief="sunken").grid(column=0,row=0,padx=5,pady=1,columnspan=3,sticky=W)
I've tried: -Adding a function to the Marquee class to either overwrite the existing marquee or to update the text, both fail with the NoneType message -accessing marquee.itemconfig in multiple places, all failing
I've addressed some syntax errors and other issues, and moved the definition of update_marquee
into the Marquee
class where it belongs. I've also added marquee_default_text
as a class attribute since it was undefined.
Now when you instantiate Marquee
, the value passed in as text
will be treated as the default text, and you can call update_marquee
to change that text as needed.
As an example here, I'm using after
to change the text to something else after 5 seconds.
I've also made some minor adjustments to the names of things to adhere to PEP8 - I'd recommend familiarizing yourself with it! Understanding the Python style guide will help you to write better, more readable Python.
import tkinter as tk
#Pulled from: https://stackoverflow.com/questions/47224061/how-to-make-marquee-on-tkinter-in-label
class Marquee(tk.Canvas):
def __init__(
self, parent, text, margin=2, borderwidth=1, relief='flat', fps=30
) -> None:
super().__init__(parent, borderwidth=borderwidth, relief=relief)
self.fps = fps
self.marquee_text_default = text
# start by drawing the text off screen, then asking the canvas
# how much space we need. Use that to compute the initial size
# of the canvas.
text = self.create_text(
0, -1000, text=text, anchor="w", tags=("text",)
)
(x0, y0, x1, y1) = self.bbox("text")
width = (x1 - x0) + (2 * margin) + (2 * borderwidth)
height = (y1 - y0) + (2 * margin) + (2 * borderwidth)
self.configure(width=325, height=height)
# start the animation
self.animate()
def animate(self) -> None:
(x0, y0, x1, y1) = self.bbox("text")
if x1 < 0 or y0 < 0:
# everything is off the screen; reset the X
# to be just past the right margin
x0 = self.winfo_width()
y0 = int(self.winfo_height()/2)
self.coords("text", x0, y0)
else:
self.move("text", -1, 0)
# do again in a few milliseconds
self.after_id = self.after(int(1000/self.fps), self.animate)
def update_marquee(self, marquee_text=None) -> None:
if not marquee_text:
# NOTE: marquee_text_default was undefined.
# I've added it as a class attr.
marquee_text = self.marquee_text_default
self.itemconfig("text", text=marquee_text)
root = tk.Tk()
root.title("Testing, Testing, Testing")
root.maxsize(width=340,height=393)
marquee = Marquee(root, text='testing', borderwidth=1, relief="sunken")
# NOTE: always call grid (or pack or place) on a separate line!
# otherwise your object will evaluate to None because that's what grid returns
marquee.grid(column=0, row=0, padx=5, pady=1, columnspan=3, sticky='w')
# change the marquee text after 5 seconds
root.after(5000, marquee.update_marquee, 'Hello there!')
root.mainloop()