I am trying to make a button class for my game, using pygame. But in the button class, I cannot call methods that are contained in the class itself.
I am new to classes so I may be missing something important, I'm sorry
I tried to add self to the isHovering() Method but it still doesn't work.
import pygame
class Button():
def __init__(self, pos, value, img, screen):
self.pos = pos
self.value = value
self.img = img
self.screen = screen
### Getters and Setters ###===============
### Get/Set Value : True/False ###
def setValue(self, value):
self.value = value
def getValue(self):
return self.value
### Get/Set Pos ###
def setPos(self, pos):
self.pos = pos
def getPos(self):
return self.pos
### Get/Set Img ###
def setImg(self, img):
self.img = img
def getImg(self):
return self.img
#==========================================
def isHovering(self):
pos = getPos()
imgRect = pygame.rect(pos[0], pos[1], 105, 105)
if imgRect.collidepoint(pygame.mouse.get_pos()):
return True
else:
return False
def update(self, screen):
if isHovering():
image = pygame.transform.scale(self.img(95, 95))
else:
image = pygame.transform.scale(self.img(105, 105))
screen.blit(image, self.pos)
I thought that when update(screen) was called in my main loop, that it would call isHovering(), and return a True or False, but instead I get this error:
NameError: name 'isHovering' is not defined
In def update(self, screen),
The if statement should be if self.isHovering().
If not, the interpreter will look for a isHovering function somewhere in the current module, like if you had defined it outside your class.
Using the self. prefix will indicate that you are indeed trying to call a method of the instance as JacoblRR already pointed out in a comment.
Related
Hey my task is to create a parentclass Shape and pass the functions on to the different shapes. Since a Circlearea calculation takes different parameters I am trying to overwrite the function. It throws following warning: "Signature of method 'Circel.get_area()' does not match Signature in the base method in Shape". Is that possible at all? How would I have to do it? Should it work anyway later on?
class Shape(ABC):
#abstractmethod
def get_area(self, x_l, y_l):
pass
def move(self, newx, newy):
pass
class Circle(Shape):
def __init__(self, rad, x_pos, y_pos):
self.rad = rad
self.pos = (x_pos, y_pos)
self.area = self.get_area(self, self.rad)
def get_area(self, rad):
return(self.rad*self.rad*2*m.pi)
It is apparently impossible to pass attributes of an object to its own methods:
def drawBox(color):
print("A new box of color ", color)
return
class Box:
def __init__(self, color):
self.defaultColor = color
self.color = color
def update(self, color = self.defaultColor):
self.color = color
drawBox(color)
This does not work:
Traceback (most recent call last):
File "<string>", line 5, in <module>
File "<string>", line 9, in Box
NameError: name 'self' is not defined
I found a way to bypass this issue like this:
def drawBox(color):
print("A new box of color ", color)
return
class Box:
def __init__(self, color):
self.defaultColor = color
self.color = color
def update(self, color = None):
if color == None:
self.color = self.defaultColor
else:
self.color = color
drawBox(color)
Is there a better (more elegant?) way to do this?
The reason you can't use self.color as a default parameter value is that the default is evaluated at the time the method is defined (not at the time that it's called), and at the time the method is defined, there is no self object yet.
Assuming that a valid color is always a truthy value, I would write this as:
class Box:
def __init__(self, color):
self.default_color = self.color = color
def draw(self):
print(f"A new box of color {self.color}")
def update(self, color=None):
self.color = color or self.default_color
self.draw()
I have created a RelativeLayout subclass that positions its children in a grid by supplying them positions in the code (Python, not kv file). It works, but items are placed some 25 pixels to the upper-right from the position of layout itself, as shown by the canvas block. Python code for Layout subclass:
class RLMapWidget(RelativeLayout):
def __init__(self, map=None, **kwargs):
super(FloatLayout, self).__init__(**kwargs)
# Connecting to map, factories and other objects this class should know about
self.tile_factory = TileWidgetFactory()
self.map = map
# Initializing tile widgets for BG layer and adding them as children
for x in range(self.map.size[0]):
for y in range(self.map.size[1]):
tile_widget = self.tile_factory.create_tile_widget(self.map.get_item(layer='bg',
location=(x, y)))
# tile_widget.pos = (50*x, 50*y)
tile_widget.pos = self._get_screen_pos((x, y))
self.add_widget(tile_widget)
# Initializing widgets for actor layers
for x in range(self.map.size[0]):
for y in range(self.map.size[1]):
if self.map.has_item(layer='actors', location=(x, y)):
actor_widget = self.tile_factory.create_actor_widget(self.map.get_item(layer='actors',
displayed location=(x, y)))
actor_widget.pos=(50*x, 50*y)
self.add_widget(actor_widget)
# Map background canvas. Used solely to test positioning
with self.canvas.before:
Color(0, 0, 1, 1)
self.rect = Rectangle(size = self.size, pos=self.pos)
self.bind(pos=self.update_rect, size=self.update_rect)
# Initializing keyboard bindings and key lists
self._keyboard = Window.request_keyboard(self._keyboard_closed, self)
self._keyboard.bind(on_key_down=self._on_key_down)
# The list of keys that will not be ignored by on_key_down
self.used_keys=['w', 'a', 's', 'd']
def redraw_actors(self):
for actor in self.map.actors:
actor.widget.pos = self._get_screen_pos(actor.location)
def _get_screen_pos(self, location):
"""
Return screen coordinates (in pixels) for a given location
:param location: int tuple
:return: int tuple
"""
return (location[0]*50, location[1]*50)
# Keyboard-related methods
def _on_key_down(self, keyboard, keycode, text, modifiers):
"""
Process keyboard event and make a turn, if necessary
:param keyboard:
:param keycode:
:param text:
:param modifiers:
:return:
"""
if keycode[1] in self.used_keys:
self.map.process_turn(keycode)
self.redraw_actors()
def _keyboard_closed(self):
self._keyboard.unbind(on_key_down=self._on_key_down)
self._keyboard = None
def update_rect(self, pos, size):
self.rect.pos = self.pos
self.rect.size = self.size
class CampApp(App):
def build(self):
root = FloatLayout()
map_factory = MapFactory()
map = map_factory.create_test_map()
map_widget = RLMapWidget(map=map,
size=(map.size[0]*50, map.size[1]*50),
size_hint=(None, None))
root.add_widget(map_widget)
return root
if __name__ == '__main__':
CampApp().run()
Factory class that makes tiles:
class TileWidgetFactory(object):
def __init__(self):
pass
def create_tile_widget(self, tile):
tile.widget = Image(source=tile.image_source,
size_hint=(None, None))
return tile.widget
def create_actor_widget(self, actor):
actor.widget = Image(source='Tmp_frame_black.png',
size_hint=(None, None))
return actor.widget
Okay, solved it myself. It turns out that if I supply the size to child widgets in factory, they are positioned properly. Although it solves the problem I have, I'd still be grateful if someone can explain where this quirk does come from.
I am trying to draw my own graphic within a kivy 'canvas'. For now I have a red or green rectangle which changes colour once per second, but I want to add a changing text label.
After a little searching it appears that there isn't a "Text" Instruction which can be added to the canvas. I have found a few references to using a Label() widget as well as the canvas Instructions, but this does not seem ideal, and also I can't seem to get it to render more than once.
Here's my object as it stands at the moment:
class HVObject(BoxLayout):
def __init__(self, **kwargs):
BoxLayout.__init__(self, **kwargs)
self.colour = 1
self.label = Label()
self.render()
self.add_widget(self.label)
self.bind(size=self._update_rect, pos=self._update_rect)
Clock.schedule_interval(self.callevery, 1)
def render(self):
self.canvas.clear()
self.rect = Rectangle(size=self.size, pos=self.pos)
self.canvas.add(Color(1-self.colour, self.colour, 0, 1))
self.canvas.add(self.rect)
self.label.text = "COL %d" % self.colour
self.canvas.ask_update()
def callevery(self, x):
self.colour = 1-self.colour
self.render()
def _update_rect(self, instance, value):
self.rect.pos = instance.pos
self.rect.size = instance.size
self.label.pos = instance.pos
Is there an easy way to achieve the effect I need?
Thank you
Answering my own question:
After a little look around the [kivy] garden, I found Tickline (and Tick). and the use of CoreLabel() and Rectangle(texture=...)
Here's my updated render() method which adds the text object I need.
def render(self):
self.canvas.clear()
self.canvas.add(Color(1-self.colour, self.colour, 0, 1))
self.rect = Rectangle(size=self.size, pos=self.pos)
self.canvas.add(self.rect)
label = CoreLabel(text="COL %d" % self.colour, font_size=20)
label.refresh()
text = label.texture
self.canvas.add(Color(self.colour, 1-self.colour,0, 1))
pos = list(self.pos[i] + (self.size[i] - text.size[i]) / 2 for i in range(2))
self.canvas.add(Rectangle(size=text.size, pos=pos, texture=text))
self.canvas.ask_update()
Which works for me, albeit a little clunky!
I'm currrently learning pyglet, and there is a teeny problem. I called a superclass of the init of pyglet.sprite.Sprite in my player class, but when I try to change the self.image from the update function (or any function that is not the init), it doesn't animate anymore, in fact, it just displays the first element in the array conatining the images for my animation. Thanks. Here is are my codes:
import pyglet
from pyglet.window import key
import physicalobject, resources
class Player(physicalobject.PhysicalObject): #PhysicalObject is just another pyglet.sprite.Sprite superclass
def __init__(self, *args, **kwargs):
super(Player,self).__init__(img=resources.right_anim, *args, **kwargs)
# some codes here....
def update(self, dt):
super(Player,self).update(dt)
#... codes
if self.look_right:
self.image = resources.right_anim
elif self.look_left:
self.image = resources.left_anim