Tkinter create image function error: pyimage1 does not exist - python

I was writing code for a minesweeper program but I keep on getting an error message that states that "pyimage1" does not exist. The error, I suspect originates from "def frame" and or "class control". I have seen others post similar questions yet to no avail. The images themselves block.png and flag.png are not corrupt. Previous tests show that it worked but started to fail when I integrated multiple windows(one with the visuals and the other with the control buttons)
C:\Users\akiva\PycharmProjects\helloOpencv\venv\Scripts\python.exe C:/Users/akiva/PycharmProjects/helloOpencv/operation.py
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Users\akiva\AppData\Local\Programs\Python\Python37\lib\tkinter\__init__.py", line 1705, in __call__
return self.func(*args)
File "C:\Users\akiva\PycharmProjects\helloOpencv\mine_sweeper.py", line 72, in frame
self.canvas.create_image(x1, y1, image=self.block_pic)
File "C:\Users\akiva\AppData\Local\Programs\Python\Python37\lib\tkinter\__init__.py", line 2489, in create_image
return self._create('image', args, kw)
File "C:\Users\akiva\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
from tkinter import *
from tkinter import Canvas
from PIL import ImageTk, Image
from time import sleep
class ResizingCanvas(Canvas):
def __init__(self,parent,**kwargs):
Canvas.__init__(self,parent,**kwargs)
self.bind("<Configure>", self.on_resize)
self.height = self.winfo_reqheight()
self.width = self.winfo_reqwidth()
def on_resize(self,event):
# determine the ratio of old width/height to new width/height
wscale = float(event.width)/self.width
hscale = float(event.height)/self.height
self.width = event.width
self.height = event.height
# resize the canvas
self.config(width=self.width, height=self.height)
# rescale all the objects tagged with the "all" tag
self.scale("all",0,0,wscale,hscale)
class Minesweeper(Tk):
def __init__(self, master):
Tk.__init__(self)
fr = Frame(self)
fr.pack(fill=BOTH, expand=YES)
self.canvas = ResizingCanvas(fr, width=940, height=920, bg="black", highlightthickness=0)
self.count = 0
self.start = 0
self.newWindow = Toplevel(self.master)
self.app = Control(self, self.newWindow)
self.title("MineSweeper")
self.canvas.pack(fill=BOTH, expand=YES)
x1 = 20
y1 = 20
x2 = 80
y2 = 80
self.block_pic = PhotoImage(file='C:/Users/akiva/OneDrive/Desktop/akiva/Python Files/block.PNG')
self.flag_pic = PhotoImage(file='C:/Users/akiva/OneDrive/Desktop/akiva/Python Files/flag.PNG')
for k in range(14):
for i in range(15):
self.canvas.create_rectangle(x1, y1, x2, y2, fill='white')
x1 += 60
x2 += 60
x1 = 20
x2 = 80
y1 += 60
y2 += 60
def shift_image(self):
if self.count == 0:
Tk.canvas.itemconfig(self.block_pic, image=self.flag_pic)
def end(self):
self.start = 0
del self.block_pic
print("Game has ended")
self.after(2000, quit())
def frame(self):
self.start += 1
if self.start == 1:
x1 = 50
y1 = 50
for i in range(14):
for k in range(15):
self.canvas.create_image(x1, y1, image=self.block_pic)
x1 += 60
x1 = 50
y1 += 60
self.canvas.pack()
else:
print("Game has already started")
class Control(Toplevel):
def __init__(self, parent, master):
self.master = master
self.frame = Frame(self.master)
start_button = Button(self.frame, text="Start Game", command=parent.frame,)
stop_button = Button(self.frame, text="End Game", command=parent.end)
start_button.pack()
stop_button.pack()
self.quitButton = Button(self.frame, text='Quit', width=25, command=self.close_windows)
self.quitButton.pack()
self.frame.pack()
def close_windows(self):
self.master.destroy()

Related

How to move a shape while retaining original size in tkinter

