Impossible to update UI because can not delete widgets - pyqt4

I am creating an application with a dynamic user interface meaning that widgets should appear and vanish depending on data in a database. The code is listed below.
The data part is going alright, the update of UI not.
I create dynamically a widget within a QTabWidget. This widget (being a class) contains 3 widget: 2 listWidgets and a pushbutton. Deleting or calling the destroy-method, with or without the widget.update or widget.repaint is not successfull.
What am I doing wrong ?
class ImEntityWidget(object):
def __init__(self, widgetParent, entityclass, x, y, width, height):
self.widgetParent = widgetParent
self.entityclass = entityclass
self.x = x
self.y = y
self.height = height
self.width = width
self.focusItem = session.query(Entity).filter(
Entity.parent_vpu_id == VpuFocus.Id).filter(
Entity.Class == self.entityclass).first()
self.listWidgetHigher = QListWidget(self.widgetParent)
self.listWidgetHigher.setGeometry(QtCore.QRect(
self.x, self.y, self.width, 25))
font = QtGui.QFont()
font.setPointSize(8)
self.listWidgetHigher.setFont(font)
self.listWidgetHigher.setObjectName("listWidgetHigher" +
str(self.entityclass.name))
# Entity in focus pushbutton widget
self.listWidget_pb = QtGui.QPushButton(self.widgetParent)
self.listWidget_pb.setGeometry(QtCore.QRect(
self.x, self.y + 25, self.width, 25))
font = QtGui.QFont()
font.setPointSize(7)
font.setWeight(75)
font.setBold(True)
self.listWidget_pb.setFont(font)
self.listWidget_pb.setObjectName("pushButton" + str(self.entityclass.name))
# Lower recursion entity widget
self.listWidget = QListWidget(self.widgetParent)
self.listWidget.setGeometry(QtCore.QRect(
self.x, self.y + 50, self.width, self.height - 50))
font = QtGui.QFont()
font.setPointSize(8)
self.listWidget.setFont(font)
self.listWidget.setObjectName("listWidget" + str(self.entityclass.name))
# set button context menu policy
self.listWidget_pb.setContextMenuPolicy(Qt.CustomContextMenu)
self.listWidget_pb.customContextMenuRequested.connect(
self.listWidget_pbCtxMenu)
self.listWidget_pb.connect(self.listWidget_pb,
SIGNAL("activated(QModelIndex)"), self.focusHigherRecursion)
self.listWidget.itemDoubleClicked.connect(self.doubleClick)
#Using the widget class
self.entWidget = ImEntityWidget(
self.tabPrimaryProcesses, entityclass,
wX, wY, wWidth, wHeight)
self.entityWidgets.append(self.entWidget)
# Trying to delete the widgets
for self.widget in self.entityWidgets:
#del self.widget.listWidgetHigher
#del self.widget.listWidget_pb
#del self.widget.listWidget
#self.tabPrimaryProcesses.update()
self.widget.listWidgetHigher.destroy(destroyWindow=True)
self.widget.listWidget_pb.destroy(destroyWindow=True)
self.widget.listWidget.destroy(destroyWindow=True)

Jeff is Right, to delete a Widget you should do try:
self.widget.listWidgetHigher.setParent(None)
self.widget.listWidget_pb.setParent(None)
self.widget.listWidget.setParent(None)

Related

Figure Canvas widget not showing up

