When I typed the following code as per the Think Python text book, I'm getting the error message below.
The window does actually get displayed, but it doesn't contain the desired content.
from swampy.World import World
world=World()
world.mainloop()
canvas = world.ca(width=500, height=500, background='white')
bbox = [[-150,-100], [150, 100]]
canvas.rectangle(bbox, outline='black', width=2, fill='green4')
The error message was like this:
Traceback (most recent call last):
File "15.4.py", line 4, in <module>
canvas = world.ca(width=500, height=500, background='white')
File "/usr/local/lib/python2.7/dist-packages/swampy/Gui.py", line 244, in ca
return self.widget(GuiCanvas, width=width, height=height, **options)
File "/usr/local/lib/python2.7/dist-packages/swampy/Gui.py", line 359, in widget
widget = constructor(self.frame, **widopt)
File "/usr/local/lib/python2.7/dist-packages/swampy/Gui.py", line 612, in __init__
Tkinter.Canvas.__init__(self, w, **options)
File "/usr/lib/python2.7/lib-tk/Tkinter.py", line 2234, in __init__
Widget.__init__(self, master, 'canvas', cnf, kw)
File "/usr/lib/python2.7/lib-tk/Tkinter.py", line 2094, in __init__
(widgetName, self._w) + extra + self._options(cnf))
_tkinter.TclError: can't invoke "canvas" command: application has been destroyed
The main application loop needs to be pretty much the last thing you run in your application. So move world.mainloop() to the end of your code like this:
from swampy.World import World
world = World()
canvas = world.ca(width=500, height=500, background='white')
bbox = [[-150, -100], [150, 100]]
canvas.rectangle(bbox, outline='black', width=2, fill='green4')
world.mainloop()
What happens in your code is that when the line with world.mainloop() is hit, it builds the user interface elements and goes in to the main loop, which continuously provides your application with user input.
During it's lifetime, that main loop is where your application will spend 99% of its time.
But once you quit your application, the main loop terminates and tears down all those user interface elements and the world. Then the remaining lines after the main loop will be executed. In those, you're trying to build and attach a canvas to a world that has already been destroyed, hence the error message.
Related
I am searching any simple way to make urwid.Frame work in python without success, as example, I try this MWE:
frame = urwid.Frame(urwid.Text(('some text'), align='center'))
filler = urwid.Filler(frame, "top")
loop = urwid.MainLoop(filler)
loop.run()
And I get the following unuseful error message:
Traceback (most recent call last):
File "pycurses.py", line 596, in <module>
loop.run()
File "/home/fauve/.local/lib/python3.5/site-packages/urwid/main_loop.py", line 287, in run
self._run()
File "/home/fauve/.local/lib/python3.5/site-packages/urwid/main_loop.py", line 385, in _run
self.event_loop.run()
File "/home/fauve/.local/lib/python3.5/site-packages/urwid/main_loop.py", line 790, in run
self._loop()
File "/home/fauve/.local/lib/python3.5/site-packages/urwid/main_loop.py", line 818, in _loop
self._entering_idle()
File "/home/fauve/.local/lib/python3.5/site-packages/urwid/main_loop.py", line 779, in _entering_idle
callback()
File "/home/fauve/.local/lib/python3.5/site-packages/urwid/main_loop.py", line 574, in entering_idle
self.draw_screen()
File "/home/fauve/.local/lib/python3.5/site-packages/urwid/main_loop.py", line 588, in draw_screen
canvas = self._topmost_widget.render(self.screen_size, focus=True)
File "/home/fauve/.local/lib/python3.5/site-packages/urwid/widget.py", line 145, in cached_render
canv = fn(self, size, focus=focus)
File "/home/fauve/.local/lib/python3.5/site-packages/urwid/decoration.py", line 814, in render
top, bottom = self.filler_values(size, focus)
File "/home/fauve/.local/lib/python3.5/site-packages/urwid/decoration.py", line 799, in filler_values
height = self._original_widget.rows((maxcol,),focus=focus)
File "/home/fauve/.local/lib/python3.5/site-packages/urwid/widget.py", line 205, in cached_rows
return fn(self, size, focus)
File "/home/fauve/.local/lib/python3.5/site-packages/urwid/container.py", line 1611, in rows
return sum(self.get_item_rows(size, focus))
File "/home/fauve/.local/lib/python3.5/site-packages/urwid/container.py", line 1512, in get_item_rows
l.append(w.rows((maxcol,),
AttributeError: 'Frame' object has no attribute 'rows'
I try many variations, such putting the frame inside an urwid.Pile before giving it to the Filler widget, but I still get similar error messages.
So, is their any minimalist example work using urwid.Frame?
And what doese mean this “Frame' object has no attribute 'rows'”? I never ask in my example any .rows method, so why the error message have a behavior like if I used it?
The error is indeed not very good. The actual problem here is that the nesting of the Frame and the Filler should be the other way around:
import urwid
text = urwid.Text('some text', align='center')
filler = urwid.Filler(text, "top")
frame = urwid.Frame(filler)
loop = urwid.MainLoop(frame)
loop.run()
A flow widget (like Text) gets to decide how many rows it should be. The main loop expects a box widget (which does not get to pick its own width or height). The Filler widget bridges the two: it asks the wrapped flow widget how many rows it takes up (given a number of columns) and then fills up the rest of the rows.
The Frame is a box widget that contains another box widget in the center.
If you haven't seen this page already, I highly recommend looking at the diagrams here: http://urwid.org/manual/widgets.html#included-widgets The colors describe what kinds of widgets nest inside what kinds of widgets.
(Also I suspect it would be technically feasible for Urwid to detect that the widget is of the wrong type and say that instead in the error message, but I haven't dived that deep into the implementation.)
I am trying to make a digital clock through python tkinter .My code is
import tkinter,time
def exiter():
root.destroy()
root=tkinter.Tk()
root.title("Digital Clock")
root.geometry("340x100")
root.resizable(False,False)
root.protocol("WM_DELETE_WINDOW",exiter)
def time_setter():
hr=tkinter.Label(root,font=('k',60,'bold'),text=time.strftime("%H:%M:%S"))
hr.grid(row=0,column=0)
while True:
root.update()
time_setter()
After I close the window I get an error
Traceback (most recent call last):
File "c:\Users\Tanmay Daga\OneDrive\Documents\Programming\Clock-Tkinter\digital_clock.py", line 20, in <module>
time_setter()
File "c:\Users\Tanmay Daga\OneDrive\Documents\Programming\Clock-Tkinter\digital_clock.py", line 13, in time_setter
hr=tkinter.Label(root,font=('k',60,'bold'),text=time.strftime("%H:%M:%S"))
File "C:\Program Files\Python39-32\lib\tkinter\__init__.py", line 3145, in __init__
Widget.__init__(self, master, 'label', cnf, kw)
File "C:\Program Files\Python39-32\lib\tkinter\__init__.py", line 2569, in __init__
self.tk.call(
_tkinter.TclError: can't invoke "label" command: application has been destroyed
How do I break the loop when the window is closed.
How do I know when the window is closed
from tkinter import *
from time import strftime
def clock_tick():
string = strftime('%H:%M:%S %p')
lbl.config(text=string)#set the text
lbl.after(1000, time)#updater
root=Tk()
lbl = Label(root, font=('calibri', 40, 'bold'),
background='purple',
foreground='white')
lbl.pack(anchor = 'center')
clock_tick()
root.title("Digital Clock")
root.geometry("340x100")
root.resizable(False,False)
root.mainloop() #this is the loop you are looking for instead of the while loop
The loop is wrong. For tkinter there is a built in loop that is much better to use. Exiting this with the X will provide a clean exit if you set the loop up by using root.mainloop(). Here is a good example of what you are trying to do
https://www.geeksforgeeks.org/python-create-a-digital-clock-using-tkinter/
Althogh it works perfectly fine with built in turtle shapes it doesn't work with
new registered shapes.The error is pyimage1 doesn't exist and both my program and file are in the same directories
Here is the code
root=Tk()
import turtle
def image():
global img
img='batman.gif'
player=turtle.Turtle()
wn=turtle.Screen()
wn.register_shape(img)
player.shape(img)
B=Button(root,text='click',command=image).pack()```
The error shown is:```Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Users\dell\AppData\Local\Programs\Python\Python37\lib\tkinter\__init__.py", line 1705, in __call__
return self.func(*args)
File "C:\Users\dell\OneDrive\Desktop\imagetk.py", line 10, in image
player.shape(img)
File "C:\Users\dell\AppData\Local\Programs\Python\Python37\lib\turtle.py", line 2777, in shape
self.turtle._setshape(name)
File "C:\Users\dell\AppData\Local\Programs\Python\Python37\lib\turtle.py", line 2506, in _setshape
self._item = screen._createimage(screen._shapes["blank"]._data)
File "C:\Users\dell\AppData\Local\Programs\Python\Python37\lib\turtle.py", line 723, in _createimage
return self.cv.create_image(0, 0, image=image)
File "<string>", line 1, in create_image
File "C:\Users\dell\AppData\Local\Programs\Python\Python37\lib\tkinter\__init__.py", line 2489, in create_image
return self._create('image', args, kw)
File "C:\Users\dell\AppData\Local\Programs\Python\Python37\lib\tkinter\__init__.py", line 2480, in _create
*(args + self._options(cnf, kw))))
_tkinter.TclError: image "pyimage1" doesn't exist```
The problem is the way you're mixing turtle and tkinter is creating two roots which leads to this error. You're trying to use standalone turtle when you should be using embedded turtle. I.e. you should be using RawTurtle instead of Turtle and RawScreen instead of Screen. But don't just swap the names, look them up in the documentation. Your code should look roughly like:
from tkinter import *
from turtle import TurtleScreen, RawTurtle
IMAGE = 'batman.gif'
def image():
player = RawTurtle(screen)
player.shape(IMAGE)
root = Tk()
Button(root, text='click', command=image).pack()
canvas = Canvas(root)
canvas.pack()
screen = TurtleScreen(canvas)
screen.register_shape(IMAGE)
screen.mainloop()
I write on Python an GUI with dynamic resizing, I mean, if the height of all the elements of the window are more than the height of the window, the scrollbar appears on the right side, but if I resize the window and the height of all elements are less than the window, the scrollbar disappear.
I have made this using the <Configure> event. This is part of the code I have used:
mycanvas = Canvas(self.parent,bd=0,highlightthickness=0,relief='flat')
sbar = tk.Scrollbar(mycanvas,orient='vertical')
sbar.config(command=mycanvas.yview)
mycanvas.config(yscrollcommand=sbar.set)
def canvasscroll(event):
mycanvas.yview('scroll',int(-1*(event.delta/120)), "units")
def resizecanvas(event):
if self.mainyposition > event.height: #mainyposition is the addition of the height of all widgets in screen
sbar.pack(side=RIGHT, fill=Y)
else:
sbar.forget()
print(event.height)
self.widgetname.bind_all("<MouseWheel>", canvasscroll)
self.widgetname.bind_all("<Configure>", resizecanvas)
Everything works well except for two issues:
First of all, when the window starts, the <Configure> event is called a few times, and print the height on screen, but the last time its called before the program stills on stand by is a height that is not the correct height of the window, so this causes that it shows unnecesarily the scrollbar. The problem solves itself once I resize the window and the event runs again, but the first run it fails doing this. Any way to solve this?
And second, during the resizing, the console doesn't show any errors, but when I close the window, it shows me an Exception in Tkinter Callback that references the pack() method of the scrollbar. I don't know how to solve this... The errors are this:
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Python34\lib\tkinter\__init__.py", line 1702, in __call__
return self.func(*args)
File "Y:\Factory\GLP2-e Admin (Todos los archivos)\Nueva Interfaz\classmain.py", line 108, in resizecanvas
sbar.pack(side=RIGHT, fill=Y)
File "C:\Python34\lib\tkinter\__init__.py", line 2140, in pack_configure + self._options(cnf, kw))
_tkinter.TclError: bad window path name ".!canvas.!scrollbar"
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Python34\lib\tkinter\__init__.py", line 1702, in __call__
return self.func(*args)
File "Y:\Factory\GLP2-e Admin (Todos los archivos)\Nueva Interfaz\classmain.py", line 108, in resizecanvas
sbar.pack(side=RIGHT, fill=Y)
File "C:\Python34\lib\tkinter\__init__.py", line 2140, in pack_configure + self._options(cnf, kw))
_tkinter.TclError: bad window path name ".!canvas.!scrollbar"
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Python34\lib\tkinter\__init__.py", line 1702, in __call__
return self.func(*args)
File "Y:\Factory\GLP2-e Admin (Todos los archivos)\Nueva Interfaz\classmain.py", line 108, in resizecanvas
sbar.pack(side=RIGHT, fill=Y)
File "C:\Python34\lib\tkinter\__init__.py", line 2140, in pack_configure + self._options(cnf, kw))
_tkinter.TclError: bad window path name ".!canvas.!scrollbar"
[Finished in 22.3s]
Thanks to everyone.
I am currently trying to implement code into my program to update a buttons color when the user hovers the mouse cursor over it. The program recognizes the hover, but returns an error.
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Python34\lib\tkinter\__init__.py", line 1482, in __call__
return self.func(*args)
File "C:\Users\oiest\Documents\Programs\iNTMI\v1.1.3b\iNTMI.py", line 252, in <lambda>
achievementsButton.bind("<Enter>", lambda event: achievementsButton.configure(bg = "red"))
File "C:\Python34\lib\tkinter\__init__.py", line 1270, in configure
return self._configure('configure', cnf, kw)
File "C:\Python34\lib\tkinter\__init__.py", line 1261, in _configure
self.tk.call(_flatten((self._w, cmd)) + self._options(cnf))
_tkinter.TclError: unknown option "-bg"
I had Googled how to do the changing of colors when hovering and found the following code. Though for some reason, it does not work for me. What am I doing wrong?
achievementsButton.bind("<Enter>", lambda event: achievementsButton.configure(bg = "red"))
achievementsButton.bind("<Leave>", lambda event: achievementsButton.configure(bg = "white"))
This is the code from where I originally defined achievementsButton.
achievementsButton = ttk.Button(self, text = "Achievements", command = lambda: controller.show_frame(achievements), width = "25")
ttk.Button instances do not have a bg or background attribute. There are two solutions:
Use an ordinary tkinter.Button, which does have a bg attribute.
Keep using the ttk.Button, and configure it using a style object. See Using and customizing ttk styles for more information. Example:
from Tkinter import *
import ttk
root = Tk()
s = ttk.Style()
s.configure("regular.TButton", background="red")
s.configure("onhover.TButton", background="white")
button = ttk.Button(root, style="regular.TButton")
button.pack()
button.bind("<Enter>", lambda event: button.configure(style="onhover.TButton"))
button.bind("<Leave>", lambda event: button.configure(style="regular.TButton"))
root.mainloop()
However, this will only change the background color of the area behind the actual button, rather than the button's face. This
post seems to indicate that it's impossible to change the face color of a ttk Button.