Won't Pass Object - python-3.x

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'.

Related

Why widgets are not displayed in PyQt6?

I am implementing an app through a class that has a QMainWindow member. I call two functions (create_side_menu, create_mixing_window) that display dynamically added widgets inside a bigger widget, which is added to the QMainWindow. The first function works, while the second does not, even though very similar to the first. Can you help me solving this problem?
Update: I modified the code as it appears now in the question, and debugging it seems that the problem is at line 78 (wid = ChannelModifier(af)) where it throws the error: "QWidget: Must construct a QApplication before a QWidget" even though the application is defined in MainWindow class init
Notes:
ChannelModifier is a custom class deriving from QWidget
class MainWindow:
def __init__(self):
self.QListWidgetLeft = None # QListWidget
self.QListWidgetRight = None # QListWidget
self.central_widget = None
self.manager = Manager.get_instance()
self.app = QApplication(sys.argv)
self.window = QMainWindow()
self.set_window()
self.set_menu_bar()
self.sb = self.window.statusBar()
self.sb.showMessage(f"ready to remix")
self.window.show()
sys.exit(self.app.exec())
def set_window(self):
# self.window.setFixedSize(1080, 720)
self.window.setGeometry(50, 50, 1500, 1000)
self.window.setWindowIcon(QIcon("assets/icon.png"))
# self.window.setStyleSheet('background-color: #b7dfeb')
self.window.setStyleSheet('background-color:qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:1, stop:0 '
'rgba(165, 165, 247, 255), stop:1 rgba(255, 255, 255, 255))')
self.window.setWindowTitle("Music Remixes and Mashups")
self.create_central_widget()
def create_central_widget(self):
self.central_widget = QWidget()
self.window.setCentralWidget(self.central_widget)
self.central_widget.setLayout(QHBoxLayout())
self.create_side_menu()
self.create_mixing_window()
def create_side_menu(self):
widget = QWidget()
vbox = QVBoxLayout()
scrollbar = QScrollBar()
scrollbar.setMaximum(100)
scrollbar.setStyleSheet("background-color: rgb(60,60,90); width: 14px; border-radius 0px;")
scrollbar.sliderMoved.connect(scrollbar.value)
self.QListWidgetLeft = QListWidget()
self.QListWidgetLeft.setSpacing(5)
self.QListWidgetLeft.setVerticalScrollBar(scrollbar)
self.QListWidgetLeft.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOn)
pr = self.manager.get_current_project()
if pr:
for af in pr.get_audio_files():
self.add_audiofile_to_side_menu(af)
vbox.addWidget(self.QListWidgetLeft)
widget.setLayout(vbox)
widget.setFixedWidth(230)
widget.setStyleSheet('background-color:white')
self.central_widget.layout().addWidget(widget)
def add_audiofile_to_side_menu(self, af: AudioFile):
if self.QListWidgetLeft is None:
self.QListWidgetLeft = QListWidget()
label = QLabel()
label.setFixedWidth(180)
label.setFixedHeight(180)
if af.get_thumb_path():
label.setPixmap(QPixmap(af.get_thumb_path()))
else:
label.setText(af.get_title())
item = QListWidgetItem(self.QListWidgetLeft)
item.setSizeHint(label.size())
self.QListWidgetLeft.addItem(item)
self.QListWidgetLeft.setItemWidget(item, label)
def create_mixing_window(self): # TODO: it doesn't work
mixing_window = QWidget()
grid = QGridLayout()
pr = self.manager.get_current_project()
if pr:
for af in pr.get_audio_files():
# self.add_channel_modifier_to_mixing_menu(af)
wid = ChannelModifier(af)
grid.addWidget(wid)
# grid.addWidget(self.QListWidgetRight)
mixing_window.setLayout(grid)
mixing_window.setStyleSheet('background-color:white')
self.central_widget.layout().addWidget(mixing_window)
def add_channel_modifier_to_mixing_menu(self, af: AudioFile):
if self.QListWidgetRight is None:
self.QListWidgetRight = QListWidget()
wid = ChannelModifier(af)
item = QListWidgetItem(self.QListWidgetRight)
# item.setSizeHint(wid.size())
self.QListWidgetRight.addItem(item)
self.QListWidgetRight.setItemWidget(item, wid)
With the limited amount of code you have included it is difficult to say if this is what is causing your problem or not, but one issue I see is that you aren't using the correct signature in your calls to addWidget() on your grid layout.
With grid layout you have to specify the column and row you want the widget to occupy when adding them to the layout.
def create_mixing_window(self): # TODO: it doesn't work
mixing_window = QWidget()
grid = QGridLayout()
pr = self.manager.get_current_project()
if pr:
for af in pr.get_audio_files():
self.add_channel_modifier_to_mixing_menu(af)
wid = ChannelModifier(af)
grid.addWidget(wid,0,0) # row 0; column 0
grid.addWidget(self.QListWidgetRight, 0, 1) # row 0; column 1
mixing_window.setLayout(grid)
mixing_window.setStyleSheet('background-color:white')
self.central_widget.layout().addWidget(mixing_window)