I'm trying to embed a matplotlib animation into my PyQt5-GUI. Here's my class of the animation:
class AnimationCanvas(FigureCanvas):
def __init__(self, parent=None, width=5, height=4, dpi=100):
self.fig = Figure(figsize=(width, height), dpi=dpi)
self.fig.set_facecolor('#1a1a1a')
self.ax = self.fig.add_subplot(111,polar=True)
self.ax.set_facecolor('#333333')
self.ax.set_ylim(0,1)
self.ax.tick_params(axis='x', labelsize=8,colors='#ffffff')
self.ax.set_yticks([]) # Shows no r values
self.ax.set_xticks(np.pi/180. * np.linspace(0, 360, 18, endpoint=False)) # A line every 20 degrees
self.ax.grid(color='#595959', linestyle='-.', linewidth=0.7)
self.ax.set_title("Position of the speakers", pad = 8,
fontdict= dict(
color = '#ffffff',
fontsize = 15,
fontweight = 0.9))
self.line, = self.ax.plot([], [], marker = 'X', color= '#ffffe6',markersize=15)
super(AnimationCanvas, self).__init__(self.fig)
When I use the following code in a MainWindow class (that handles the animation) to set it as a central widget it shows, no problem there. But I want to embed it into my existing GUI so that it's not the central widget. When I try to give it an absolute position by using setGeometry() or move(), it just doesn't show up anymore. I've also tried using QVBoxLayout and adding self.canvas to the layout, that works too, but I don't know how to stylize it properly so that it doesn't look like a huge mess with those unwanted whitespaces.
class MainWindow(QtWidgets.QMainWindow):
def __init__(self,xdata, *args, **kwargs,):
super(MainWindow, self).__init__(*args, **kwargs)
widget = QWidget()
vbox = QVBoxLayout(widget)
self.canvas = AnimationCanvas(self, width=5, height=4, dpi=100)
self.setCentralWidget(self.canvas)
vbox.addWidget(self.canvas)
self.angles = xdata*(3.14/180)
self.ydata = np.ones(len(self.angles))*0.96
self.index = 0
self.line = self.canvas.line
self.plot_button = QtWidgets.QPushButton(self.canvas)
self.plot_button.setGeometry(QtCore.QRect(5, 5, 50, 25))
self.plot_button.setText("plot")
self.plot_button.setObjectName("plot")
self.plot_button.clicked.connect(self.plot_animation)
vbox.addWidget(self.plot_button)
self.setCentralWidget(widget)
self.setGeometry(300, 300, 800, 600)
Questions:
Why doesn't setGeometry() work in this instance and how to fix it.
If I have to use QVBoxLayout, what's the best way to achieve a nice look? addSpacing() and addStretch() everywhere seems like a bit of an overkill to me.
Thanks in advance!
If you want to place your AnimationCanvas directly on the central widget then call setParent in the constructor.
class AnimationCanvas(FigureCanvas):
def __init__(self, parent=None, width=5, height=4, dpi=100):
...
super(AnimationCanvas, self).__init__(self.fig)
self.setParent(parent)
And construct the AnimationCanvas with parent widget, the central widget of the QMainWindow. The call to setGeometry on the QPushButton will now work, it will be placed on the AnimationCanvas at position (5, 5) with the size (50, 25).
class MainWindow(QtWidgets.QMainWindow):
def __init__(self,xdata=None, *args, **kwargs,):
super(MainWindow, self).__init__(*args, **kwargs)
widget = QWidget()
self.setCentralWidget(widget)
self.canvas = AnimationCanvas(parent=widget, width=5, height=4, dpi=100)
self.plot_button = QtWidgets.QPushButton(self.canvas)
self.plot_button.setGeometry(QtCore.QRect(5, 5, 50, 25))
self.setGeometry(300, 300, 800, 600)
If you use a layout then setGeometry will usually be ignored because the layout manages the position and size of all its widgets. However, you could add only the AnimationCanvas to the layout and not the QPushButton, allowing it to stay on the AnimationCanvas with the exact geometry you specify.
class MainWindow(QtWidgets.QMainWindow):
def __init__(self,xdata=None, *args, **kwargs,):
super(MainWindow, self).__init__(*args, **kwargs)
widget = QWidget()
self.setCentralWidget(widget)
vbox = QVBoxLayout(widget)
self.canvas = AnimationCanvas(width=5, height=4, dpi=100)
vbox.addWidget(self.canvas)
self.plot_button = QtWidgets.QPushButton(self.canvas)
self.plot_button.setGeometry(QtCore.QRect(5, 5, 50, 25))
self.setGeometry(300, 300, 800, 600)

