I have a working program, but if I change my pygame.Rect() constructor / function, then my program crashes.
If you look at the code below, then
button_rect1 = pygame.Rect(450, 300, 100, 100)
works just fine. But if I change it to
button_rect2 = pygame.Rect(left = 450, top = 300, width = 100, height = 100)
then I get the error:
Traceback (most recent call last):
File "F:\_python_projects\workshift_logger\component_testing.py", line 31, in <module>
button_rect = pygame.Rect(left = 450, top = 300, width = 100, height = 100)
TypeError: Argument must be rect style object
[Finished in 0.7s with exit code 1]
The only thing I've changed is adding names to the arguments. Why can't I do this, and how can I change my code so I can name my arguments (I want to name them for clarity's sake - in some API's rectangles are (x, y, width, height), in others they are (x1, y1, x2, y2)):
Here's a minimal, standalone example:
##### Imports ######
import pygame
##### Settings #####
window_width = 1000
window_height = 600
##### Program #####
pygame.init()
window = pygame.display.set_mode((window_width, window_height))
button_rect1 = pygame.Rect(450, 300, 100, 100) # This works
button_rect2 = pygame.Rect(left = 450, top = 300, width = 100, height = 100) # This doesn't work
Keyword arguments support for, at least, some was added in 2.0.0 as per the official documentation.
Changed in pygame 2.0.0: Added support for keyword arguments.
So Pygame does support keyword arguments (if the version is >= 2.0.0) .. at least for some.
As per the documentation here, you can instantiate a Rect using three methods:
Rect(left, top, width, height) -> Rect
Rect((left, top), (width, height)) -> Rect
Rect(object) -> Rect
May be, still, there is not a support for the pygame.rect even though there is support for pygame.draw.rect ... in terms of keyword arguments. May be your version is less than 2.0.0
Not sure if I was able to answer the question.
Related
I am trying to make a button in pygame, which is meant to be part of my Snake game but I keep getting this error. I am not sure what I have done wrong.
import pygame
import sys
pygame.init()
GREEN = '#00FF00'
gameFont = pygame.font.SysFont('Courier', 32)
class button():
def __init__(self , x, y, w, h, t_inp):
self.x = x
self.y = y
self.w = w
self.h = h
self.t_inp = t_inp
self.colour = GREEN
self.text = gameFont.render(self.t_inp, True, self.colour)
self.text_rect = self.text.get_rect(center=(self.x, self.y))
def draw(self):
buttonRect = Rect(self.rect.x, self.rect.y, self.rect.w, self.rect.h)
button1 = button('Start Game', 200, 40,(400,400))
window_size = (800,800)
screen = pygame.display.set_mode((window_size))
pygame.display.set_caption('Snake')
clock = pygame.time.Clock()
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
#
#
pygame.display.update()
if someone could help it would be greatly appreciated, thank you
The object button1 is initialized with 4 params instead of 5 (x, y, w, h, t_inp)
The problematic line is here:
button1 = button('Start Game', 200, 40,(400,400))
If we look at the __init__ method for a button, we have this:
def __init__(self , x, y, w, h, t_inp):
The first parameter, self, refers to the object, and you ignore it when you instantiate a new object. Then there are five parameters you do need to provide.
However, you are only providing four. Note that (400, 400) is a single argument of the type tuple. It is made into a tuple by the parentheses. You either need to remove those or figure out what argument you are actually missing. (It's unclear to me if it's supposed to be a tuple - that is dependent on what the functions that value is sent to are expecting.)
Note that you have also made window_size a tuple, and also parenthesized it when passing it to set_mode(). This may also be incorrect, but unrevealed since you're errorring earlier.
The constructor requires 5 arguments (x, y, w, h, t_inp). (By the way, by convention, class names begin with a capital). Here are some valid calls:
Button(200, 40, 400, 400, "Start Game")
Button(200, 40, t_inp="Start Game", w=400, h=400)
dimensions = (400, 400)
# Button(200, 40, dimensions, "Start Game") is not valid
# you should use a star * to unpack the width and height:
Button(200, 40, *dimensions, "Start Game")
coordinates = (200, 40)
Button(*coordinates, *dimensions, "Start Game")
In your function that you defined:
class button():
def __init__(self , x, y, w, h, t_inp):
you mention 5 arguments but when calling the function, you only provide 4 arguments:
button1 = button('Start Game', 200, 40,(400,400))
You need to provide one more argument
I've noticed a strange blip with Python Turtle Graphics when using Screen.setworldcoordinates(). For some reason when I click on the window title bar after running the code below, there is a small but perceptible shift of the contents of the window. Can anyone please explain this phenomenon, and let me know if there is way to avoid it? I'm on Windows 10 with Python 3.8.
import turtle
screen = turtle.Screen()
screen.setup(500, 500) # Set the dimensions of the Turtle Graphics window.
screen.setworldcoordinates(0, screen.window_height(), screen.window_width(), 0)
my_turtle = turtle.Turtle(shape="circle")
my_turtle.color("red")
my_turtle.forward(10)
turtle.done()
turtle comes with it's own default config, that uses 50% of your monitor width, and 75% of it's height.
_CFG = {"width" : 0.5, # Screen
"height" : 0.75,
"canvwidth" : 400,
"canvheight": 300,
...
}
There's some interplay between various elements during construction.
self._canvas = TK.Canvas(master, width=width, height=height,
bg=self.bg, relief=TK.SUNKEN, borderwidth=2)
Setting borderwidth to any value above 3 alleviates it; believe that's because it calls for a complete screen redraw.
/usr/lib/python3.9/turtle.py is read-only, though.
Made a local copy with write privs, then modded it for debugging purposes. Named turtel.py instead, just to keep changes seperate. Put in a few print() statements to figure out when functions were running.
Method 1:
You can fix it by forcing new values, before screen construction.
Either by supplying your own turtle.cfg in the same directory as your script, or
Method 2:
overriding those values:
import turtle
Width, Height = 500, 500
turtle._CFG['canvwidth'], turtle._CFG['canvheight'] = Width, Height
screen = turtle.Screen()
Method 3:
_setscrollregion(self, srx1, sry1, srx2, sry2) calls _rescale(self, xscalefactor, yscalefactor)
but doesn't call adjustScrolls(self) until onResize(self, event) is called, after you drag the titlebar.
So you can force a redraw after screen.setworldcoordinates(0, Height, Width, 0)
cv = screen.getcanvas()
cv.adjustScrolls()
Uncomment ## to enable lines, and try.
#! /usr/bin/python3
import turtle
## print( turtle._CFG['width'], turtle._CFG['height'] ) ## 0.5 0.75
## print( turtle._CFG['canvwidth'], turtle._CFG['canvheight'] ) ## 400 300
Width, Height = 500, 500
## turtle._CFG['width'], turtle._CFG['height'] = Width, Height
## turtle._CFG['canvwidth'], turtle._CFG['canvheight'] = Width, Height
screen = turtle.Screen()
screen.setworldcoordinates(0, Height, Width, 0)
## cv = screen.getcanvas()
## cv.adjustScrolls()
my_turtle = turtle.Turtle(shape='circle')
my_turtle.color('red')
my_turtle.forward(10)
def click_callback( x, y ):
cv = screen.getcanvas()
print( cv.width, cv.height, screen.screensize(), cv.winfo_width(), cv.winfo_height() )
print('<< initialized >>')
screen.onclick( click_callback )
screen.mainloop()
Why do shapes and other objects in pyglet have to be named (as in assigned variables) ? The Rectangle below named "test" renders successfully, the one a line below does not. Why is that?
from pyglet import shapes
WHITE = (255, 255, 255)
HEIGHT = 1080
WIDTH = 720
window = pyglet.window.Window(HEIGHT, WIDTH)
background = pyglet.graphics.Batch()
test = shapes.Rectangle(200, 200, 50, 50, color=WHITE, batch=background) # Renders successfuly
shapes.Rectangle(300, 300, 50, 50, color=WHITE, batch=background) # Does not render
#window.event
def on_draw():
window.clear()
background.draw()
pyglet.app.run()
Here you are using pyglet.graphics.Batch() to render the shape objects together i.e. test(the first rectangle) + the second rectangle. A batch manages a set of objects that will be drawn at once, and hence to instantiate the objects that need to be drawn, they need to have a reference against which they can be mapped to that batch.
To draw a rectangle as is, without assigning it to a varible can be done by using the pyglet.graphics.draw() function directly by passing in the primitive type, rectanggle coordinates and formatstring.
I have a program that needs to often write text on the screen on a textbox. Since I'm already using pygame for something else here and have a separate file for my functions, I thought about going about it this way. As it stands right now my code looks like this:
In the main file:
import mainengine as me, pygame
pygame.init()
pygame.font.init()
screenSize = width, height = 1280, 720
screen = pygame.display.set_mode(screenSize)
pygame.display.set_caption('test')
bg = pygame.image.load("test.jpg")
screen.blit(bg, [0, 0])
me.text('test', 'how do code')
pygame.display.flip()
Then in the secondary file:
def text(speaker, text):
import pygame
white = (255, 255, 255)
gray = (200, 200, 200)
Sfont = pygame.font.Font('freesansbold.ttf', 24)
Lfont = pygame.font.Font('freesansbold.ttf', 18)
tb = pygame.image.load("textbox.png")
tbRect = tb.get_rect()
tbRect = tbRect.move(20, 550)
screen.blit(tb, tbRect)
screen.blit(Sfont.render(speaker, True, gray), [tbRect.x + 10, tbRect.y + 10])
screen.blit(Lfont.render(text, True, white), [tbRect.x + 10, tbRect.y + 35])
This is the error I get when I run the main file:
Traceback (most recent call last):
File "C:\Users\Denis\Desktop\Stuff\Misc Misc\Python\Scripts\Game\Main\test.py", line 18, in <module>
me.text('test', 'how do code')
File "C:\Users\Denis\Desktop\Stuff\Misc Misc\Python\Scripts\Game\Main\mainengine.py", line 66, in text
screen.blit(tb, tbRect)
NameError: name 'screen' is not defined
Putting this in a function would make the code a lot cleaner, so It'd be nice if there was a way to pull it off, chances are its not exactly what I have here. I'm not exactly the best at software development.
Thanks in advance!
I recommend to add an argument for the target surface to the function:
def text(surf, speaker, text):
# [...]
Furthermore, do not load the image and do not recreate the pygame.font.Font, every time when a text is rendered. Create the objects once at initialization. Loading a pygame.Surface and creating a pygame.font.Font object are very time consuming operations:
import pygame
pygame.init()
Sfont = pygame.font.Font('freesansbold.ttf', 24)
Lfont = pygame.font.Font('freesansbold.ttf', 18)
tb = pygame.image.load("textbox.png")
def text(surf, speaker, text):
white = (255, 255, 255)
gray = (200, 200, 200)
tbRect = tb.get_rect(topleft = (20, 550))
surf.blit(tb, tbRect)
surf.blit(Sfont.render(speaker, True, gray), [tbRect.x + 10, tbRect.y + 10])
surf.blit(Lfont.render(text, True, white), [tbRect.x + 10, tbRect.y + 35])
Pass the target surface to the function. For instance:
me.text(screen, 'test', 'how do code')
def moveR(amount):
global x
global y
x = x+amount
can.itemconfig(player, image = playerImageL)
can.move("player", x, y)
root.update()
##SETTING##
can = Canvas(width = 850, height = 550, bg = "black")
can.pack(expand = YES, fill = BOTH)
player = can.create_image(x, y, image = playerImageL, anchor = NW)
root.update()
Hey, i am trying to create a mini game using tkinter and canvas however the move command doesn't work. As you see the SETTING is the setup later in the code i am calling
moveR(100)
however it doesn't work and seems to completly destroy my sprite/image.
Text "player" and variable player are two different things.
Use variable player in move()
can.move(player, x, y)
BTW: you don't need itemconfig()
Please read https://stackoverflow.com/help/mcve. To make the code easily verifiable, use something like a rectangle instead of an image. (Your code fails the same for any item.)
Canvas.move(item, delta_x, delta_y) moves the item a given x and y amount. It does the x + dy calculation itself, so you should not. If you want to move to a given position, use Canvas.coords(item, x0, y0, x1, y1). If the size of the new bounding box is different from what it was, it will also change the shape. The following example uses both methods. As a bonus, it also shows how to use root.after to make repeated changes.
import tkinter as tk
root = tk.Tk()
can = tk.Canvas(root, width=800, height=500)
can.pack()
rec = can.create_rectangle(0, 0, 100, 100, fill='red')
def rmove():
box = can.bbox(rec)
if box[0] < 700:
can.move(rec, 100, 30)
root.after(1000, rmove)
else:
can.coords(rec, 0, 400, 50, 500) # position with new shape
root.after(1000, rmove)
root.mainloop()