I am trying to have the rectangle I create spawn at the bottom of my tkinter canvas instead of spawning at the top without affecting its size. I have only found this post and this doesn't help me as it does not describe how to move the shape to the bottom of the canvas
Here's my code:
from tkinter import *
from tkinter.ttk import *
#Temporary
width = 1024
height=768
class GFG:
def __init__(self, master = None):
self.master = master
self.master.configure(bg="black") #Apart from the canvas colour
self.x = 0
self.y = 0
self.canvas = Canvas(master, background="black", highlightthickness=0, width=width, height=height) #Canvas colour
self.rectangle = self.canvas.create_rectangle(5, 5, 25, 25, fill='white')
self.canvas.pack()
self.movement()
def movement(self):
self.canvas.move(self.rectangle, self.x, self.y)
self.canvas.after(100, self.movement)
def left(self, event):
print(event.keysym)
self.x = -5
self.y = 0
def right(self, event):
print(event.keysym)
self.x = 5
self.y = 0
if __name__ == "__main__":
master = Tk()
master.title("Black Hole Settlers")
gfg = GFG(master)
master.bind("<KeyPress-Left>", lambda e: gfg.left(e))
master.bind("<KeyPress-Right>", lambda e: gfg.right(e))
mainloop()
Pastebin
Thanks,
swanserquack

animate text color and let other widgets waiting

I'm looking for a way to create a "custom animation" for various texts. In my case, everything is done in sequence. I'm looking for a solution where when I add a widget to my method, the widget is played first after the first widget is finished
from tkinter import *
class MainWindow(Tk):
def __init__(self):
super().__init__()
self._animate = TAnimation()
self.textLabel = Label(self, bg="black", text="FontAnimation Text1", font=("Microsoft YuHei", 30))
self.textLabel.pack(fill="both", expand="yes")
self.textLabel2 = Label(self, bg="black", text="FontAnimation Text2", font=("Microsoft YuHei", 30))
self.textLabel2.pack(fill="both", expand="yes")
self._animate.animateTextColor(self.textLabel, 0, 40, 50)
self._animate.animateTextColor(self.textLabel2, 0, 120, 100) ## want to make other widgets "waiting" maybe one sec maybe 60 secs
class TAnimation(Frame):
def __init__(self):
super().__init__()
self.r = 0
self.g = 0
self.b = 0
def animateTextColor(self, widget, start, wait, speed):
if start < wait:
widget.after(wait, lambda: self.animateTextColor(widget, start, wait, speed))
start += 5
print(start)
elif start == wait:
if self.b < 255:
widget.configure(fg=self.rgbColor((self.r, self.g, self.b)))
widget.after(speed, lambda : self.animateTextColor(widget,start, wait, speed))
self.r += 1
self.g += 1
self.b += 1
else:
self.r = 0
self.g = 0
self.b = 0
def rgbColor(self, rgb):
return "#%02x%02x%02x" % rgb
if __name__ == '__main__':
mw = MainWindow()
x1, y1 = mw.winfo_screenwidth() / 2, mw.winfo_screenheight() / 2
x2, y2 = mw.winfo_screenwidth() / 4, mw.winfo_screenheight() / 4
mw.geometry("%dx%d+%d+%d" % (x1, y1, x2, y2))
mw.mainloop()
The problem here is that "widget2" assumes the same animation, actually this animation should only start when "widget1" is finished
A simple way would be to add items to a queue(list) and once one animation is completed start another.
check the example below
from tkinter import *
class MainWindow(Tk):
def __init__(self):
super().__init__()
self._animate = TAnimation()
self.textLabel = Label(self, bg="black", text="FontAnimation Text1", font=("Microsoft YuHei", 30))
self.textLabel.pack(fill="both", expand="yes")
self.textLabel2 = Label(self, bg="black", text="FontAnimation Text2", font=("Microsoft YuHei", 30))
self.textLabel2.pack(fill="both", expand="yes")
self._animate.addToQueue(self.textLabel, 5, 20)
self._animate.addToQueue(self.textLabel2, 10, 5) ## want to make other widgets "waiting" maybe one sec maybe 60 secs
class TAnimation(Frame):
def __init__(self):
super().__init__()
self.thresh = 255
def animate(self, item, wait, rgb: list):
if any(x>=self.thresh for x in rgb):
return
rgb = [x+1 for x in rgb]
item.config(fg=self.rgbColor(rgb))
self.after(wait, self.animate, item, wait, rgb)
def addToQueue(self, widget, wait, start_after: int):
self.after(start_after, self.animate, widget, wait, [0, 0, 0])
def rgbColor(self, rgb):
return "#{0:02x}{1:02x}{2:02x}".format(*rgb)
if __name__ == '__main__':
mw = MainWindow()
x1, y1 = mw.winfo_screenwidth() / 2, mw.winfo_screenheight() / 2
x2, y2 = mw.winfo_screenwidth() / 4, mw.winfo_screenheight() / 4
mw.geometry("%dx%d+%d+%d" % (x1, y1, x2, y2))
mw.mainloop()