Why is pyglet.app.run() dispatching on_resize() repeatedly leading to max recursion depth error?

I am trying to make a simple pyglet app (with Python 3.7) where I can draw a grid, some tiles, color them etc. While also being able to pan, zoom around, so I followed this question :
How to pan and zoom properly in 2D?
However the on_resize() event of the MyWindow subclass
seems to be called repeatedly after run() is called, even if I don't touch the window, and the script crashes with a max recursion depth, except when I comment out its self.width = width and self.height = height lines.
But then resizing the window squishes the objects I'm drawing...
I would like resizing to just show more of the map, keeping the aspect ratio and size of drawn objects.
I have added some print() calls to check what happens, but I'm truly at a loss with what's happening here.
import pyglet
from pyglet.gl import *
from algorithm.priorityqueue import PriorityQueue
from algorithm.squaregrid import heuristic, SquareGrid
ZOOM_IN_FACTOR = 1.2
ZOOM_OUT_FACTOR = 1/ZOOM_IN_FACTOR
class Triangle:
def __init__(self, color):
self.color = color
self.vertices = pyglet.graphics.vertex_list(3, ('v3f', [0,0,0, 1000,0,0, 1000,1000,0]),
('c4B', self.color*3))
def draw(self, x, y, z):
glTranslatef(x, y, z)
self.vertices.draw(GL_TRIANGLES)
glTranslatef(-x, -y, -z)
class Tile:
def __init__(self, size, color):
self.size = size
self.color = color
self.vertices = pyglet.graphics.vertex_list(
4, ('v3f', [0,0,0, 0+self.size,0,0,
0+self.size,0+self.size,0, 0,0+self.size,0]),
('c4B', self.color*4))
def draw(self, x, y, z):
glTranslatef(x, y, z)
self.vertices.draw(GL_QUADS)
glTranslatef(-x, -y, -z)
class Line:
def __init__(self, start, end, color, stroke):
self.color = color
self.stroke = stroke
self.sx, self.sy = start
self.ex, self.ey = end
self.vertices = pyglet.graphics.vertex_list(
2, ('v3f', [self.sx,self.sy,0, self.ex,self.ey,0]),
('c4B', self.color*2))
def draw(self):
glLineWidth(self.stroke)
self.vertices.draw(GL_LINES)
class MyWindow(pyglet.window.Window):
def __init__(self, width, height, *args, **kwargs):
conf = Config(sample_buffers=1,
samples=4,
depth_size=16,
double_buffer=True)
super().__init__(width, height, config=conf, *args, **kwargs)
#self.set_minimum_size(960, 540)
#glClearColor(0.8, 0.8, 0.8, 1.0)
#glOrtho(0, self.width, 0, self.height, -10, 10) # setup orthogonal projection
self.left = 0
self.right = width
self.bottom = 0
self.top = height
self.zoom_level = 1
self.zoomed_width = width
self.zoomed_height = height
print("init finished")
def init_gl(self, width, height):
print("init_gl started")
# Clear color
glClearColor(255/255, 255/255, 255/255, 255/255)
# Antialiasing
glEnable(GL_LINE_SMOOTH)
glEnable(GL_POLYGON_SMOOTH)
glHint(GL_LINE_SMOOTH_HINT, GL_NICEST)
# Alpha Blending
glEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
# Viewport
glViewport(0, 0, width, height)
def drawstuff(self):
print("drawstuff called")
self.triangle = Triangle((255, 0, 0, 255))
self.square = Tile(10, (255,0,255,255))
self.gridlines = []
for j in range(0,25):
self.gridlines.append(Line((0, 40*j),(1000, 40*j),(50,50,50,255), 2))
for i in range(0,25):
self.gridlines.append(Line((40*i, 0),(40*i, 1000),(50,50,50,255), 2))
def on_resize(self, width, height):
print("on_resize called")
# It crashes here!
self.width = width
print(width)
self.height = height
print(height)
self.init_gl(width, height)
def on_mouse_drag(self, x, y, dx, dy, buttons, modifiers):
# Move camera
self.left -= dx*self.zoom_level
self.right -= dx*self.zoom_level
self.bottom -= dy*self.zoom_level
self.top -= dy*self.zoom_level
def on_mouse_scroll(self, x, y, dx, dy):
# Scale factor
f = ZOOM_IN_FACTOR if dy<0 else ZOOM_OUT_FACTOR if dy>0 else 1
# If in proper range
if .2 < self.zoom_level*f < 5:
self.zoom_level *= f
print(self.width, self.height)
mouse_x = x/self.width
mouse_y = y/self.height
mouse_x_in_world = self.left + mouse_x * self.zoomed_width
mouse_y_in_world = self.bottom + mouse_y * self.zoomed_height
self.zoomed_width *= f
self.zoomed_height *= f
self.left = mouse_x_in_world - mouse_x * self.zoomed_width
self.right = mouse_x_in_world + (1 - mouse_x) * self.zoomed_width
self.bottom = mouse_y_in_world - mouse_y * self.zoomed_height
self.top = mouse_y_in_world + (1 - mouse_y) * self.zoomed_height
def on_draw(self):
print("draw called")
# Init projection matrix
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
# Init Modelview matrix
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
# Save default modelview matrix
glPushMatrix()
# Clear window with ClearColor
glClear(GL_COLOR_BUFFER_BIT)
# Set Orthographic projection matrix
glOrtho(self.left, self.right, self.bottom, self.top, 1, -1)
#self.draw_background()
self.triangle.draw(0, 0, 0)
self.square.draw(-100, -100, 0)
for line in self.gridlines:
line.draw()
# Remove default modelview matrix
glPopMatrix()
def run(self):
print("run called")
pyglet.app.run()
print("run finished")
if __name__ == "__main__":
App = MyWindow(800, 500, resizable=True)
App.drawstuff()
App.run()
And the console shows :
$ python3 environments/testapp.py
init finished
drawstuff called
run called
on_resize called
on_resize called
on_resize called
on_resize called
on_resize called
etc. with at some point :
on_resize called
on_resize called
Traceback (most recent call last):
File "environments/testapp.py", line 185, in <module>
App.run()
File "environments/testapp.py", line 177, in run
pyglet.app.run()
File "/home/admin/.local/lib/python3.7/site-packages/pyglet/app/__init__.py", line 107, in run
event_loop.run()
File "/home/admin/.local/lib/python3.7/site-packages/pyglet/app/base.py", line 159, in run
self._legacy_setup()
File "/home/admin/.local/lib/python3.7/site-packages/pyglet/app/base.py", line 182, in _legacy_setup
window.dispatch_pending_events()
File "/home/admin/.local/lib/python3.7/site-packages/pyglet/window/xlib/__init__.py", line 914, in dispatch_pending_events
EventDispatcher.dispatch_event(self, *self._event_queue.pop(0))
File "/home/admin/.local/lib/python3.7/site-packages/pyglet/event.py", line 415, in dispatch_event
if getattr(self, event_type)(*args):
File "environments/testapp.py", line 110, in on_resize
self.width = width
File "/home/admin/.local/lib/python3.7/site-packages/pyglet/window/__init__.py", line 964, in width
self.set_size(new_width, self.height)
File "/home/admin/.local/lib/python3.7/site-packages/pyglet/window/xlib/__init__.py", line 574, in set_size
self.dispatch_event('on_resize', width, height)
File "/home/admin/.local/lib/python3.7/site-packages/pyglet/window/__init__.py", line 1323, in dispatch_event
if EventDispatcher.dispatch_event(self, *args) != False:
File "/home/admin/.local/lib/python3.7/site-packages/pyglet/event.py", line 415, in dispatch_event
if getattr(self, event_type)(*args):
File "environments/testapp.py", line 110, in on_resize
self.width = width
File "/home/admin/.local/lib/python3.7/site-packages/pyglet/window/__init__.py", line 964, in width
self.set_size(new_width, self.height)
File "/home/admin/.local/lib/python3.7/site-packages/pyglet/window/xlib/__init__.py", line 574, in set_size
self.dispatch_event('on_resize', width, height)
File "/home/admin/.local/lib/python3.7/site-packages/pyglet/window/__init__.py", line 1323, in dispatch_event
if EventDispatcher.dispatch_event(self, *args) != False:
With the last 10 lines repeating for a while, and at the end :
if EventDispatcher.dispatch_event(self, *args) != False:
File "/home/admin/.local/lib/python3.7/site-packages/pyglet/event.py", line 415, in dispatch_event
if getattr(self, event_type)(*args):
File "environments/testapp.py", line 108, in on_resize
print("on_resize called")
RecursionError: maximum recursion depth exceeded while calling a Python object
I find it weird that run() is calling on_resize() on its own, and I really have no clue as to why reassigning the attributes self.width and self.height is entering a recursion.
It also crashes when the value assigned is not width or height, but any constant number as well, and also whether the Window instance is passed resizable = True or = False as argument
run does not call on_resize(), but run executes the event loop and the resize event occurs once after initializing the window.
pyglet.window provides the properties .width and .height, thus there is no need to set them by self.width = width respectively self.height = height. Likely, assigning a value to self.width or self.height triggers the on_resize event. If you do that in on_resize, that causes an endless recursion.
That self.width and self.height contain the current size of the window, can be verified with ease, by printing the values in on_resize:
class MyWindow(pyglet.window.Window):
# [...]
def on_resize(self, width, height):
print("on_resize called")
print(self.width, width)
print(self.height, height)
Do not assign a value to self.width or self.height, if the new value is equal the current value:
class MyWindow(pyglet.window.Window):
# [...]
def on_resize(self, width, height):
new_width = ...
new_height = ...
if new_width != width:
self.width = new_width
if new_height != height:
self.height = new_height

