I'm a relatively new programmer, and i'm in the process of making a game. I am using some code from my previous project which ran fine. But now when i try to call a certain function, which i don't think should need any parameters, it returns some strange errors.
i have this class which i have copied from my previous project:
import pyglet as p
class Button(object):
def __init__(self, image, x, y, text, on_clicked):
self._width = image.width
self._height = image.height
self._sprite = p.sprite.Sprite(image, x, y)
self._label = p.text.Label(text,
font_name='Times New Roman',
font_size=20,
x=x + 20, y=y + 15,
anchor_x='center',
anchor_y='center')
self._on_clicked = on_clicked # action executed when button is clicked
def contains(self, x, y):
return (x >= self._sprite.x - self._width // 2
and x < self._sprite.x + self._width // 2
and y >= self._sprite.y - self._height // 2
and y < self._sprite.y + self._height // 2)
def clicked(self, x, y):
if self.contains(x, y):
self._on_clicked(self)
def draw(self):
self._sprite.draw()
self._label.draw()
i have my window event that calls the function (w is the window):
#w.event
def on_mouse_press(x, y, button, modifiers):
for button in tiles:
button.clicked(x, y)
and three variations of the function it calls that each has a different 'errors':
def phfunc(a):
print(a)
returns this thing: <Button.Button object at 0x0707C350>
def phfunc(a):
print('a')
returns: a
which it actually should
def phfunc():
print('a')
returns a long list of callbacks resulting in this:
File "C:\Google Drive\game programmeren\main.py", line 15, in on_mouse_press
button.clicked(x, y)
File "C:\Google Drive\game programmeren\Button.py", line 25, in clicked
self._on_clicked(self)
TypeError: phfunc() takes no arguments (1 given)
my best guess is that the argument it has is the self from the Button class. is this correct, and should i worry about this?
You call the function reference stored in self._on_clicked with self as a parameter. self is the instance of your Button class:
self._on_clicked(self)
The default representation for your custom Button class is <Button.Button object at 0x0707C350>.
Since you did so explicitly, that is not something to worry about.
Related
Im making a paint program and I keep running into this error
however if I change where event.x /event.y are defined the same error comes up but with a different subroutine
here is the code:
class paint:
def __init__(self,root,canvas):
self.root = root
self.mouse_press = False #mouse isnt being pressed
self.canvas = canvas
self.canvas.bind('<ButtonPress-1>',self.MouseDown) # when left click is pressed / down the subroutine MouseDown is opened
self.canvas.bind('<ButtonRelease-1>',self.MouseUp)#when left click is relased open the MouseUp subroutine
def MouseDown(self,event):
self.x = event.x
self.y = event.y
self.mouse_press = True # mouse is pressed
self.poll() #calls the coodinates subroutine
def MouseUp(self,event):
self.poll()
self.mouse_press = False
self.root.after_cancel(self.after_id)
def poll(self):
if self.mouse_press:
canvas.create_oval(self.x,self.y, self.x+10, self.y+10 , fill = '#f6f65a',outline = '#f6f65a')
self.after_id = self.root.after(10,self.MouseDown)
def colour_pick():
colour_choose = colorchooser.askcolor()[1] #opens a colour picker and picks the colour
print(colour_choose)
return(colour_choose)
how I am calling the class
p = paint(root,canvas)
root.mainloop()
the error I keep getting:
Traceback (most recent call last):
File "C:\Users\user\AppData\Local\Programs\Python\Python310\lib\tkinter\__init__.py", line 1921, in __call__
return self.func(*args)
File "C:\Users\user\AppData\Local\Programs\Python\Python310\lib\tkinter\__init__.py", line 839, in callit
func(*args)
TypeError: paint.MouseDown missing 1 required positional argument: 'event'
Your function MouseDown requires an event parameter since you defined it like this:
def MouseDown(self,event):
However, when you call the function from after, you're not passing this argument:
self.after_id = self.root.after(10,self.MouseDown)
Since MouseDown uses the data in the event object, you will need to synthesize an object with the attributes used by the function (ie: .x and .y) or you need to rewrite MouseDown so that it doesn't require an event parameter.
The cause of the error has been explained by Bryan's answer.
For your case, binding on events <Button-1> and <B1-Motion> is enough and don't need to use .after():
class paint:
def __init__(self, root, canvas):
self.root = root
self.canvas = canvas
self.canvas.bind('<Button-1>', self.mouse_down)
self.canvas.bind('<B1-Motion>', self.poll)
def mouse_down(self, event):
# save initial drag point
self.x, self.y = event.x, event.y
def poll(self, event):
# draw line from last saved point to current point instead of drawing circle
self.canvas.create_line(self.x, self.y, event.x, event.y, fill='#f6f65a', width=10, capstyle='round')
# save current point
self.x, self.y = event.x, event.y
You are working too hard in trying to fake <Motion>. The below illustrates what I believe you are actually trying to do.
import tkinter as tk
class Canvas(tk.Canvas):
def __init__(self, master, **kwargs):
tk.Canvas.__init__(self, master, **{'background':'#000000', 'highlightthickness':0, **kwargs})
self.bind('<Motion>', self.motion)
#instead of attempting to programmatically toggle up and down states
def motion(self, e):
if (e.state & 256) == 256: #if mouse 1 is down
self.create_oval(e.x, e.y, e.x+10, e.y+10, fill='#f6f65a', outline='#f6f65a') #fill and outline should actually come from your ColorChooser
class Root(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.paint = Canvas(self)
self.paint.pack(expand=True, fill=tk.BOTH)
if __name__ == '__main__':
Root().mainloop()
I'm a beginner and i'm trying to make paint with python turtle but my code gives an error. I've tried everything I could think of but it still isn't working.
from turtle import *
from menuitem import MenuItem
def changePenColor(c):
"""Changes the system turtle's color to c."""
color(c)
def createMenu(callBack):
"""Displays 6 menu items to respond to the given callback function."""
x = - (window_width() / 2) + 30
y = 100
colors = ('red', 'green', 'blue', 'yellow', 'black', 'purple')
shape = "circle"
for color in colors:
MenuItem(x, y, shape, color, callBack)
y -= 30
def main():
"""Creates a menu for selecting colors."""
reset()
shape("turtle")
createMenu(color)
return "done!"
if __name__=='__main__':
msg = main()
print(msg)
mainloop()
And this code in a different file:
from turtle import Turtle
class MenuItem(Turtle):
"""Represents a menu item."""
def __init__(self, x, y, shape, color, callBack):
"""Sets the initial state of a menu item."""
Turtle.__init__(x, y, self, shape = shape, visible = False)
self.speed(0)
self.up()
self.goto(x, y)
self.color(color, color)
self._callBack=callBack
self.onclick(lambda x,y: self._callBack(color))
self.showturtle()
If anyone knows what I can do to fix this, I'd be happy to know.
Thanks 😊
Your code is somewhat confused. Specifically:
from turtle import *
Just don't. Particularly in a module. Import as little as you need to get the job done.
createMenu(color)
This should be createMenu(changePenColor) and changePenColor() should be defined in the main module, not the MenuItem class module.
Turtle.__init__(x, y, self, shape = shape, visible = False)
first three arguments to __init__ shouldn't be there and you should use super, all as #Evan notes.
reset()
self._callBack=callBack
These two statments are effectively no-ops and can be left out.
Below is my rework of your code that I believe does what you're attempting to do. For example purposes, instead of the main module, I just used a if __name__ == '__main__': for testing:
from turtle import Screen, Turtle
COLORS = ('red', 'green', 'blue', 'yellow', 'black', 'purple')
CURSOR_SIZE = 20
class MenuItem(Turtle):
''' Represents a menu item. '''
def __init__(self, x, y, shape, color, callBack):
''' Sets the initial state of a menu item '''
super().__init__(shape=shape, visible=False)
self.penup()
self.goto(x, y)
self.color(color)
self.onclick(lambda x, y: callBack(color))
self.showturtle()
def createMenu(callBack):
''' Displays 6 menu items to respond to the given callback function. '''
screen = Screen()
x = CURSOR_SIZE * 1.5 - screen.window_width() / 2
y = 100
for color in COLORS:
MenuItem(x, y, 'circle', color, callBack)
y -= CURSOR_SIZE * 1.5
if __name__ == '__main__':
from turtle import getscreen, getturtle
def changePenColor(c):
''' Changes the turtle's color to c. '''
turtle.color(c)
screen = getscreen() # singular screen instance
turtle = getturtle() # default turtle
turtle.shape('turtle')
# Create a menu for selecting colors.
createMenu(changePenColor)
screen.mainloop()
In your first line of the __init__ function on your MenuItem class, use this
super().__init__(shape=shape, visible=False)
instead of
Turtle.__init__(x, y, self, shape = shape, visible = False)
You don't need to pass in x, y, or self, because you are already setting the position by saying self.goto(x, y). Also, use super() instead of Turtle, because you need to initialize the superclass, not just another instance of Turtle. By saying Turtle.__init__(...) you are creating an instance of that object and doing nothing with it. By saying super().__init__(...), you are initializing the superclass of your object, which is what you always need to do when you are subclassing an object (in this case Turtle).
Note: your __init__ function needs to be indented, but I'll assume that was a pasting error.
I'm using python's arcade library and I keep getting the "NameError: name 'window' is not defined" error. I tried deleting the main function and just running the
window = MyGame()
for button in window.buttonList:
button.draw()
arcade.run() without the function around it, but now I need to run this from another file without using os.exec or subprocess while still being able to go back and run the first one. I need to use a main function, but I don't know how to do it without raising an error. Here is my code
from subprocess import call
from tkinter import *
from tkinter import filedialog
SCREEN_WIDTH = 600
SCREEN_HEIGHT = 600
SCREEN_TITLE = "Menu"
class MenuItem():
def __init__(self, x, y, width, height):
self.x = x
self.y = y
self.width = width
self.height = height
def draw(self):
pass
def func(self):
pass
class FreeDraw(MenuItem):
def func(self):
window.close()
window.set_visible(False)
#run other file
def draw(self):
window.buttonShapes.append(arcade.create_rectangle_outline(self.x, self.y, self.width, self.height, arcade.color.BLACK))
class MyGame(arcade.Window):
""" Our custom Window Class"""
def __init__(self):
""" Initializer """
# Call the parent class initializer
super().__init__(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
# Set the working directory (where we expect to find files) to the same
# directory this .py file is in. You can leave this out of your own
# code, but it is needed to easily run the examples using "python -m"
# as mentioned at the top of this program.
file_path = os.path.dirname(os.path.abspath(__file__))
os.chdir(file_path)
self.buttonList = [FreeDraw(SCREEN_WIDTH/2, 100, 400, 100)]
self.buttonShapes = arcade.ShapeElementList()
arcade.set_background_color(arcade.color.ASH_GREY)
def setup(self):
pass
def on_draw(self):
""" Draw everything """
arcade.start_render()
arcade.draw_text("Free Draw", (self.buttonList[0].x - self.buttonList[0].width / 2) + 115,
self.buttonList[0].y - 25,
arcade.color.BLACK, 30)
self.buttonShapes.draw()
def on_key_press(self, key, modifiers):
pass
def on_key_release(self, key, modifiers):
pass
def on_mouse_motion(self, x: float, y: float, dx: float, dy: float):
pass
def on_mouse_release(self, x: float, y: float, button: int,
modifiers: int):
for button in self.buttonList:
if x <= button.x + (button.width / 2) and x >= button.x - (button.width / 2):
if y <= button.y + (button.height / 2) and y >= button.y - (button.height / 2):
button.func()
self.buttonList[0].func()
def main():
window = MyGame()
for button in window.buttonList:
button.draw()
arcade.run()
main()
You can use arcade.View instead of arcade.Window in your separate classes. Example:
import arcade
class View1(arcade.View):
def on_draw(self):
arcade.start_render()
arcade.draw_text('View 1', 300, 200, arcade.color.RED, font_size=30, anchor_x='center')
def on_mouse_press(self, _x, _y, _button, _modifiers):
self.window.show_view(View2())
class View2(arcade.View):
def on_draw(self):
arcade.start_render()
arcade.draw_text('View 2', 300, 200, arcade.color.RED, font_size=30, anchor_x='center')
def on_mouse_press(self, _x, _y, _button, _modifiers):
self.window.show_view(View1())
window = arcade.Window(600, 400)
window.show_view(View1())
arcade.run()
Output:
I created a class called Thing for Pygame, which will later be subclassed to develop more specific classes and objects. This class needs to store among other attributes, the position, and size of the object.
However, Pygame has this very powerful class called Rect that can already do that, and has many useful built-in methods and properties. I could use a single Rect attribute in my Thing class, but it stores position in integers, and I'm looking to store it in floats (a Thing might need to move less than one pixel per frame).
This I what I had in mind.
class Thing:
def __init__(self, pos, size):
self.x: float = pos[0]
self.y: float = pos[1]
self.size = size
#property
def rect(self):
return pygame.Rect((self.x, self.y), self.size)
#rect.setter
def rect(self, var: pygame.Rect):
self.x = var.x
self.y = var.y
self.size = var.size
This would allow me to get and set the Thing's Rect, but I wouldn't be able to use the Rect's built-in setters as desired:
foo = Thing((10, 10), (20, 20))
foo.draw(win)
foo.x += 100
foo.draw(win)
# This has no effect
foo.rect.y += 100
foo.draw(win)
temp = foo.rect
temp.x -= 100
foo.rect = temp
foo.draw(win)
The desired behavior for the above code would be to have the square move right, down, then left.
Is there a way to either:
Have a Rect as a Thing attribute, but update x and y whenever it's changed?
Apply methods to Rect property
Some other solution you can think of
TL;DR: If I have a Rect as a property, I can't do self.rect.center = (500,500)
Thanks in advance!
Your class should inherit from pygame.Rect. That is the only way to get the Rect methods automatically. However by doing this you will inherits also the int typecasting of the coordinates since that is in the original implementation of pygame.Rect. I'm afraid inheritance won't solve your problem.
You know what the Rect methods are supposed to do (The documentation is well written), so I'm afraid the only way is to reimplement them (or at least reimplement those you need) for your own Thing class, to mimic the Rect behaviour with float numbers.
I did something similar: here is a portion of the class I wrote (I won't show it all because is too long) to give you an idea:
class FlRect:
"""Similar to pygame.Rect but uses float numbers.
The class stores internally only coordinates, width and height.
Other attributes are rendered through properties, with getter and setter:
x, y: coordinates of the top-left corner of the rectangle.
top, bottom: y coordinates of the top and bottom edges respectively.
left, right: x coordinates of the left and right edges respectively.
centerx, centery: coordinates of the centre of the rectangle.
width, height: self-explanatory.
"""
def __init__(self, x, y, w, h):
"""Initialization:
x, y - coordinates of top-left corner of the rectangle
w, h - width and height
"""
self._x = x
self._y = y
self._w = w
self._h = h
#property
def x(self):
return self._x
#x.setter
def x(self, value):
self._x = value
#property
def y(self):
return self._y
#y.setter
def y(self, value):
self._y = value
#property
def width(self):
return self._w
#width.setter
def width(self, value):
self._w = value
#property
def height(self):
return self._h
#height.setter
def height(self, value):
self._h = value
#property
def top(self):
return self._y
#top.setter
def top(self, value):
self._y = value
#property
def bottom(self):
return self._y + self._h
#bottom.setter
def bottom(self, value):
self._y = value - self._h
#property
def left(self):
return self._x
#left.setter
def left(self, value):
self._x = value
#property
def right(self):
return self._x + self._w
#right.setter
def right(self, value):
self._x = value - self._w
#property
def centerx(self):
return self._x + (self._w / 2)
#centerx.setter
def centerx(self, value):
self._x = value - (self._w / 2)
#property
def centery(self):
return self._y + (self._h / 2)
#centery.setter
def centery(self, value):
self._h = value - (self._h / 2)
def get_rect(self):
"""Return a pygame.Rect object with rounded coordinates"""
return Rect(round(self._x), round(self._y), round(self._w), round(self._h))
Of course this is not going to be as efficient as pygame.Rect, since this is written in python and the pygame Rect class is in C.
Beginner here trying to make sense of classes. Below is the code for my class Cell:
import tkinter
import random
top = tkinter.Tk()
canvas = tkinter.Canvas(top, bg="grey", height=400, width=400)
canvas.pack()
class Cell:
def __init__(self, x, y, r):
self.x = random(x)
self.y = random(y)
self.r = 200
def show(self):
canvas.create_oval(self.x, self.y, self.r, self.r, fill = "blue")
top.mainloop()
I'm attempting to draw the cell in my main program by calling the function show from the class. Here is the code for my main window:
import tkinter
top = tkinter.Tk()
canvas = tkinter.Canvas(top, bg="grey", height=400, width=400)
canvas.pack()
from Cell import Cell
cell = Cell()
cell.show()
top.mainloop()
This is resulting in the canvas being drawn correctly, but the oval is nowhere to be found. I am not getting any errors either.
Any help would be appreciated. Thank you!
====================
Turns out, I misunderstood the arguments for create_oval. I found some code that converts the clunky create_oval function into a function which receives a set of coordinates for the center of the oval and a radius.
In addition to this, the help I received in understanding classes and other Python functionality helped significantly as well. Thanks to those who helped!
This is my revised code which works as intended.
import tkinter as tk
import random
top = tk.Tk()
canvas = tk.Canvas(top, width=400, height=400, bg="grey")
canvas.grid()
def _create_circle(self, x, y, r, **kwargs):
return self.create_oval(x-r, y-r, x+r, y+r, **kwargs)
tk.Canvas.create_circle = _create_circle
class Cell:
def __init__(self, canvas, x, y, r):
self.canvas = canvas
self.x = x
self.y = y
self.r = r
def show(self):
self.canvas.create_circle(self.x, self. y, self.r, fill = "blue")
cell = Cell(canvas, random.randrange(50, 350), random.randrange(50, 350), 25)
cell.show()
top.mainloop()
The problem here is that your main script and your Cell module are both creating a new Tk instance, adding a Canvas to it, and then calling its mainloop method.
If you trace through the order in which statements get executed, you'll find that the cell = Cell() and cell.show() don't happen until after the first top.mainloop() returns, and mainloop() doesn't return until you quit the program. (In fact, if your code did get that far, it would fail with a TypeError, which I'll get to below.)
But, more generally, you only want one Tk in your program, and everyone else should refer to that.
And, in this case, you want the same for the Canvas: just one of them, packed onto the one Tk main window.
So, how can Cell.show refer to the canvas global from another module?
The best solution is to not refer to it as a global at all, and instead pass it in to the initializer, the same way you do with x, y, and r:
class Cell:
def __init__(self, canvas, x, y, r):
self.canvas = canvas
self.x = random(x)
self.y = random(y)
self.r = 200
def show(self):
self.canvas.create_oval(self.x, self.y, self.r, self.r, fill = "blue")
And then in the main script:
cell = Cell(canvas, ?, ?. ?)
cell.show()
But notice those ?s I put there. Your Cell class definition demands x, y, and r values in its initializer, but your Cell() constructor call doesn't pass any. That will raise a TypeError complaining that you're missing required arguments.
What do you want to pass here? Since the canvas is 400x400, maybe you want to pass something like 400, 400, 200? If so:
cell = Cell(canvas, 400, 400, 200)
cell.show()
Going back to that initializer, you've got some other problems there:
self.x = random(x)
self.y = random(y)
That random is a module. You can't call a module. You probably wanted something like this:
self.x = random.randrange(x)
That calls a function from the random module, one which is defined to return a random number in range(0, x), which seems like what you want.
Also:
self.r = 200
Why take an r parameter, just to ignore it? You probably wanted this:
self.r = r
Or, maybe you didn't actually want x, y, and r as parameters? Maybe you want to hardcode randrange(400), randrange(400), and 200, or maybe you want to compute them from the width and height of the canvas parameter, or… you can do almost anything you want, you just have to think through what you want, and make sure the interface you declare in the def matches the way you call it in the Cell(…) later.
I think your execution path is never getting to the cell.show() line.
When you import Cell, you have code at the top level top.mainloop(). This enters the main loop and never exits, so you never get to the lines below it.
It's a good rule of thumb to avoid putting code at the base level. Leave that for defining classes and functions. If you want code to run when the file is called like a script, put it in a if __name__ == __main__: condition.
You also had some syntax issues using random and calling the Cell constructor.
The example below works as expected.
import tkinter
import random
top = tkinter.Tk()
canvas = tkinter.Canvas(top, bg="grey", height=400, width=400)
canvas.pack()
class Cell:
def __init__(self, x, y, r):
self.x = x
self.y = y
self.r = r
def show(self):
canvas.create_oval(self.x, self.y, self.r, self.r, fill = "blue")
if __name__ == "__main__":
cell = Cell(100, 50, 5)
cell.show()
top.mainloop()