How to set a value in the tkinter slider.set method?

I am trying to simulate a stellar system. I aim to manipulate the parameters via slider widgets, see the (reduced) code below. The slider widget in my code accepts a new value for the sun mass, which was set via a StellarSys instance. However the slider.set method fails with TypeError: 'NoneType' object is not subscriptable. Does somebody have a solution or can explain what I'm doing wrong? Many thanks.
import tkinter as tk
import math
class Space(tk.Frame):
def __init__(self, master, size, bg=None):
super().__init__(master)
frame = tk.Frame(self, border=5)
frame.pack()
self.width, self.height = size
self.canvas = tk.Canvas(frame, width=self.width,height=self.height,
borderwidth=0, highlightthickness=0, bg=bg)
self.canvas.pack()
self.bodies = None
def place_bodies(self):
for body in self.bodies:
x1, y1 = int(body.loc[0]-body.size/2.0),int(body.loc[1]-body.size/2.0)
x2, y2 = x1 + body.size, y1 + body.size
body.tk_id = self.canvas.create_oval(x1, y1, x2, y2, fill=body.color)
class SpaceBody:
def __init__(self, **kwargs):
self.name = kwargs['name']
self.size = kwargs['size']
self.mass = kwargs['mass']
self.loc = kwargs['loc']
self.speed = kwargs['speed']
self.color = kwargs['color']
self.dxdy = (0,0)
self.tk_id = None
def __repr__(self):
return f"\n{self.name} is {self.color}"
class Dashboard(tk.Frame):
def __init__(self, master, bg=None):
super().__init__(master)
frame = tk.Frame(self, border=5, bg=bg)
frame.pack()
sun_frame=tk.Frame(frame)
sun_frame.grid(row=1)
w, h = 15, 3
tk.Label(sun_frame, text = '').grid(row=0)
tk.Label(sun_frame, text = 'SUN MASS').grid(row=1)
self.sun_mass = tk.Scale(sun_frame, from_=0, to=1000, orient='horizontal')
self.sun_mass.bind("<ButtonRelease-1>", self.update)
# self.sun_mass.set(500) # this works
self.sun_mass.set(space.bodies[0].mass) # This doesn't work
self.sun_mass.grid(row=2)
def update(self, event):
space.bodies[0].mass = self.sun_mass.get()
print(space.bodies[0].mass)
class StellarSys:
def __init__(self):
sun = SpaceBody(name='Sun', size=30, mass=500, loc=(500,400),speed=(0, 0), color='yellow')
earth = SpaceBody(name='Earth',size=15, mass=1, loc=(500,200),speed=(15,0), color='green')
space.bodies = [sun, earth]
space.place_bodies()
# MAIN
root = tk.Tk()
root.title('UNIVERSE')
size = (1000, 800)
space = Space(root, size, bg='black')
space.grid(row=0, column = 0,sticky="nsew")
dashboard = Dashboard(root)
dashboard.grid(row=0, column = 1,sticky="nsew")
stellarsys = StellarSys()
root.mainloop()
You initialize self.bodies to None, and then try to subscript that value. As the error says, you can't use subscripts on a value of None.
You need to rework your logic so that self.bodies is a non-empty list before trying to reference an item in the list.

How to implement a mouse hovering callback on canvas items in tkinter?