How can i reload a sprite on my game in pygame?

I'm doing a project in python the goal is to make a little game.
In my main menu I have 3 buttons and I found a UI pack which gives me a sprite of buttons with one unpressed and one pressed. I want it to change the sprite when the button is pushed, but when I reload it doesn't work.
Does anyone have an idea of what is wrong?
def main():
pygame.init()
screen = pygame.display.set_mode((800, 500))
pygame.display.set_caption('POKEMON')
bg = pygame.image.load("./assets/img/lspkm.png")
btn_new_Game = btn.Button("./assets/custombtn/btnbcnewgame.png", 305, 270, screen)
btn_continue = btn.Button("./assets/custombtn/btnbccontinue.png", 305, 340, screen)
btn_quit = btn.Button("./assets/custombtn/btnbcquit.png", 305, 410, screen)
screen.blit(bg, (0, 0))
btn_new_Game.draw()
btn_continue.draw()
btn_quit.draw()
while 1:
for event in pygame.event.get():
if event.type == MOUSEBUTTONDOWN:
x, y = event.pos
son = pygame.mixer.Sound("./assets/UIpack/Bonus/click2.ogg")
if btn_quit.rect.collidepoint(x, y):
btn_quit.image = pygame.image.load("./assets/custombtn/btnacquit.png")
btn_quit.draw()
pygame.time.delay(3000)
return
Edit1:
As you can see, I have a button object to help me manage this:
class Button :
def __init__(self, image, x, y, screen):
self.image = pygame.image.load(image)
self.width = self.image.get_width()
self.height = self.image.get_height()
self.rect = pygame.Rect(x, y, self.width, self.height)
self.screen = screen
def move(self, dx, dy):
self.rect.x += dx
self.rect.y += dy
def draw(self):
self.screen.blit(self.image, (self.rect.x, self.rect.y))
def update(self, image):
self.image = pygame.image.load(image)
self.screen.blit(self.image, (self.rect.x, self.rect.y))
It seems like you have the button in this, and you only need to add the sprite changing on click.
You can create a list of button sprites i.e.
button1 = pygame.image.load('/resources/button1.png')
button2 = pygame.image.load('/resources/button2.png')
buttonClicked = pygame.image.load('/resources/buttonDark.png')
buttonList = [button1,button2,buttonClicked]
and then in your main game loop when you detect that the button has been pressed (mousebuttonup on the same position as the sprite)
you can execute this:
# pseudocode for you
when button = clicked
i = 2
screen.blit(buttonList[i])
cheers!

