I am setting up animations to call another function on_complete and then repeat itself with redefined variables. Except that my variable doesn't change so it applies the repeated animations on the 1st widget spawned. Here is an example:
from kivy.config import Config
Config.set('input', 'mouse', 'mouse,multitouch_on_demand')
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.core.window import Window
from kivy.uix.image import Image
from kivy.animation import Animation
from kivy.graphics import Color, Rectangle
from kivy.uix.button import Button
global Draw_Card
Draw_Card=0
global SpadeCount
SpadeCount=0
##Window.fullscreen = True
##Window.size = (1920, 1080)
Player1Deck=['Spade', 'Spade', 'Spade']
class Spade1(Widget):
def __init__(self, **kwargs):
super(Spade1, self).__init__(**kwargs)
global CardSetup
CardSetup=True
self.b=Button(text='Hello World!')
self.add_widget(self.b)
self.bind(pos=self.redraw, size=self.redraw)
self.size=(186.5 / 2, 259.25 / 2)
self.pos=(200, 50)
def Set_Originals(self, x):
self.Original_pos=x.pos
self.Original_size=x.size
def redraw(self, *args):
self.b.pos=self.pos
self.b.size=self.size
class Spade2(Widget):
def __init__(self, **kwargs):
super(Spade2, self).__init__(**kwargs)
global CardSetup
CardSetup=True
self.b=Button(text='Hello World!')
self.add_widget(self.b)
self.bind(pos=self.redraw, size=self.redraw)
self.size=(186.5 / 2, 259.25 / 2)
self.pos=(200, 50)
def Set_Originals(self, x):
self.Original_pos=x.pos
self.Original_size=x.size
def redraw(self, *args):
self.b.pos=self.pos
self.b.size=self.size
class Spade3(Widget):
def __init__(self, **kwargs):
super(Spade3, self).__init__(**kwargs)
global CardSetup
CardSetup=True
self.b=Button(text='Hello World!')
self.add_widget(self.b)
self.bind(pos=self.redraw, size=self.redraw)
self.size=(186.5 / 2, 259.25 / 2)
self.pos=(200, 50)
def Set_Originals(self, x):
self.Original_pos=x.pos
self.Original_size=x.size
def redraw(self, *args):
self.b.pos=self.pos
self.b.size=self.size
class DrawCard(Widget):
def __init__(self, **kwargs):
super(DrawCard, self).__init__(**kwargs)
self.anim_queue = []
self.Draw_Card()
def Draw_Card(self):
global Draw_Card
global CardSetup
global SpadeCount
if Draw_Card > 0:
NextCard=Player1Deck.pop(0)
if NextCard == 'Spade':
if SpadeCount==0:
self.add_widget(Spade1())
elif SpadeCount==1:
self.add_widget(Spade2())
elif SpadeCount==2:
self.add_widget(Spade3())
SpadeCount += 1
PlayerHandCardsList=self.children[:]
print(PlayerHandCardsList)
x=len(PlayerHandCardsList)-1
print(x)
print(PlayerHandCardsList[x])
anim = Animation(pos=((493.75) + ((x * 25)), 0),
t='in_out_quad', duration=1)
anim &= Animation(size=(186.5, 259.25),
t='in_out_quad', duration=1)
self.anim_queue.append((PlayerHandCardsList[x], anim))
w, a = self.anim_queue.pop(0)
a.bind(on_complete=lambda pos, size: self.WrapUpAnimation(PlayerHandCardsList, x))
a.start(w)
else:
CardSetup=False
def WrapUpAnimation(self, instance, x):
global Draw_Card
instance[x].Set_Originals(instance[x])
Draw_Card += -1
self.Draw_Card()
class Game(Widget):
def __init__(self):
super(Game, self).__init__()
global Draw_Card
Draw_Card += 3
self.add_widget(DrawCard())
class GameApp(App):
def build(self):
top = Widget()
top.add_widget(Game())
return top
if __name__ == '__main__':
GameApp().run()
When the console prints PlayerHandCardsList it correctly shows all widgets added so far. When it prints x, this also is correct. However when PlayerHandCardsList[x] prints, it displays the very first value it was assigned and does not update when the Draw_Card function is repeated. It does however correctly redefine the position x * 25. Why is it doing this!? Please help
Ok, so I figured it out! Because I was redefining the self.children list every time instead of .appending, the newest item was always at index 0! So for this, all you have to do is reference index 0 of the list for each run through!
Related
I am currently working on creating a paint app and I am using a Widget canvas for that purpose.
Basically I have defined methods to create straight lines and curve_line. But the problem is I can't switch between these two methods. meaning I have two buttons to trigger these methods and each method bind the widget with on_touch_down and on_touch_move methods. My problem is first time as soon as the app starts running and for example I clicked button with name f hand it works fine and after that once i clicked the next button the widget bind itself with both methods creating a mess.... now i want that the widget should bind itself with only one method at a time .. how can i achieve this..
my python code is here
import kivy
from kivy.uix.widget import Widget
from kivy.uix.widget import Canvas
from kivy.graphics import Color
from kivy.graphics import Line
from kivy.uix.floatlayout import FloatLayout
from kivy.app import App
from kivy.uix.button import Button
class Main(FloatLayout):
def __init__(self,**kwargs):
super(Main, self).__init__(**kwargs)
self.my_widget = Widget(size_hint= (0.6,0.6),pos_hint = {'x':0.5,'top' : 0.8})
self.add_widget(self.my_widget)
self.free_hand = Button(text='f_hand',pos_hint = {'x':0.04,'top':0.2},size_hint = (0.12,0.12))
self.add_widget(self.free_hand)
self.free_hand.bind(on_press = self._free_hand)
self._hand = Button(text='s_hand', pos_hint={'x': 0.04, 'top': 0.6}, size_hint=(0.12, 0.12))
self.add_widget(self._hand)
self._hand.bind(on_press=self._straight_lines)
def _free_hand(self,instance):
def on_touch_downah(self,touch):
self.line = Line(points = (touch.x,touch.y),width = 5)
self.canvas.add(self.line)
'''self.line_list.append(self.line)
print(self.line_list)'''
def on_touch_moveah(self,touch):
self.line.points += touch.x,touch.y
self.my_widget.bind(on_touch_down=on_touch_downah)
self.my_widget.bind(on_touch_move=on_touch_moveah)
def _straight_lines(self,instance):
def on_touch_downeh(self, touch):
self.x_ = touch.x
self.y_ = touch.y
self.lines = Line(points=(touch.x,touch.y),width = 5)
self.canvas.add(self.lines)
def on_touch_moveeh(self, touch):
self.x2 = self.x_ + 0.1
self.y2 = self.y_ + 0.1
self.lines.points = (self.x_,self.y_,self.x2,self.y2,touch.x,touch.y)
self.my_widget.bind(on_touch_down=on_touch_downeh)
self.my_widget.bind(on_touch_move=on_touch_moveeh)
'''
def undo_1(self):
self.i -= 1
k = self.line_list[self.i]
self.canvas.remove(k)
'''
class Myapp(App):
def build(self):
return Main()
Myapp().run()
Any help is greatly appreciated .
Expanding on the suggestion from #inclement, here is a modified version of your code where just two methods are bound to on_touch_down and on_touch_move. The methods triggered by the Buttons just set the mode for the drawing, and the bound methods choose what to do based on the current mode:
from kivy.uix.widget import Widget
from kivy.graphics import Line
from kivy.uix.floatlayout import FloatLayout
from kivy.app import App
from kivy.uix.button import Button
class Main(FloatLayout):
def __init__(self,**kwargs):
super(Main, self).__init__(**kwargs)
self.my_widget = Widget(size_hint= (0.6,0.6),pos_hint = {'x':0.5,'top' : 0.8})
self.add_widget(self.my_widget)
self.free_hand = Button(text='f_hand',pos_hint = {'x':0.04,'top':0.2},size_hint = (0.12,0.12))
self.add_widget(self.free_hand)
self.free_hand.bind(on_press = self._free_hand)
self._hand = Button(text='s_hand', pos_hint={'x': 0.04, 'top': 0.6}, size_hint=(0.12, 0.12))
self.add_widget(self._hand)
self._hand.bind(on_press=self._straight_lines)
self.mode = None # could be "free_hand" or "straight_lines"
self.my_widget.bind(on_touch_down=self.on_touch_downh)
self.my_widget.bind(on_touch_move=self.on_touch_moveh)
def _free_hand(self,instance):
self.mode = "free_hand"
def _straight_lines(self,instance):
self.mode = "straight_lines"
def on_touch_downh(self, widget, touch):
if self.mode == "free_hand":
self.free_hand_touch(touch)
elif self.mode == "straight_lines":
self.straight_lines_touch(touch)
def on_touch_moveh(self, widget, touch):
if self.mode == "free_hand":
self.free_hand_move(touch)
elif self.mode == "straight_lines":
self.straight_lines_move(touch)
def free_hand_touch(self,touch):
self.line = Line(points = (touch.x,touch.y),width = 5)
self.canvas.add(self.line)
'''self.line_list.append(self.line)
print(self.line_list)'''
def free_hand_move(self,touch):
self.line.points += touch.x,touch.y
def straight_lines_touch(self, touch):
self.x_ = touch.x
self.y_ = touch.y
self.lines = Line(points=(touch.x,touch.y),width = 5)
self.canvas.add(self.lines)
def straight_lines_move(self, touch):
self.x2 = self.x_ + 0.1
self.y2 = self.y_ + 0.1
self.lines.points = (self.x_,self.y_,self.x2,self.y2,touch.x,touch.y)
'''
def undo_1(self):
self.i -= 1
k = self.line_list[self.i]
self.canvas.remove(k)
'''
class Myapp(App):
def build(self):
return Main()
Myapp().run()
Note that in your original code, you were binding more methods every time one of the Buttons was pressed.
Okay, so I've got a program that, under certain conditions, needs to remove some options and present some other ones. I originally designed it so that certain buttons would occupy the same space. However, when the on_touch_down() is triggered, the intended switch I've coded doesn't swap layouts.
from kivy.uix.widget import Widget
from kivy.config import Config
from kivy import graphics
from kivy.uix.button import Button
from SpotChess import SCBoardWidget
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.gridlayout import GridLayout
from kivy.uix.relativelayout import RelativeLayout
promote_now = False
class Chessboard(Widget):
def __init__(self, **kwargs):
super(Chessboard, self).__init__(**kwargs)
self.bind(pos=self.drawBoardandPieces)
self.bind(size=self.drawBoardandPieces)
self.drawBoardandPieces()
def drawBoardandPieces(self, *args):
with self.canvas:
# Reset the canvas in case of redraws.
self.canvas.clear()
# Define the lengths of the edges of the squares.
edge_len = min(self.height, self.width) // 8
Config.set("graphics", "resizable", True)
for column in range(0, 8):
for row in range(0, 8):
if ((row + column) % 2) == 0:
graphics.Color(0, 0, 1)
self.dark_rect = graphics.Rectangle(pos=(column*edge_len, row*edge_len),
size=(edge_len, edge_len))
else:
graphics.Color(1, 1, 1)
self.light_rect = graphics.Rectangle(pos=(column*edge_len, row*edge_len),
size=(edge_len, edge_len))
# This provides all of the user's mouse interaction with the board.
def on_touch_down(self, touch):
global promote_now
if promote_now == False:
promote_now = True
Options().swapMenus()
else:
promote_now = False
Options().swapMenus()
game_board = Chessboard()
class PromotionOptions(GridLayout):
def __init__(self, **kwargs):
super(PromotionOptions, self).__init__(**kwargs)
self.cols = 4
self.queen_btn = Button(text="Q/q")
self.rook_btn = Button(text="R/r")
self.bishop_btn = Button(text="B/b")
self.knight_btn = Button(text="N/n")
self.add_widget(self.queen_btn)
self.add_widget(self.rook_btn)
self.add_widget(self.bishop_btn)
self.add_widget(self.knight_btn)
class Options(GridLayout):
def __init__(self, **kwargs):
super(Options, self).__init__(**kwargs)
self.cols = 1
self.swapMenus()
def swapMenus(self):
global promote_now
self.clear_widgets()
if promote_now == False:
# Present the menu layout for normal play.
self.normal_play_menu = GridLayout(cols=3)
# It contains the button to flip the board.
self.flip_board_btn = Button(text="Flip")
self.normal_play_menu.add_widget(self.flip_board_btn)
self.add_widget(self.normal_play_menu)
else:
# Present the menu layout for pawn promotions.
self.promotion_menu = GridLayout(cols=1)
self.promotion_menu.add_widget(widget=PromotionOptions())
self.add_widget(self.promotion_menu)
class SCApp(App):
def build(self):
top_portion = BoxLayout(orientation="vertical")
chessboard_portion = RelativeLayout()
chessboard_portion.add_widget(widget=game_board)
top_portion.add_widget(widget=chessboard_portion)
bottom_portion = BoxLayout(orientation="vertical")
bottom_portion.add_widget(widget=Options())
app_layout = BoxLayout(orientation="vertical")
app_layout.add_widget(top_portion)
app_layout.add_widget(bottom_portion)
return app_layout
SCApp().run()
I'm clearly calling the object to perform the replacement, but it's not completing either the self.clear_widgets() or the self.add_widget(), even though I've called the function. What am I missing?
In your on_touch_down() method the
Options().swapMenus()
lines are creating new instances of the Options class, and calling the swapMenus() method of that new instance. But that new instance of Options is not in your GUI, so it haas no effecet on your App. You need to get a reference to the Options instance that IS in your GUI, and call the swapMenus() method of that instance.
You can do that by saving a reference to the Options class in your App, by replacing this line in your build() method:
bottom_portion.add_widget(widget=Options())
with:
self.options = Options()
bottom_portion.add_widget(widget=self.options)
Then in the on_touch_down() method:
def on_touch_down(self, touch):
global promote_now
# get the instance of Options that is in the App
options = App.get_running_app().options
if promote_now == False:
promote_now = True
options.swapMenus()
else:
promote_now = False
options.swapMenus()
My custom mouse image is not showing up in pyglet. It loads normally(When you create the window outside a class), but when i make the window in a class and try yo add the custom mouse cursor nothing happens
class x:
def __init__(self):
self.game_window = pyglet.window.Window()
self.on_mouse_press = self.game_window.event(self.on_mouse_press)
self.game_cursor = pyglet.image.load('image.png')
self.cursor = pyglet.window.ImageMouseCursor(self.game_cursor, 50, 70)
self.game_window.set_mouse_cursor(self.cursor)
I have tried printing every one of the lines of code (for mouse image loading)
When i print - self.game_cursor = pyglet.image.load('image.png') - This is the result:
ImageData 85x82
When i print - self.cursor = pyglet.window.ImageMouseCursor(self.game_cursor, 50, 70) - This is the result:
pyglet.window.ImageMouseCursor object at 0x7f4dad76b390
When i print - self.game_window.set_mouse_cursor(self.cursor) - This is the result:
None
How do I fix make the mouse show?
I strongly suggest you inherit the window class in to your class instead of keeping it as a internal value if possible. So you can use override hooks to replace the on_ functions. Perhaps this is an outdated approach, but for the time I've learned to work with these things - it's reommended.
class x(pyglet.window.Window):
def __init__(self):
super(x, self).__init__()
self.game_cursor = pyglet.image.load('image.png')
self.cursor = pyglet.window.ImageMouseCursor(self.game_cursor, 50, 70)
self.set_mouse_cursor(self.cursor)
def on_mouse_press():
# Your code handling on_mouse_press
Here's a working example:
from pyglet import *
from pyglet.gl import *
key = pyglet.window.key
class main(pyglet.window.Window):
def __init__ (self, width=800, height=600, fps=False, *args, **kwargs):
super(main, self).__init__(width, height, *args, **kwargs)
self.game_cursor = pyglet.image.load('image.png')
self.cursor = pyglet.window.ImageMouseCursor(self.game_cursor, 50, 70)
self.set_mouse_cursor(self.cursor)
self.x, self.y = 0, 0
self.keys = {}
self.mouse_x = 0
self.mouse_y = 0
self.alive = 1
def on_draw(self):
self.render()
def on_close(self):
self.alive = 0
def on_mouse_motion(self, x, y, dx, dy):
self.mouse_x = x
def on_key_release(self, symbol, modifiers):
try:
del self.keys[symbol]
except:
pass
def on_key_press(self, symbol, modifiers):
if symbol == key.ESCAPE: # [ESC]
self.alive = 0
self.keys[symbol] = True
def render(self):
self.clear()
## Add stuff you want to render here.
## Preferably in the form of a batch.
self.flip()
def run(self):
while self.alive == 1:
self.render()
# -----------> This is key <----------
# This is what replaces pyglet.app.run()
# but is required for the GUI to not freeze
#
event = self.dispatch_events()
if __name__ == '__main__':
x = main()
x.run()
According to pyglet documentation for ImageMouseCursor class link method draw(x, y) is abstract. So I've tried sub-classing ImageMouseCursor and implementing draw method like this:
import pyglet
pyglet.resource.path = ['resources']
pyglet.resource.reindex()
# subclass definition
class GameMouse(pyglet.window.ImageMouseCursor):
# class initialization
def __init__(self):
self.game_cursor = pyglet.resource.image('game_cursor.png')
super().__init__(self.game_cursor, 0, 34)
# method override
def draw(self, x, y):
self.game_cursor.blit(x, y)
However, this will not work if gl blending is not enabled. gl blending is needed to display alpha channels. I've managed to enable it by sub-classing Window class, and using glEnable / glBlendFunc functions. This is the part of the code that does as described:
# subclass definition:
class GameApp(pyglet.window.Window):
# class initialization
def __init__(self):
super(GameApp, self).__init__()
pyglet.gl.glEnable(pyglet.gl.GL_BLEND)
pyglet.gl.glBlendFunc(pyglet.gl.GL_SRC_ALPHA,
pyglet.gl.GL_ONE_MINUS_SRC_ALPHA)
Hope this helps
I'm trying to make a text adventure with tkinter and I'm slowly getting something together. I'm trying to display commands as they come from room to room but even though the buttons appear, nothing happens when I press them.
game.py
#!/usr/bin/python
# -*- coding: utf-8 -*-
import world
from player import Player
from ui import *
def main():
gui = Window(root())
while True:
gui.mainloop()
else:
pass
if __name__ == '__main__':
main()
ui.py
#!/usr/bin/python
# -*- coding: utf-8 -*-
import tkinter as tk
from tkinter import ttk
import world, tiles, action
from player import Player
class Window(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self, master=master)
self.master = master
self.player = Player()
self.init_ui()
def init_ui(self):
self.master.title("****")
self.tabs = Tabs(self.master)
world.load_tiles()
self.world = world.tile_exists(self.player.location_x, self.player.location_y)
self.update_main()
def update_main(self):
self.world.scene_init()
self.world.modify_player(self.player)
self.tabs.update_tab(self.world, self.player)
def _label(self, master, text, side=None, anchor=None):
new_label = tk.Label(master, text=text)
new_label.pack(side=side, anchor=anchor)
def _button(self, master, text, command, side=None, anchor=None):
new_button = tk.Button(master, text=text, command=command)
new_button.pack(side=side, anchor=anchor)
class Tabs(Window):
def __init__(self, master):
self.master = master
self.nb = ttk.Notebook(self.master)
nb_1 = ttk.Frame(self.nb)
self.frame_1 = tk.Frame(nb_1, bg='red', bd=2, relief=tk.SUNKEN, padx=5, pady=5)
self.frame_1.pack(expand=1, fill='both', side=tk.LEFT)
self.nb.add(nb_1, text='Game')
self.nb.pack(expand=1, fill='both', side=tk.LEFT)
def update_tab(self, world, player):
avaliable_actions = world.avaliable_actions()
self._label(self.frame_1, world.display_text(), side=tk.LEFT, anchor=tk.N)
for action in avaliable_actions:
self._button(self.frame_1, text=action, command=player.do_action(action, **action.kwargs), side=tk.BOTTOM, anchor=tk.E)
def root():
root = tk.Tk()
root.geometry("600x350+200+200")
return root
world.py
#!/usr/bin/python
# -*- coding: utf-8 -*-
_world = {}
def tile_exists(x, y):
"""Returns the tile at the given coordinates or None if there is no tile.
:param x: the x-coordinate in the worldspace
:param y: the y-coordinate in the worldspace
:return: the tile at the given coordinates or None if there is no tile
"""
return _world.get((x, y))
def load_tiles():
with open('scenes.txt', 'r') as f:
rows = f.readlines()
x_max = len(rows[0].split('\t'))
for y in range(len(rows)):
cols = rows[y].split('\t')
for x in range(x_max):
tile_name = cols[x].replace('\n', '')
_world[(x, y)] = None if tile_name == '' else getattr(__import__('tiles'),
tile_name)(x, y)
return _world
tiles.py
#!/usr/bin/python
# -*- coding: utf-8 -*-
import world, action
from player import Player
class MapTile():
def __init__(self, x, y):
self.x = x
self.y = y
def display_text(self):
pass
# raise NotImplementedError()
def modify_player(self, the_player):
raise NotImplementedError()
def adjacent_moves(self):
moves = []
if world.tile_exists(self.x + 1, self.y):
moves.append(action.MoveEast())
if world.tile_exists(self.x - 1, self.y):
moves.append(action.MoveWest())
if world.tile_exists(self.x, self.y - 1):
moves.append(action.MoveNorth())
if world.tile_exists(self.x, self.y + 1):
moves.append(action.MoveSouth())
return moves
def avaliable_actions(self):
'''Returns all of the default avaliable_actions in a room'''
moves = self.adjacent_moves()
# moves.append(action.ViewInventory())
return moves
class Scene_1(MapTile):
def scene_init(self):
self.location = 'Scene_1'
self.long_desc = 'Welcome to {}, the shittiest place on earth.'.format(self.location)
self.short_desc = 'Eh, I don\'t care.'
def display_text(self):
return self.long_desc
def modify_player(self, the_player):
self.first = True
return self.display_text()
class Scene_2(MapTile):
def scene_init(self):
self.location = 'Scene_2'
self.long_desc = 'This is {}, but noone gives a damn.'.format(self.location)
self.short_desc = 'Eh, I don\'t care, really.'
def display_text(self):
return self.long_desc
def modify_player(self, the_player):
self.first = True
return self.display_text()
player.py
#!/usr/bin/python
# -*- coding: utf-8 -*-
class Player():
'''Base for player'''
def __init__(self):
self.inventory = []
self.hp = 100
self.location_x, self.location_y = 1, 1
self.victory = False
def is_alive(self):
return self.hp >= 0
def do_action(self, action, **kwargs):
action_method = getattr(self, action.method.__name__)
if action_method:
action_method(**kwargs)
def print_inventory(self):
for item in self.inventory:
print(item, 'n')
def move(self, dx, dy):
self.location_x += dx
self.location_y += dy
def move_north(self):
self.move(dx=0, dy=-1)
def move_south(self):
self.move(dx=0, dy=1)
def move_east(self):
self.move(dx=1, dy=0)
def move_west(self):
self.move(dx=-1, dy=0)
action.py
#!/usr/bin/python
# -*- coding: utf-8 -*-
from player import Player
class Action():
def __init__(self, method, name, **kwargs):
"""Creates a new action
:param method: the function object to execute
:param name: the name of the action
:param ends_turn: True if the player is expected to move after this action else False
:param hotkey: The keyboard key the player should use to initiate this action
"""
self.method = method
self.name = name
self.kwargs = kwargs
def __str__(self):
return "{}".format(self.name)
class MoveNorth(Action):
def __init__(self):
super().__init__(method=Player.move_north, name='north')
class MoveSouth(Action):
def __init__(self):
super().__init__(method=Player.move_south, name='south')
class MoveEast(Action):
def __init__(self):
super().__init__(method=Player.move_east, name='east')
class MoveWest(Action):
def __init__(self):
super().__init__(method=Player.move_west, name='west')
class ViewInventory(Action):
"""Prints the player's inventory"""
def __init__(self):
super().__init__(method=Player.print_inventory, name='View inventory', hotkey='i')
class Attack(Action):
def __init__(self, enemy):
super().__init__(method=Player.attack, name="Attack", hotkey='a', enemy=enemy)
class Flee(Action):
def __init__(self, tile):
super().__init__(method=Player.flee, name="Flee", hotkey='f', tile=tile)
command expect function name without () and arguments.
Mistake:
command=player.do_action(action, **action.kwargs)
This way you assign to command value returned by player.do_action() but this functions returns None
You have to use lambda function
command=lambda:player.do_action(action, **action.kwargs)
but maybe you will need also arguments in lambda because you create this in for loop.
command=lambda act=action, kws=action.kwargs : player.do_action(act, **kws)
[edit]
It seems I solved the problem... In fact, I now do that:
class Gameboard(QGraphicsScene):
def deletePawn(self, num):
pawnToDelete = self.pawns.pop(num)
pawnToDelete.delete()
class Pawn(QGraphicsItem):
def delete(self):
child.prepareGemotryChange()
child.setParent(None)
#idem for each child
self.gameboard.removeItem(self)
self.gameboard = None
[/edit]
What is the good way to implement references in a pyqt QGraphicsScene?
I made a class Gameboard which inherits QGraphicsScene, and this gameboard contains a variable number of pawns (wich inherit QGraphicsPolygonItem)
Pawns can be created or deleted dynamically, creation is ok but deletion sometimes crash...
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from PyQt4 import QtOpenGL
class Gameboard(QGraphicsScene):
def __init__(self):
super(Gameboard, self).__init__()
self.pawns = {}
self.currentPawn = None
def createSquares(self):
#create the polygons of the squares and affect their coordinates x,y
def createPawn(self):
pawn = Pawn(self)
num = len(self.pawns)
self.pawns[num] = pawn
self.addItem(self.pawns[num])
def deletePawn(self, num):
self.currentPawn = None
self.pawns[num].beforeDelete()
self.pawns[num].prepareGeometryChange()
self.removeItem(self.pawns[num])
del self.pawns[num]
def selectPawn(self, pawn):
#a pawn is selected, by click for example
self.currentPawn = pawn
def keyPressEvent(self, event):
ekey = event.key()
if ekey == Qt.Key_Delete and self.currentPawn != None:
num = self.currentPawn.number
self.deletePawn(num)
class Pawn(QGraphicsItem):
def __init__(self, gameboard):
super(Pawn, self).__init__()
self.gameboard = gameboard
self.number = 0
self.pos = (-1,-1)
self.name = ""
def create(self, x, y, num):
#create a QGraphicsPolygonItem, a QGraphixPixmapItem and a QGraphicsTextItem which are child of the QGraphicsItem Pawn, and set position
self.number = num
self.pos = (x,y)
self.gameboard.squares[(x,y)].occupiedBy = self
def move(self, newPos):
self.gameboard.squares[self.pos].occupiedBy = None
self.pos = newPos
self.gameboard.squares[self.pos].occupiedBy = None
def beforeDelete(self):
#a function I add trying to get rid of the crash
self.gameboard = None
self.graphicsPolygon.setParent = None
self.graphicsPix.setParent = None
self.text.setParent = None
def mousePressEvent(self, event):
super(Pawn, self).mousePressEvent(event)
if event.button() == 1:
self.gameboard.currentPawn = self
event.accept()
class Square(QGraphicsPolygonItem):
def __init__(self, gameboard):
self.coordinates = (x,y)
self.occupiedBy = None
What is the proper way to proceed, should I use deleteLater?
Or maybe something with the weakref lib?
Is it because of the variable gameboard in Pawn?