I used the code below which I found on internet to implement a mouse hovering action in python:
from tkinter import *
import numpy as np
class rect:
def __init__(self, root):
self.root = root
self.size = IntVar()
self.canvas = Canvas(self.root, width=800, height=300)
self.scale = Scale(self.root, orient=HORIZONTAL, from_=3, to=20, tickinterval=1, variable=self.size)
self.scale.bind('<ButtonRelease>', self.show)
self.canvas.bind('<Motion>', self.motion)
self.board = []
self.array = np.zeros((self.scale.get(),self.scale.get())).tolist()
self.canvas.pack()
self.scale.pack()
def motion(self,event):
if self.canvas.find_withtag(CURRENT):
current_color = self.canvas.itemcget(CURRENT, 'fill')
self.canvas.itemconfig(CURRENT, fill="cyan")
self.canvas.update_idletasks()
self.canvas.after(150)
self.canvas.itemconfig(CURRENT, fill=current_color)
def show(self,event):
self.canvas.delete('all')
x = 50
y = 50
row = []
self.board.clear()
for i in range(self.scale.get()):
row = []
for j in range(self.scale.get()):
rectangle = self.canvas.create_rectangle(x, y, x + 50, y + 50, fill='red')
x += 50
row.append(rectangle)
x -= j*50
y +=50
self.board.append(row)
print(self.board)
root = Tk()
a = rect(root)
root.mainloop()
The problem with the execution is that the color of the item changes to blue only for a limited time.
I need the color of each item in the canvas to be changed whenever I enter its zone and remain blue until the mouse is leaving the item.
You can pass the argument activefill when creating your rectangle.
From effboot.org:
Fill color to use when the mouse pointer is moved over the item, if
different from fill.
To do so, replace:
rectangle = self.canvas.create_rectangle(x, y, x + 50, y + 50, fill='red')
By:
rectangle = self.canvas.create_rectangle(x, y, x + 50, y + 50, fill='red', activefill='cyan')
This removes the need to bind Motion to your canvas, and also makes the code noticebly shorter:
from tkinter import *
import numpy as np
class rect:
def __init__(self, root):
self.root = root
self.size = IntVar()
self.canvas = Canvas(self.root, width=800, height=300)
self.scale = Scale(self.root, orient=HORIZONTAL, from_=3, to=20, tickinterval=1, variable=self.size)
self.scale.bind('<ButtonRelease>', self.show)
self.board = []
self.array = np.zeros((self.scale.get(),self.scale.get())).tolist()
self.canvas.pack()
self.scale.pack()
def show(self,event):
self.canvas.delete('all')
x = 50
y = 50
row = []
self.board.clear()
for i in range(self.scale.get()):
row = []
for j in range(self.scale.get()):
rectangle = self.canvas.create_rectangle(x, y, x + 50, y + 50, fill='red', activefill='cyan')
x += 50
row.append(rectangle)
x -= j*50
y +=50
self.board.append(row)
print(self.board)
root = Tk()
a = rect(root)
root.mainloop()
I changed motion method and added self.last = None to __init__ method:
from tkinter import *
import numpy as np
class rect:
def __init__(self, root):
self.root = root
self.size = IntVar()
self.canvas = Canvas(self.root, width=800, height=300)
self.scale = Scale(self.root, orient=HORIZONTAL, from_=3, to=20, tickinterval=1, variable=self.size)
self.scale.bind('<ButtonRelease>', self.show)
self.canvas.bind('<Motion>', self.motion)
self.board = []
self.array = np.zeros((self.scale.get(),self.scale.get())).tolist()
self.canvas.pack()
self.scale.pack()
self.last = None
def motion(self, event):
temp = self.canvas.find_withtag(CURRENT)
if temp == self.last:
self.canvas.itemconfig(CURRENT, fill="cyan")
self.canvas.update_idletasks()
else:
self.canvas.itemconfig(self.last, fill="red")
self.last = temp
def show(self,event):
self.canvas.delete('all')
x = 50
y = 50
row = []
self.board.clear()
for i in range(self.scale.get()):
row = []
for j in range(self.scale.get()):
rectangle = self.canvas.create_rectangle(x, y, x + 50, y + 50, fill='red')
x += 50
row.append(rectangle)
x -= j*50
y +=50
self.board.append(row)
print(self.board)
root = Tk()
a = rect(root)
root.mainloop()

Global tkinter entry box has no attribute 'get' [duplicate]