Drawing shapes in place of using image

So I have a class that displays some text with a background and an image of a shape that goes along with it. I want to create/draw the shapes inside this class instead of using an actual image file. I found some code that draws a circle/diamond but I have been having trouble implementing it inside my class. What would be some ways to do this?
This is my class:
from tkinter import Tk, Button, Label, BOTTOM, CENTER
from PIL import ImageTk, Image
# set properties of window
window_width = 1920
window_height = 238
# set properties of image
# NOTE: pixel dims of image file mapped 1 to 1 on window
image_width = 238
image_height = 238
# set properties of label
label_width = window_width - image_width
label_height = 238
background_color = 'navy'
foreground_color = 'yellow'
# set properties of font
font_family = 'Helvetica'
font_height = 110
font_weight = 'bold'
# variables for text switching
testIndex = 0
text = [
'Test Test Test',
'TEST TEST !!!',
'12345678901234567890'
]
class GUIClass:
def __init__(self, root):
# bringing text variables into class scope
self.root = root
self.testIndex = testIndex
self.text = text
# creating image for sign
self.image = ImageTk.PhotoImage(Image.open("testImage.png"))
self.imageLabel = Label(image=self.image, width=image_width, height=image_height, bg=background_color)
self.imageLabel.place(x=0, y=0, width=image_width, height=image_height)
# creating label, setting font, position, and binding left-click event listener
self.label = Label(root, text=text[testIndex], bg=background_color, fg=foreground_color, borderwidth=10, relief="solid")
self.label['font'] = (font_family, font_height, font_weight)
self.label.bind("<Button-1>", self.closeScreen)
self.label.place(x=image_width, y=0, width=label_width, height=window_height)
# creating close button to close window (necessary if removing border of window)
#self.close_button = Button(root, text='close', command= root.quit)
#self.close_button.pack(side=BOTTOM)
#set timer to cycle through strings
self.root.after(1500, self.timerTest)
# threaded event handler for when timer elapses
def timerTest(self):
self.testIndex = self.testIndex + 1
self.testIndex %= len(self.text)
self.label['text'] = self.text[self.testIndex]
self.root.after(1500, self.timerTest)
# closes window when label is left-clicked
def closeScreen(self, event):
self.root.quit()
# creates the window
root = Tk()
# sets dimensions of window (ex: "200x200")
root.geometry(str(window_width) + "x" + str(window_height))
# removes boundary, disables resizing, and removes buttons of window
root.overrideredirect(True)
# attaches the window to the GUI class
rootGUI = GUIClass(root)
# continously loops, draws display, and waits for user input
root.mainloop()
This is the code I found for the circle/diamond shape:
from tkinter import Tk, Canvas, Frame, BOTH
class Example(Frame):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.master.title("Shapes")
self.pack(fill=BOTH, expand=1)
canvas = Canvas(self)
canvas.create_oval(5, 5, 233, 233, outline="#fb0", fill="#fb0", width=0)
points = [119, 5, 233, 119, 119, 233, 5, 119]
canvas.create_polygon(points, outline='#fb0', fill='#fb0', width=0)
canvas.pack(fill=BOTH, expand=1)
def main():
root = Tk()
ex = Example()
root.geometry("330x220+300+300")
root.mainloop()
if __name__ == '__main__':
main()

Cropping multiple parts of the image and placing on the canvas in Tkinter

