AttributeError: '_tkinter.tkapp' object has no attribute 'create_rectangle' - python

Trying to make a flappy bird clone and am having troubles with spawning in the object that will be the bird. I have tried to fix it by myself but I cannot think of why it isn't working. Tried to find if there was any fix online and I came up empty. I don't understand what the issue is and why it isn't spawning. I can't think of how to fix it. my code is here.
import tkinter
class Menu:
_BUTTON_HEIGHT = 2
_BUTTON_WIDTH = 10
_PLAY_BUTTON = "Start"
_EXIT_BUTTON = "Exit"
def __init__(self):
""""""
self.canvas = tkinter.Tk()
self.play_button = None
self.exit_button = None
self.starting_game_new = None
def canvas_create(self):
"""Creates a window for the frame to be displayed"""
self.canvas.geometry("500x700")
def menu_buttons(self):
""""""
self.play_button = tkinter.Button(self.canvas,
command=self.start_game,
text=Menu._PLAY_BUTTON,
width=Menu._BUTTON_WIDTH,
height=Menu._BUTTON_HEIGHT)
self.play_button.pack()
self.exit_button = tkinter.Button(self.canvas,
command=self.exit_game,
text=Menu._EXIT_BUTTON,
width=Menu._BUTTON_WIDTH,
height=Menu._BUTTON_HEIGHT)
self.exit_button.pack()
def start_game(self):
self.play_button.pack_forget()
self.exit_button.pack_forget()
self.starting_game_new = GameFrame(canvas=self.canvas)
self.starting_game_new.create_frame()
self.starting_game_new.create_bird()
def exit_game(self):
self.canvas.destroy()
class GameFrame:
_FRAME_HEIGHT = 700
_FRAME_WIDTH = 500
_FRAME_COLOUR = "dodger blue"
_X_BIRD = 200
_X_BIRD_2 = 250
_Y_BIRD = 150
_Y_BIRD_2 = 200
_BIRD_COLOUR = "gold2"
def __init__(self, canvas):
""""""
self.game_frame = None
self.canvas = canvas
self.bird = None
def create_frame(self):
"""Creates the game canvas and background"""
self.game_frame = tkinter.Canvas(self.canvas,
width=GameFrame._FRAME_WIDTH,
height=GameFrame._FRAME_HEIGHT,
background=GameFrame._FRAME_COLOUR)
self.game_frame.pack()
def create_bird(self):
self.bird = self.canvas.create_rectangle(GameFrame._X_BIRD,
GameFrame._Y_BIRD,
GameFrame._X_BIRD_2,
GameFrame._Y_BIRD_2,
fill=GameFrame._BIRD_COLOUR)
def main():
start = Menu()
start.canvas_create()
start.menu_buttons()
start.canvas.mainloop()
if __name__ == '__main__':
main()
Error code here:
Exception in Tkinter callback Traceback (most recent call last): File "(file)init.py", line 1883, in call return self.func(*args) File "", line 50, in start_game self.starting_game_new.create_bird() File "D:/Downloads/flappybirdgame-main/aewf.py", line 82, in create_bird self.bird = self.canvas.create_rectangle(GameFrame._X_BIRD, File "", line 2345, in getattr return getattr(self.tk, attr) AttributeError: '_tkinter.tkapp' object has no attribute 'create_rectangle'

The method create_rectangle is a method on your canvas. Your variable self.canvas is not a canvas, it's the root window.
You need to create a tkinter Canvas widget in order to draw a rectangle. It looks like you're doing so in this line:
self.game_frame = tkinter.Canvas(self.canvas, ...)
So, you need to call create_rectangle on this widget instead of self.canvas:
self.bird = self.game_frame.create_rectangle(...)

Related

How to automaticlly close or hide a tkinter window and be able to make it appear on conditions?

I'm trying to make a more user friendly screen crosshair for games like Apex Legends,because exsisted ones can't atuomaticlly go disappear when you returns to desktop,minimize the game window or switch to a browers while the game is still running with borderless window mode.I chose Python to make it because I'm most familiar with it(just started to learn coding recently).You can see my codes and error information below:
import time
from tkinter import *
class CrosshairWindow:
def __init__(self):
self.root = Toplevel()
self.root.attributes(
'-transparentcolor', '#ffffff',
'-topmost', '1'
)
self.root.overrideredirect(1)
self.screen_width = self.root.winfo_screenwidth()
self.screen_height = self.root.winfo_screenheight()
def start(self):
image = PhotoImage(file='crosshair.png')
canvas = Canvas(self.root, width=image.width(), height=image.height(), highlightthickness=0)
canvas.pack(fill='both')
canvas.create_image(0, 0, anchor=NW, image=image)
self.root.geometry(f'%sx%s+%s+%s' % (
image.width(), # width
image.height(), # height
round(self.screen_width/2 - image.width()/2), # x offset
round(self.screen_height/2 - image.height()/2), # y offset
))
self.root.lift()
self.root.mainloop()
if __name__ == '__main__':
import win32gui as w
print('Started!')
while(True):
title = w.GetWindowText(w.GetForegroundWindow())
print(title)
cw = CrosshairWindow()
while 1:
time.sleep(1)
print(title)
title = w.GetWindowText(w.GetForegroundWindow())
if title == "Apex Legends":
key = True
else:
key = False
print(title)
if key==True:
cw.start()
else:
cw.root.destroy()
C:\ProgramData\Anaconda3\python.exe "C:/Users/Hosisora_Ling/Documents/Tencent Files/2374416274/FileRecv/crosshair.py"
Started!
新建文件夹 – C:\Users\Hosisora_Ling\Documents\Tencent Files\2374416274\FileRecv\crosshair.py
Traceback (most recent call last):
File "C:\Users\Hosisora_Ling\Documents\Tencent Files\2374416274\FileRecv\crosshair.py", line 41, in <module>
cw.start(0)
File "C:\Users\Hosisora_Ling\Documents\Tencent Files\2374416274\FileRecv\crosshair.py", line 33, in start
self.root.lift()
File "C:\ProgramData\Anaconda3\lib\tkinter\__init__.py", line 1026, in tkraise
self.tk.call('raise', self._w, aboveThis)
_tkinter.TclError: can't invoke "raise" command: application has been destroyed
进程已结束,退出代码1#program ended with exit code 1
Please help point out the mistakes or a posible way to achieve my goal!

Tkinter: return self.func(*args) exception

I'm new to python and can't get a test program functioning. The goal was to keep rotating an image until it reaches the desired angle using tkinter GUI. The program runs fine but after reaching to the desired angle the console shows the following exception.
Traceback (most recent call last):
File "/usr/lib/python3.8/tkinter/__init__.py", line 1883, in __call__
return self.func(*args)
File "/usr/lib/python3.8/tkinter/__init__.py", line 804, in callit
func(*args)
StopIteration
And the code is :
import tkinter as tk
from PIL import ImageTk
from PIL import Image
import time
class SimpleApp(object):
def __init__(self, master, filename, **kwargs):
self.master = master
self.filename = filename
self.canvas = tk.Canvas(master, width=500, height=500)
self.canvas.pack()
self.update = self.draw().__next__
master.after(100, self.update)
def draw(self):
image = Image.open(self.filename)
angle = 0
while (angle<90):
tkimage = ImageTk.PhotoImage(image.rotate(angle))
canvas_obj = self.canvas.create_image(
250, 250, image=tkimage)
self.master.after_idle(self.update)
yield
angle += 5
time.sleep(0.1)
root = tk.Tk()
app = SimpleApp(root, 'cat.jpg')
root.mainloop()
Thanks in advance.
This is happening because when you reach the desired angle, the code doesn't enter your while loop, meaning no yield statement is encountered. Thus when you call self.draw().__next__, there is no next element to retrieve.
If you want to keep the current code, simply adding an else statement with a yield afterwards would solve your error
def draw(self):
image = Image.open(self.filename)
angle = 0
while (angle<90):
tkimage = ImageTk.PhotoImage(image.rotate(angle))
canvas_obj = self.canvas.create_image(
250, 250, image=tkimage)
self.master.after_idle(self.update)
yield
angle += 5
time.sleep(0.1)
else:
yield
However I see no point in using the next and yield statements in the function you've made, and would probably have done something like the below instead
import tkinter as tk
from PIL import ImageTk
from PIL import Image
import time
class SimpleApp(object):
def __init__(self, master, filename, **kwargs):
self.master = master
self.filename = filename
self.canvas = tk.Canvas(master, width=500, height=500)
self.canvas.pack()
self.image = Image.open(self.filename)
self.angle = 0
self.master.after(100, self.draw)
def draw(self):
while self.angle < 90:
tkimage = ImageTk.PhotoImage(self.image.rotate(angle))
canvas_obj = self.canvas.create_image(
250, 250, image=tkimage)
self.angle += 5
self.master.after(100, self.draw)
root = tk.Tk()
app = SimpleApp(root, 'cat.jpg')
root.mainloop()

python AttributeError: NoneType has no attribute

In my program snippet I create a python window with 2 fields and 3 buttons. The left two buttons should perform some action but instead an error is thrown:
Exception in Tkinter callback
Traceback (most recent call last):
File "/usr/lib64/python3.4/tkinter/__init__.py", line 1538, in __call__
return self.func(*args)
File ".../GuiFile.py", line 11, in <lambda>
self.F[2] = ButtonClass().make_button(stacked="left",buttontext= "Action button", buttoncommand = lambda: cf.mainButtons.doButtonAction1(self))
File ".../ClassFile.py", line 11, in doButtonAction1
print(gf.StartGui.F[0].textField.get("1.0","end-1c"))
AttributeError: 'NoneType' object has no attribute 'textField'
Why is dict item F[0] (created in line 9 of GuiFile.py) not recognized as Text() class with the attribute textField (defined in line 43 of GuiFile.py)?
MainProgramFile.py
#!/usr/bin/env python3
import sys
import ClassFile
import GuiFile as gf
if __name__== '__main__':
gf.StartGui().mainloop()
GuiFile.py
import sys
from tkinter import *
import ClassFile as cf
class StartGui(Frame):
F = {}
def __init__(self,parent=None):
Frame.__init__(self, parent)
self.F[0] = FieldTextClass().make_field(labeltext="Label of field 1", fieldtext="veld 1", fieldheight=90)
self.F[1] = FieldTextClass().make_field(labeltext="Label of field 2", fieldtext="veld 2")
self.F[2] = ButtonClass().make_button(stacked="left",buttontext= "Action button", buttoncommand = lambda: cf.mainButtons.doButtonAction1(self))
self.F[3] = ButtonClass().make_button(stacked="left", buttontext= "Exchange button", buttoncommand = lambda: cf.mainButtons.doButtonSwitchValues(self))
self.F[4] = ButtonClass().make_button(stacked="right",buttontext= "Quit button",buttoncommand = lambda: cf.mainButtons.doButtonQuit(self))
self.pack(expand=True, fill=BOTH, anchor="nw", side=LEFT)
#for i in range(self.F.__len__()): print(self.F[i].__class__,self.F[i].objectType)
class ButtonClass (Frame, Button):
objectType = "button"
def make_button(self, parent=None, stacked="horizontal", buttontext="Button", buttonwidth=120, buttonheight=32, buttoncommand=""):
self.buttonwidth=buttonwidth
self.buttonheight=buttonheight
self.buttontext=buttontext
self.buttoncommand=buttoncommand
if stacked=="vertical":
BUTTONSTACK = TOP
elif stacked=="right":
BUTTONSTACK = RIGHT
elif stacked=="horizontal" or stacked=="left":
BUTTONSTACK = LEFT
else:
BUTTONSTACK = LEFT
self.top = Frame(parent, height=self.buttonheight, width=self.buttonwidth)
self.top.pack_propagate(False)
self.top.pack(side=BUTTONSTACK)
button = Button(self.top, text=self.buttontext, command=self.buttoncommand,height=self.buttonheight, width=self.buttonwidth)
button.pack(fill=BOTH)
class FieldTextClass(Frame,Text,Label):
textField = None
objectType = "inputField"
def make_field(self, parent=None, labeltext="Empty", fieldtext="Empty", fieldwidth=600, fieldheight=20, labelwidth=120, labelheight=20):
self.fieldheight=fieldheight
self.fieldwidth=fieldwidth
self.fieldtext=fieldtext
self.labeltext=labeltext
self.labelheight=labelheight
self.labelwidth=labelwidth
self.top = Frame(parent)
#create the label, whith the text shifted left/top in a separate Frame
labelFrame = Frame(self.top, height = self.labelheight,width=self.labelwidth)
label = Label(labelFrame, text=self.labeltext, fg="black", anchor="nw")
label.pack(expand=True, fill=BOTH, anchor="nw", side=LEFT)
labelFrame.pack_propagate(False)
labelFrame.pack(side=LEFT, anchor="nw")
#create the text field, packed in a separate Frame
fieldFrame = Frame(self.top, height = self.fieldheight,width=self.fieldwidth)
self.textField = Text(fieldFrame, fg="black",bg="white")
self.textField.insert(INSERT,self.fieldtext)
self.textField.pack(expand=True, fill=BOTH, side=LEFT)
fieldFrame.pack_propagate(False)
fieldFrame.pack(side=LEFT)
self.top.pack(side=TOP)
ClassFile.py
import sys
from tkinter import *
import GuiFile as gf
class mainButtons():
def doButtonQuit(self):
print("Quitting test via ClassFile")
self.quit()
def doButtonAction1(self):
print(gf.StartGui.F[0].textField.get("1.0","end-1c"))
print(gf.StartGui.F[1].textField.get("1.0","end-1c"))
gf.StartGui.F[0].textField.delete("1.0","end")
gf.StartGui.F[0].textField.insert(INSERT, "New text")
def doButtonSwitchValues(self):
tmp0=gf.StartGui.F[0].textField.get("1.0","end-1c")
tmp1=gf.StartGui.F[1].textField.get("1.0","end-1c")
gf.StartGui.F[0].textField.delete("1.0","end")
gf.StartGui.F[0].textField.insert(INSERT, tmp1)
gf.StartGui.F[1].textField.delete("1.0","end")
gf.StartGui.F[1].textField.insert(INSERT, tmp0)
When you do ButtonClass().make_button() (or FieldTextClass.make_field()) , python will return the value of the function, not the instance of the class. The function returns None, so the dictionary elements are None.
The way you're using the custom classes is very strange. Instead of creating special functions, put that code in an __init__, and use the class like you would any other class.
For example:
class ButtonClass (Frame):
def __init__(self, parent=None, stacked="horizontal",
buttontext="Button", buttonwidth=120, buttonheight=32,
buttoncommand=""):
Frame.__init__(self, parent)
self.buttonwidth=buttonwidth
...
...
self.F[2] = ButtonClass(stacked="left",buttontext= "Action button", buttoncommand = lambda: cf.mainButtons.doButtonAction1(self))
Note: when doing it this way, you don't have to create a separate frame inside __init__ (ie: self.top), since self is itself already a Frame.

How to create StringVar in a class whose Tk() is defined in some other class?

Here I need to crate a DropDown class & later add it to the root or main GUI. But Tkinter.StringVar() is throwing an error saying
`Traceback (most recent call last):
File "D:/Testing/Tiks/main2.py", line 64, in <module>
d = Droppy(application)
File "D:/Testing/Tiks/main2.py", line 45, in __init__
self.control_variable = Tkinter.StringVar()
File "C:\Python26\lib\lib-tk\Tkinter.py", line 251, in __init__
Variable.__init__(self, master, value, name)
File "C:\Python26\lib\lib-tk\Tkinter.py", line 182, in __init__
self._tk = master.tk
AttributeError: 'NoneType' object has no attribute 'tk'
Exception AttributeError: "StringVar instance has no attribute '_tk'" in <bound method StringVar.__del__ of <Tkinter.StringVar instance at 0x0236F7B0>> ignored`
My code is this
import Tkinter
class App(object):
def __init__(self):
self.root = Tkinter.Tk()
############################
############################
self.root.mainloop()
class Droppy(object):
def __init__(self, frame=None):
# if frame is None:
# raise Exception
self.frame = frame
self.control_variable = Tkinter.StringVar()
self.control_variable.set("Choose Options")
self.dropDown = None
def dropIt(self):
self.dropDown = Tkinter.OptionMenu(self.frame.root, self.control_variable, "Rules", "Processing", "Output",
"Actions")
self.dropDown.pack()
if __name__ == '__main__':
application = App()
# application = Droppy()
# application.dropIt()
d = Droppy(application)
d.dropIt()
Now I know that Tkinter.Tk() before Tkinter.StringVar() can solve this issue but I cannot put Tkinter.Tk() and Tkinter.StringVar()` in the same class. How can I avert this issue ? Can anyone help please
mainloop is endless loop and it is running till you close Tk window.
You create Droppy when App is already closed.
You have to create object before mainloop - for example inside App.__init__.
import Tkinter
class App(object):
def __init__(self):
self.root = Tkinter.Tk()
############################
d = Droppy(self)
d.dropIt()
############################
self.root.mainloop()
class Droppy(object):
def __init__(self, frame=None):
# if frame is None:
# raise Exception
self.frame = frame
self.control_variable = Tkinter.StringVar()
self.control_variable.set("Choose Options")
self.dropDown = None
def dropIt(self):
self.dropDown = Tkinter.OptionMenu(self.frame.root, self.control_variable,
"Rules", "Processing", "Output",
"Actions")
self.dropDown.pack()
if __name__ == '__main__':
application = App()
As an alternative you can call your mainloop() in the main:
if __name__ == '__main__':
application = App()
d = Droppy(application)
d.dropIt()
application.root.mainloop()

Frame instance has no __call__ method

I am trying to change dashed lines into filled lines when you click on them. Instead, I get this error when clicking on a line:
Traceback (most recent call last):
File "/Users/dan/Documents/pyCatan/path_engine.py", line 106, in <module>
root.mainloop()
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-tk/Tkinter.py", line 1017, in mainloop
self.tk.mainloop(n)
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-tk/Tkinter.py", line 1414, in __call__
self.widget._report_exception()
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-tk/Tkinter.py", line 1175, in _report_exception
root = self._root()
AttributeError: Frame instance has no __call__ method
Common causes in similar questions were polluting variables with multiple uses, but I am not doing that here. Also, I have declared the method that is being called, which was the error in another, similar question.
Here is my noobie code:
from map_gen import MapGen
from gen_board import CatanApp
from Tkinter import *
class PathEngine(object):
'''Use the configuration options to show or hide certain attributes.'''
# show the edges explicitly
SHOW_EDGES = False
# show road intersections as nodes, explicitly
SHOW_NODES = True
# color the hexes to their resource color
COLOR_HEXES = False
CLICK_ADD_EDGES = True
# dimensions
HEIGHT = 600
WIDTH = 800
def __init__(self, root):
self._model = MapGen()
self._model.gen()
CatanApp.set_vertices(self._model.get_map())
self._model.prepare()
frame = Frame(root, height=PathEngine.HEIGHT, width=PathEngine.WIDTH)
frame.pack()
self._canvas = MapDrawer(frame)
self.render()
self._canvas.config(height=PathEngine.HEIGHT, width=PathEngine.WIDTH)
self._canvas.pack()
def render(self):
if PathEngine.SHOW_NODES:
for node in self._model.get_nodes():
self._canvas.draw_node(*node)
self.add_edges()
def add_edges(self):
for edge in self._model.get_roads():
if PathEngine.CLICK_ADD_EDGES:
self._canvas.draw_dashed_edge(edge[0][0], edge[0][1], edge[1][0], edge[1][1])
class MapDrawer(Canvas):
NODE_RADIUS = 20
def __init__(self, master):
Canvas.__init__(self, master)
self._root = master
def draw_dashed_edge(self, x1, y1, x2, y2, color=None):
if color is None:color = "black"
t = "road_%s_%s" % (str((x1, y1)), str((x2, y2)))
self.create_line(
x1,
y1,
x2,
y2,
fill=color,
dash=(1, 1),
width=3,
tags=("road", t)
)
f = lambda event: self.solidify_dashed_edge(t)
self.tag_bind(t, "<Button-1>", f)
def solidify_dashed_edge(self, tag):
self.itemconfigure(tag, dash=(0, 1))
def draw_node(self, x, y, color=None):
if color is None: color = "white"
self.create_oval(
x - MapDrawer.NODE_RADIUS / 2,
y - MapDrawer.NODE_RADIUS / 2,
x + MapDrawer.NODE_RADIUS / 2,
y + MapDrawer.NODE_RADIUS / 2,
fill=color,
outline="black"
)
if __name__ == "__main__":
root = Tk()
engine = PathEngine(root)
root.mainloop()
It looks like you've hit a name conflict with adding an attribute _root to your Canvas:
>>> import Tkinter as tk
>>> a = tk.Canvas()
>>> print a._root
<bound method Canvas._root of <Tkinter.Canvas instance at 0xee1c0>>
It's one of the hazards of working in python where there is no private data :-). Notice that _root is a method of Canvas objects. You override that method with an instance attribute:
class MapDrawer(Canvas):
NODE_RADIUS = 20
def __init__(self, master):
Canvas.__init__(self, master)
self._root = master
Where master is a Frame object.

Categories

Resources