This question already has answers here:
Tkinter: AttributeError: NoneType object has no attribute <attribute name>
(4 answers)
Closed 7 years ago.
Let me start by saying I am new to Linux. I am working on modifying some code from github. The original program runs fine(a strip chart). When I try to enter the variables from the entry boxes I receive the following error:
Exception in Tkinter callback
Traceback (most recent call last):
File "/usr/lib/python3.2/tkinter/__init__.py", line 1426, in __call__
return self.func(*args)
File "/home/pi/SCRAP.py", line 49, in show_entry_fields
frequency = float (e1.get())
AttributeError: 'NoneType' object has no attribute 'get'
The code I have added is between the two editing start and editing end comments. Any help is greatly appreciated. The code I have is as follows:
from tkinter import *
import math, random, threading, time
class StripChart:
def __init__(self, root):
self.gf = self.makeGraph(root)
self.cf = self.makeControls(root)
self.gf.pack()
self.cf.pack()
self.Reset()
def makeGraph(self, frame):
self.sw = 1000
self.h = 200
self.top = 2
gf = Canvas(frame, width=self.sw, height=self.h+10,
bg="#002", bd=0, highlightthickness=0)
gf.p = PhotoImage(width=2*self.sw, height=self.h)
self.item = gf.create_image(0, self.top, image=gf.p, anchor=NW)
return(gf)
def makeControls(self, frame):
cf = Frame(frame, borderwidth=1, relief="raised")
Button(cf, text="Run", command=self.Run).grid(column=2, row=4)
Button(cf, text="Stop", command=self.Stop).grid(column=4, row=4)
Button(cf, text="Reset", command=self.Reset).grid(column=6, row=4)
#editing start
Button(cf, text="Cycle", command=self.show_entry_fields).grid(column=7, row=4)
Label(cf, text="Frequency(Hz)").grid(column=1, row=2)
Label(cf, text="P-P Current(mA)").grid(column=1, row=3)
global e1,e2
e1=Entry(cf).grid(column=2, row=2)
e2=Entry(cf).grid(column=2, row=3)
#editing end
self.fps = Label(cf, text="0 fps")
self.fps.grid(column=2, row=5, columnspan=5)
return(cf)
#editing start
def show_entry_fields(self):
#print("Frequency: %s\nMilliamps: %s\n" % (e1.get(),e2.get()))
frequency = float (e1.get())
currrent = float (e2.get())
#print(check_var.get())
print(frequency+1)
print(current+1)
#editing end
def Run(self):
self.go = 1
for t in threading.enumerate():
if t.name == "_gen_":
print("already running")
return
threading.Thread(target=self.do_start, name="_gen_").start()
def Stop(self):
self.go = 0
for t in threading.enumerate():
if t.name == "_gen_":
t.join()
def Reset(self):
self.Stop()
self.clearstrip(self.gf.p, '#345')
def do_start(self):
t = 0
y2 = 0
tx = time.time()
while self.go:
y1 = 0.2*math.sin(0.02*math.pi*t)
y2 = 0.9*y2 + 0.1*(random.random()-0.5)
self.scrollstrip(self.gf.p,
(0.25+y1, 0.25, 0.7+y2, 0.6, 0.7, 0.8),
( '#ff4', '#f40', '#4af', '#080', '#0f0', '#080'),
"" if t % 65 else "#088")
t += 1
if not t % 100:
tx2 = time.time()
self.fps.config(text='%d fps' % int(100/(tx2 - tx)))
tx = tx2
# time.sleep(0.001)
def clearstrip(self, p, color): # Fill strip with background color
self.bg = color # save background color for scroll
self.data = None # clear previous data
self.x = 0
p.tk.call(p, 'put', color, '-to', 0, 0, p['width'], p['height'])
def scrollstrip(self, p, data, colors, bar=""): # Scroll the strip, add new data
self.x = (self.x + 1) % self.sw # x = double buffer position
bg = bar if bar else self.bg
p.tk.call(p, 'put', bg, '-to', self.x, 0,
self.x+1, self.h)
p.tk.call(p, 'put', bg, '-to', self.x+self.sw, 0,
self.x+self.sw+1, self.h)
self.gf.coords(self.item, -1-self.x, self.top) # scroll to just-written column
if not self.data:
self.data = data
for d in range(len(data)):
y0 = int((self.h-1) * (1.0-self.data[d])) # plot all the data points
y1 = int((self.h-1) * (1.0-data[d]))
ya, yb = sorted((y0, y1))
for y in range(ya, yb+1): # connect the dots
p.put(colors[d], (self.x,y))
p.put(colors[d], (self.x+self.sw,y))
self.data = data # save for next call
def main():
root = Tk()
root.title("StripChart")
app = StripChart(root)
root.mainloop()
main()
You called grid() when you defined e1. Since you did this, e1 is set to what grid() returned, which is None:
>>> e1=Entry(cf).grid(column=2, row=2)
>>> e1
None
Instead, create the widget and then grid it on a separate line:
>>> e1=Entry(cf)
>>> e1.grid(column=2, row=2)
>>> e1
<Tkinter.Entry instance at 0x01D43BC0>

Categories

Resources