Merging two applications using tk frames

I have a memory game made with python and tkinter. I've also made an application with a simple GUI that uses tk.Frame to show different frames. My problem is putting the memory game in one of the frames of the GUI app.
I have one .py file for the memory game and one for the GUI. The GUI has multiple classes and frames. I want to put the memory game inside one of those frames so that you can navigate through the menu into the game. The game should only show when navigated to.
I have tried:
importing the memorygame file at the top of the GUI file
importing the memorygame file inside a class of the GUI file
copying the whole memorygame code into a class of the GUI file
Importing the file made both the applications try to run on startup in different windows. Copying the game code into a GUI class gave lots of errors.
I have python 3.7 and tkinter 8.6
The memory game starts by creating a canvas that it draws upon:
win = tk.Tk()
canvas = tk.Canvas(win, width = 600, height = 480)
canvas.pack()
class Tile(object):
def __init__(self, x, y, text):
self.y = y
self.x = x
self.text = text
def drawFaceDown(self):
canvas.create_rectangle(self.x, self.y, self.x + 100, self.y + 100, fill = "green")
...
And this is how I use a class to create a frame where I display different things:
class PageMG(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
# memory game goes here
I can show the entire files if anyone wants to see
I want the game to only show until I click on a button that takes me to the frame where I want the game.
Edit: the whole memory game file
import tkinter as tk
import time
from random import randint
from random import shuffle
win = tk.Tk()
canvas = tk.Canvas(win, width = 500, height = 500)
canvas.pack()
class Tile(object):
def __init__(self, x, y, text):
self.y = y
self.x = x
self.text = text
def drawFaceDown(self):
canvas.create_rectangle(self.x, self.y, self.x + 70, self.y + 70, fill = "blue")
self.isFaceUp = False
def drawFaceUp(self):
canvas.create_rectangle(self.x, self.y, self.x + 70, self.y + 70, fill = "blue")
canvas.create_text(self.x + 35, self.y + 35, text = self.text, width = 70, fill = "white", font='Helvetica 12 bold')
self.isFaceUp = True
def isUnderMouse(self, event):
if(event.x > self.x and event.x < self.x + 70):
if(event.y > self.y and event.y < self.y + 70):
return True
tiles = []
colors = [
"Eple",
"Appelsin",
"Banan",
"Agurk",
"Brokkoli",
"Tomat",
"Sitron",
"Melon",
"Hvitløk",
"Erter",
"Jordbær",
"Blåbær"
]
selected = []
for i in range(10):
randomInd = randint(0, len(colors) - 1)
color = colors[randomInd]
selected.append(color)
selected.append(color)
del colors[randomInd]
shuffle(selected)
flippedTiles = []
def mouseClicked(self):
global numFlipped
global flippedTiles
for i in range(len(tiles)):
if tiles[i].isUnderMouse(self):
if (len(flippedTiles) < 2 and not(tiles[i].isFaceUp)) :
tiles[i].drawFaceUp()
flippedTiles.append(tiles[i])
if (len(flippedTiles) == 2):
if not(flippedTiles[0].text == flippedTiles[1].text):
time.sleep(1)
flippedTiles[0].drawFaceDown()
flippedTiles[1].drawFaceDown()
NUM_COLS = 5
NUM_ROWS = 4
for x in range(0,NUM_COLS):
for y in range(0,NUM_ROWS):
tiles.append(Tile(x * 78 + 10, y * 78 + 40, selected.pop()))
for i in range(len(tiles)):
tiles[i].drawFaceDown()
flippedThisTurn = 0
def mouseClicked(event):
global flippedTiles
global flippedThisTurn
for tile in tiles:
if tile.isUnderMouse(event):
if (not(tile.isFaceUp)) :
tile.drawFaceUp()
flippedTiles.append(tile)
flippedThisTurn += 1
if (flippedThisTurn == 2):
win.after(1000, checkTiles)
flippedThisTurn = 0
def checkTiles():
if not(flippedTiles[-1].text == flippedTiles[-2].text): #check last two elements
flippedTiles[-1].drawFaceDown() #facedown last two elements
flippedTiles[-2].drawFaceDown()
del flippedTiles[-2:] #remove last two elements
win.bind("<Button-1>", mouseClicked)
win.mainloop()
importing the memorygame file at the top of the GUI file
importing the memorygame file inside a class of the GUI file
Both these ways will make "both the applications try to run on startup in different windows" this is because the memorygame file makes a new tk.Tk() window, and so does your GUI handler.
copying the whole memorygame code into a class of the GUI file
This could cause many problems, depending where you copy the files to, since these files have their own import dependencies and the import paths may change depending where you copy the files to.
What I would suggest doing is the following, I would change your code of the memory game to make the memory game a class(tk.Frame) (Frame class), then you should be able to import the memory game and stick that frame into the GUI PageMk(tk.Frame), I do not know the dependencies your code has, but this should work.
Example of change using the provided code
class MemGame(tk.Frame):
def __init__(self, parent):
super(MemGame, self).__init__(parent)
self.configure(width=600, height=480)
canvas = tk.Canvas(self, width=600, height=480, bg="red")
canvas.pack()
class Tile:
def __init__(self, parent, x, y, text):
self.parent = parent
self.y = y
self.x = x
self.text = text
def drawFaceDown(self):
self.parent.create_rectangle(self.x, self.y, self.x + 100, self.y + 100, fill="green")
class PageMG(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
x = MemGame(self)
x.pack()
My Edits to the full code:
import tkinter as tk
import time
from random import randint
from random import shuffle
class Controller(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
# the container is where we'll stack a bunch of frames
# on top of each other, then the one we want visible
# will be raised above the others
container = tk.Frame(self)
container.pack(side="top", fill="both", expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
if True:
self.frames = {}
for F in (PageMG,):
page_name = F.__name__
frame = F(parent=container, controller=self)
self.frames[page_name] = frame
# put all of the pages in the same location;
# the one on the top of the stacking order
# will be the one that is visible.
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame("PageMG")
self.geometry("500x400")
def show_frame(self, page_name):
'''Show a frame for the given page name'''
frame = self.frames[page_name]
frame.tkraise()
class PageMG(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
x = MemGame(self)
x.pack()
class Tile(object):
def __init__(self, canvas, x, y, text):
self.canvas = canvas
self.y = y
self.x = x
self.text = text
def drawFaceDown(self):
self.canvas.create_rectangle(self.x, self.y, self.x + 70, self.y + 70, fill = "blue")
self.isFaceUp = False
def drawFaceUp(self):
self.canvas.create_rectangle(self.x, self.y, self.x + 70, self.y + 70, fill = "blue")
self.canvas.create_text(self.x + 35, self.y + 35, text = self.text, width = 70, fill = "white", font='Helvetica 12 bold')
self.isFaceUp = True
def isUnderMouse(self, event):
if(event.x > self.x and event.x < self.x + 70):
if(event.y > self.y and event.y < self.y + 70):
return True
class MemGame(tk.Frame):
def __init__(self, master):
super(MemGame, self).__init__(master)
self.configure(width=500, height=500)
self.canvas = tk.Canvas(self, width=500, height=500)
self.canvas.pack()
self.tiles = []
self.colors = [
"Eple",
"Appelsin",
"Banan",
"Agurk",
"Brokkoli",
"Tomat",
"Sitron",
"Melon",
"Hvitløk",
"Erter",
"Jordbær",
"Blåbær"
]
selected = []
for i in range(10):
randomInd = randint(0, len(self.colors) - 1)
color = self.colors[randomInd]
selected.append(color)
selected.append(color)
del self.colors[randomInd]
shuffle(selected)
self.flippedTiles = []
NUM_COLS = 5
NUM_ROWS = 4
for x in range(0, NUM_COLS):
for y in range(0, NUM_ROWS):
self.tiles.append(Tile(self.canvas, x * 78 + 10, y * 78 + 40, selected.pop()))
for i in range(len(self.tiles)):
self.tiles[i].drawFaceDown()
self.flippedThisTurn = 0
self.bind("<Button-1>", self.mouseClicked)
# def mouseClicked(self):
# for i in range(len(self.tiles)):
# if self.tiles[i].isUnderMouse(self):
# if (len(self.flippedTiles) < 2 and not(self.tiles[i].isFaceUp)) :
# self.tiles[i].drawFaceUp()
# self.flippedTiles.append(self.tiles[i])
# if (len(self.flippedTiles) == 2):
# if not(self.flippedTiles[0].text == self.flippedTiles[1].text):
# time.sleep(1)
# self.flippedTiles[0].drawFaceDown()
# self.flippedTiles[1].drawFaceDown()
def mouseClicked(self, event):
for tile in self.tiles:
if tile.isUnderMouse(event):
if (not(tile.isFaceUp)) :
tile.drawFaceUp()
self.flippedTiles.append(tile)
self.flippedThisTurn += 1
if (self.flippedThisTurn == 2):
self.after(1000, self.checkTiles)
self.flippedThisTurn = 0
def checkTiles(self):
if not(self.flippedTiles[-1].text == self.flippedTiles[-2].text): #check last two elements
self.flippedTiles[-1].drawFaceDown()
self.flippedTiles[-2].drawFaceDown()
del self.flippedTiles[-2:]
if __name__ == '__main__':
c = Controller()
c.mainloop()
Goodluck :)

Won't Pass Object

I have been working on asteroids but my random moving asteroid function won't happen. Here is the code-
class Player():
def __init__(self, canvas):
self.shape = canvas.create_rectangle(910, 540, 960, 590, outline = "blue", fill= "white")
self.pos = canvas.coords(self.shape)
self.xspeed = 0
self.yspeed = 0
def move(self, canvas):
canvas.move(self.shape, self.xspeed, self.yspeed)
self.pos = canvas.coords(self.shape)
class Asteroid():
def __init__(self, canvas):
self.One = canvas.create_oval(0,0, 50, 50, fill="grey")
self.speedx = (10)
self.speedy = (10)
def move(self, canvas):
while True:
self.canvas.move(self.One, self.speedx, self.speedy)
self.canvas.after(20)
self.canvas.update()
def game():
#create window and canvas
parent = Tk()
canvas = Canvas(parent, width = 1920, height = 1080,
background="black")
parent.title("Asteroids")
canvas.pack()
#create player
player = Player(canvas)
enemyOne = Asteroid(canvas)
enemyOne.move(canvas)
And I get the error message-
AttributeError: 'Asteroid' object has no attribute 'canvas'
So the object canvas isn't being passed to Asteroids and def move. I included class Player for reference to show that passing canvas worked in the past. Thanks.
Your constructor call to Asteriod() passes in canvas as a parameter. But the constructor doesn't create an attribute called canvas, in other words there is no assignment
self.canvas = canvas
But the method move() calls self.canvas.move() which implies that self.canvas must exist. It doesn't, and that is why you get the message 'Asteroid' object has no attribute 'canvas'.

Resources