I am new to the Tkinter platform so please help me where I'm going wrong.
I have a floorplan image in which I want to cut the objects in it and place it on a canvas screen so that individual objects can be dragged if I want.
I'm able to cut and paste the objects on the screen except the first object. It is not placed on the screen. Can anyone help me?
I am using a Matlab code to identify the objects in the floorplan image. I am attaching the Matlab file.
Is it possible to add the wall to the screen? I have no idea at all. Can anyone suggest how to add the wall?
Here is my code
import tkinter as tk
from tkinter import *
from PIL import Image,ImageTk
from scipy.io import loadmat
root = tk.Tk()
canvas = tk.Canvas(width=800, height=800)
canvas.grid(row=4,column=0,sticky=(N,W,E,S))
#canvas.config(width=100,height=100)
root.grid_columnconfigure(0, weight=1)
root.grid_rowconfigure(4, weight=1)
mfile=loadmat('C:\\Users\\User\\Desktop\\tkinter_codes\\obj identification\\ans.mat')
#print(mfile.values())
#print(len(mfile['ans'][0]))
print(mfile.values())
class Example(tk.Frame):
def __init__(self, parent):
self.parent =parent
tk.Frame.__init__(self, parent)
self.canvas = tk.Canvas(width=800, height=800)
self.canvas.grid(row=0,column=0,sticky=(N,W,E,S))
#canvas.pack (expand =1, fill =tk.BOTH)
self.canvas.tag_bind("DnD","<Button-1>")
self._drag_data = {"x": 0, "y": 0, "item": None}
self.canvas.tag_bind("token", "<ButtonPress-1>", self.drag_start)
self.canvas.tag_bind("token", "<ButtonRelease-1>", self.drag_stop)
self.canvas.tag_bind("token", "<B1-Motion>", self.drag)
self.canvas.bind("<ButtonPress-1>", self.on_button_1)
self.iimg=Image.open("C:\\Users\\User\\Desktop\\tkinter_codes\\floorplans\\ROBIN\\Dataset_3rooms\\Cat1_1.jpg")
#iimg=iimg.resize((1000, 800), Image.ANTIALIAS)
self.canvas.img=ImageTk.PhotoImage(self.iimg)
#canvas.img = canvas.img.resize((250, 250), Image.ANTIALIAS)
self.canvas_img=canvas.create_image(0,0,image=self.canvas.img,anchor="nw")
self.mylist=[]
for x in range(len(mfile['ans'][0])):
#canvas.create_rectangle((mfile['ans'][0][x][0][0],mfile['ans'][0][x][0][1],mfile['ans'][0][x][0][0]+mfile['ans'][0][x][0][2],mfile['ans'][0][x][0][1]+mfile['ans'][0][x][0][3]),outline='red',tags=("token","DnD"))
self.im_crop = self.iimg.crop((mfile['ans'][0][x][0][0],mfile['ans'][0][x][0][1],mfile['ans'][0][x][0][0]+mfile['ans'][0][x][0][2],mfile['ans'][0][x][0][1]+mfile['ans'][0][x][0][3]))
self.canvas.im_crop2=ImageTk.PhotoImage(self.im_crop)
self.canvas.create_image(mfile['ans'][0][x][0][0],mfile['ans'][0][x][0][1], image=self.canvas.im_crop2)
#canvas.create_image(1000,1000,image=im_crop2)
#if(x>=0):
self.mylist.append(self.canvas.im_crop2)
#im_crop.show()
#canvas.iiiimg=ImageTk.PhotoImage(im_crop)
#canvas.create_image(150,150,image=canvas.im_crop2)
self.popup = tk.Menu(root, tearoff=0)
#self.popup.add_command(label="delete",command=lambda: self.dele(id))
self.popup.add_command(label="delete",
command=lambda: self.dele(id))
self.popup.add_command(label="add",command= lambda: root.bind("<Button-1>",self.addn))
root.bind("<Button-3>", self.do_popup)
self.canvas.delete(self.canvas_img)
def do_popup(self,event):
# display the popup menu
try:
self.popup.tk_popup(event.x_root, event.y_root, 0)
finally:
# make sure to release the grab (Tk 8.0a1 only)
self.popup.grab_release()
def on_button_1(self, event):
iid = self.canvas.find_enclosed(event.x-150, event.y-150, event.x + 150, event.y + 100)
#iid=canvas.find_closest(event.x, event.y)[0]
self.canvas.itemconfigure("DEL")
self.canvas.dtag("all","DEL")
self.canvas.itemconfigure(iid, tags=("DEL","DnD","token","drag"))
#canvas.unbind("<Button-1>")
def create_token(self, x, y, color):
"""Create a token at the given coordinate in the given color"""
self.canvas.create_rectangle(
x ,
y ,
x + 50,
y + 50,
outline=color,
fill=color,
tags=("token","DnD"),
)
def create_token1(self,x,y,color):
self.canvas.create_rectangle(
x ,
y ,
x + 25,
y + 25,
outline=color,
fill=color,
tags=("token","DnD"),
)
def drag_start(self, event):
"""Begining drag of an object"""
# record the item and its location
self._drag_data["item"] = self.canvas.find_closest(event.x, event.y)[0]
rect=self.canvas.bbox(self._drag_data["item"])
self.canvas.addtag_enclosed("drag",*rect)
print(rect)
self._drag_data["x"] = event.x
self._drag_data["y"] = event.y
def drag_stop(self, event):
"""End drag of an object"""
# reset the drag information
self._drag_data["item"] = None
self._drag_data["x"] = 0
self._drag_data["y"] = 0
self.canvas.dtag("drag","drag")
def drag(self, event):
"""Handle dragging of an object"""
# compute how much the mouse has moved
self.delta_x = event.x - self._drag_data["x"]
self.delta_y = event.y - self._drag_data["y"]
# move the object the appropriate amount
self.canvas.move("drag", self.delta_x, self.delta_y)
# record the new position
self._drag_data["x"] = event.x
self._drag_data["y"] = event.y
def dele(self,id):
#canvas.tag_bind(id,"<Button-1>")
self.canvas.delete("DEL")
def addn(self,event):
canvas.create_rectangle(event.x,event.y,event.x+25,event.y+25,fill='red',tags=("DnD","token"))
root.unbind("<Button-1>")
Example(root) #pack(fill="both", expand=True)
root.mainloop()
This is the Matlab code I am using for identifying objects

TypeError : Circle() takes no arguments

I'm getting the error TypeError : Circle() takes no arguments when trying to run the code above. Does anyone know what I'm missing?
class Circle:
is_shape = True
radius,color=0,""
def __init__(self, color, radius):
self.color = color
self.radius = radius
def display(self):
print("radius:",self.radius)
print("color:",self.color)
first_circle = Circle("red",2)
first_circle.display()
Actually you have an indentation problem in your code. Your functions aren't defined under classes, that's why it shows your class Circle() takes no argument.
To correct this put your functions inside a class :
class Circle:
is_shape = True
radius, color = 0, ""
def __init__(self, color, radius):
self.color = color
self.radius = radius
def display(self):
print("radius:", self.radius)
print("color:", self.color)
first_circle = Circle("red", 2)
first_circle.display()

How do I pass a rect into pygame.display.update() to update a specific area of the window?

On the documentation page for pygame.display.update(), it says you can pass a rect into the method to update part of the screen. However, all of the examples I see just pass an existing rect from an image or shape in the program. How can I tell it to update an area on the screen directly? For example, when drawing a rectangle, I could use a rect argument of (100,200,30,40). This would draw a rectangle with a top at 200, a left at 100, a width of 30, and a height of 40. How can I pass a similar argument into pygame.display.update()? I tried pygame.display.update((100,200,30,40)), but this updates the entire window.
Just define a rect and pass it to pygame.display.update() to update only this specific region of the display. You can also pass a list of rects.
import random
import pygame as pg
from pygame.math import Vector2
# A simple sprite, just to have something moving on the screen.
class Ball(pg.sprite.Sprite):
def __init__(self, screen_rect):
super().__init__()
radius = random.randrange(5, 31)
self.image = pg.Surface((radius*2, radius*2), pg.SRCALPHA)
pg.draw.circle(self.image, pg.Color('dodgerblue1'), (radius, radius), radius)
pg.draw.circle(self.image, pg.Color('dodgerblue3'), (radius, radius), radius-2)
self.rect = self.image.get_rect(center=screen_rect.center)
self.vel = Vector2(random.uniform(-2, 2), random.uniform(-2, 2))
self.pos = Vector2(self.rect.center)
self.screen_rect = screen_rect
self.lifetime = 350
def update(self):
self.pos += self.vel
self.rect.center = self.pos
self.lifetime -= 1
if not self.screen_rect.contains(self.rect) or self.lifetime <= 0:
self.kill()
def main():
screen = pg.display.set_mode((800, 600))
screen.fill((20, 40, 70))
pg.display.update()
screen_rect = screen.get_rect()
clock = pg.time.Clock()
all_sprites = pg.sprite.Group()
# Pass this rect to `pg.display.update` to update only this area.
update_rect = pg.Rect(50, 50, 500, 400)
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
all_sprites.add(Ball(screen_rect))
all_sprites.update()
screen.fill((20, 50, 90))
all_sprites.draw(screen)
# Update only the area that we specified with the `update_rect`.
pg.display.update(update_rect)
clock.tick(60)
if __name__ == '__main__':
pg.init()
main()
pg.quit()

Resources