Kivy: How to automate clock processes one after the other - python

Was writing a tetris game and have come across some challenges which seem insurmountable. I would like the blocks to fall one after the other systematically but can't seem to get it as i come across one error after the other. This is my the error serving code
#All modules have been imported
class block1(Widget):
def __init__(self, **kwargs):
super(block1, self).__init__(**kwargs)
xpositions = (0, 50, 100, 150, 200, 250, 300, 350, 400)
self.bind(pos= self.fall)
self.pos_x = random.choice(xpositions)
self.pos_y = Window.height
self.pos = (self.pos_x,self.pos_y)
self.vel_x = 0
self.vel_y = -5
velocity = vel_x,vel_y
def fall(self, **kwargs):
self.pos = Vector(*self.position) + self.pos
if self.pos[1]==0:
self.position[1] = 0
return self.pos
#classmethod
def new_widget(cls):
return cls
#This widget is intended to help me create the new instance of a the
same class i.e to multiply this block within my app
class block2(Widget):
def __init__(self, **kwargs):
super(block1, self).__init__(**kwargs)
xpositions = (0, 50, 100, 150, 200, 250, 300, 350, 400)
self.bind(pos= self.fall)
self.pos_x = random.choice(xpositions)
self.pos_y = Window.height
self.pos = (self.pos_x,self.pos_y)
self.vel_x = 0
self.vel_y = -5
velocity = vel_x,vel_y
def fall(self, **kwargs):
self.pos = Vector(*self.position) + self.pos
if self.pos[1]==0:
self.position[1] = 0
return self.pos
#classmethod
def new_widget(cls):
return cls
#This widget is intended to help me create the new instance of a the
same class i.e to multiply this block within my app
I am facing three problems. The first is from my function under my block class call fall I keep getting an error that says block1 has no attribute called velocity when it is clearly in my __init__ function.
I defined two new block classes customized the .kv files for each, defining different colors and sizes for each. I made my code to define a new startup position and a fixed velocity whenever the class was created. After this I then created the fall class to make my application fall.
In the build of my app class i tried to make the blocks fall one after the other by detecting when one has fallen and making the next begin then.
def build(self):
game= board()
first = block1()
second = block2()
game.add_widget(first)
Clock.schedule_interval(first.fall, 1/60)
if first.pos[1] == 0:
game.add_widget(second)
Clock.schedule_interval(second.fall, 1/60)
Secondly in my init function i tried to bind the fall function to the pos property of the class so as the block falls, the pos property of the class changes with it. Regardless the program doesn't seem to detect the change in the pos regardless of the bind.
Lastly I tried to create a new #classmethod that would help me create new blocks repeatedly and infinitely for the tetris app and dont know where i got it wrong. I created a class method that returns a new instance of the class and planned on creating a loop that keeps creating a new instance of a class in this way:
game = tetrisgame()#This is the main layout for the game
while game:#To create a loop to keep adding new blocks
blockchoice = randint(1,6)
if blockchoice == 1:
game.add_widget(block1.new_widget)
Clock.schedule_interval(block1.fall,1/60)
for i in allblocks:
if block1.collide_widget(i):
block1.position[1] = 0
This gives me a bind error saying widget.bind error, and fails to create a new instance for my class.
Could someone help me clarify?
Note: I tried to pick out the parts of the code that were the source of the error to prevent having a post with a lengthy amount of code, so please note that all modules have been imported and the .kv files with all the designs are omitted.

Lots of problems here. Your block1 indeed does not have a velocity attribute. The velocity mentioned in your __init__() is only a local variable. If you want it to be an attribute, you must assign it as self.velocity=. Also the reference to velx,vely does not reference the attributes self.velx and self.vely.
Your fall() method references a self.position that I do not see defined anywhere.
I don't see how your widget position is being changed. Perhaps that line in your fall() method should be: self.pos = Vector(*self.velocity) + self.pos.
The pos attribute of the Widget tells Kivy where to draw it, so I don't think you worry about binding pos to anything, it will be the position of the Widget.

Related

How to deepcopy an object inside a curses window?

I have taken this code from a larger program, and stripped out almost everything so as to illustrate the core of the problem.
The program is used to create multiple "rooms" that initially have the same attributes, but are later changed such that they are two separate objects.
To create two completely independent Room objects, I have tried to use copy.deepcopy(), but this gives: "TypeError: cannot pickle '_curses.window' object"
import curses
from curses import wrapper
import copy
def main(stdscr):
bg = Background(100, 237)
test_room = Room(10, 15, bg)
new_room = copy.deepcopy(test_room)
print("Test deepcopy: ", new_room.height, new_room.width) ### See if attributes copied successfully
class Background():
def __init__(self, height, width):
self.height = height
self.width = width
self.pad = curses.newpad(self.height, self.width)
class Room():
def __init__(self, height, width, background):
self.height = height
self.width = width
self.bg = background.pad
self.pad = background.pad.subpad(self.height, self.width)
### def __deepcopy__(self, memo)
###return self
while True:
curses.wrapper(main)
I see that the problem is with the self.bg and self.pad lines in the Room class; if these are commented out it works in all other respects, but obviously I need these lines for it to do what I need it to do. I tried defining my own deepcopy method (see commented out in Room class below), but I don't really understand how to use it properly and it doesn't appear to solve the problem.
I get that I am probably making multiple mistakes. What am I doing wrong, and what would be a better solution?

Passing a tkinter canvas between classes without calling the child from within the parent

I am somewhat of a beginner when it comes to Python, but i decided i want to write a basic 2-d physics playground. Unfortionetly i ran straigt into trouble when trying to setup the basic structure.
My plan is to create a GUI with a canvas in a parent function named mainWindow, then i figured i would create a child class (Hero) which creates a circle the user can manipulate on the canvas. This seems to work fairly well.
The problem occurs when i try to do anything with the Hero class, like call a function to delete the circle so i can redraw it in some direction. I can't seem to pass the canvas from the mainWindow to the Hero class. Any help would be greatly appreciated, including telling me that this is the wrong way to do things.
Im adding the two documents im working with since my rambling is probably hard to follow.
I run the program from the phesics.py document, resulting in the GUI poping up with my canvas and a red circle. When i close the window i get the following error:
classes.py", line 29, in moveHeroBody
canvas.delete(heroBody)
NameError: name 'canvas' is not defined
Unfortionetly i dont know how to get the "world" into the child
classes.py
from tkinter import *
class mainWindow():
def __init__(self):
#Setup the GUI
root = Tk()
root.geometry('800x600')
# Setup the canvas within the GUI (master)
world = Canvas(root, height = 600, width = 800, bg = "#FFFFFF")
world.place(relx = 0.5, rely = 0.5, anchor = CENTER)
Hero(world)
root.mainloop()
class Hero(mainWindow):
def __init__(self,world):
#Initial creation of hero at coordinates
x1 = 10
y1 = 10
x2 = 70
y2 = 70
heroBody = world.create_oval(x1,y1,x2,y2, fill = "#FF0000", outline = "#FF0000")
#Move the hero
def moveHeroBody():
print("moveHeroBody")
world.delete(heroBody)
phesics.py
from tkinter import *
from classes import *
mainWindow1 = mainWindow()
moveHero = Hero.moveHeroBody()
You're passing it ok, but you're throwing the value away. Also, Hero shouldn’t inherit from mainWindow.
You need to save world as an attribute so that you can reference it later.
class Hero():
def __init__(self,world):
self.world = world
...
Then, you can use self.world to reference the canvas:
def moveHeroBody():
print("moveHeroBody")
self.world.delete(heroBody)
Though, the above code will fail because heroBody is a variable local to the __init__ - you need to do the same with it:
class Hero():
def __init__(self,world):
self.world = world
...
self.heroBody = world.create_oval(...)
#Move the hero
def moveHeroBody():
print("moveHeroBody")
self.world.delete(self.heroBody)
I think you need to initialize the class Hero in your mainWindow class. The modifications needed to do in the code are:
classes.py
from tkinter import *
from time import sleep
class mainWindow():
def __init__(self):
#Setup the GUI
self.jump_gap = 25
root = Tk()
root.geometry('800x600')
# Setup the canvas within the GUI (master)
self.world = Canvas(root, height = 600, width = 800, bg = "#FFFFFF")
self.world.place(relx = 0.5, rely = 0.5, anchor = CENTER)
self.hero = Hero(self.world)
self.world.pack()
root.bind("<space>",self.jump) # -> [1] Binds the SPACE BAR Key to the function jump
root.mainloop()
def jump(self,event):
gaps = list(range(self.jump_gap))
for i in gaps:
self.world.after(1,self.hero.moveHeroJump(h=i)) # [2] -> Binds the moveHeroJump method with the window action to a queue of updates
self.world.update() #[2] updates the canvas
sleep(0.01*i) # Added some linear wait time to add some look to it
gaps.reverse()
for i in gaps:
self.world.after(1,self.hero.moveHeroJump(h=-i))
self.world.update()
sleep(0.01*i)
class Hero():
def __init__(self,world):
#Initial creation of hero at coordinates
self.world = world
self.x1 = 10
self.y1 = 410
self.x2 = 70
self.y2 = 470
self.heroBody = self.world.create_oval(self.x1,self.y1,self.x2,self.y2, fill = "#FF0000", outline = "#FF0000")
#Move the hero
def moveHeroJump(self,h):
print("moveHeroBody")
self.y1 -= h
self.y2 -= h
self.world.delete(self.heroBody)
self.heroBody = self.world.create_oval(self.x1,self.y1,self.x2,self.y2, fill = "#FF0000", outline = "#FF0000")
physics.py
from tkinter import *
from classes import *
mainWindow1 = mainWindow()
Edit
So this got me playing some minutes ago, and I researched some sources from stack in order to complete this question. Here are the sources (referenced in the code as well):
How to bind spacebar key to a certain method in tkinter python
Moving Tkinter Canvas
The solution edited above is capable to perform a simple animation of a ball jumping. self.jump_gap is a fixed quantity that tells the ball how much does it needs to jump. The jump parses a certain height h to the moveHeroJump method to make the ball change its position, after the change of position is queued into the Canvas an update is called to see the changes on the ball.

kivy: my function points to other/new widget when scheduled with clock

I have my code set up to initially add a few button widgets.
The first button will spawn a rectangular box. This is written as addfunction().
the second button is bound to perform hookupfull(), which does two things - gethooks() and hookfunc():
First it grabs the pos, width, and height of the children of my MainWindowWidget, and puts it in a list for each of those, then deletes extra entries from the buttons (I have my buttons as children of the MainWindowWidget currently, but I only want the properties of the boxes). This is written as gethooks()
Secondly it calculates fancy coordinates from the list and draws a line. This is written as hookfunc()
So, if I press the first button twice and the second button once, it will create two boxes and then draw a line connecting them together. This works fine. Next on my agenda is to schedule stuff like canvas.clear() and redraw the line, etc. every 1/60th of a second. I then create a third button widget to set a flag to start update loop run.
However if I try to schedule hookupfull() with Clock.schedule_interval() it doesn't work as I think it should - not sure how to explain it or what is going on, but the scheduled code doesn't seem to "go" to the MainWindowWidget I want. It seems to be spawning a whole bunch of other MainWindowWidgets.
I figured it's the way I'm referring to the widgets or something with the arguments (which is what I assume to be the (self, *args) portion of the code) or the way I'm declaring the method/function (I'm not sure of the difference between methods and functions still, sorry)
So, I tried to debug it by adding stuff like print self in several places to see what self was.
My code:
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.scatter import Scatter
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.button import Button
from kivy.graphics import Color, Line
from kivy.clock import Clock
boxscale = 1
global startenable
global debug_1
startenable = False
debug_1 = True
class Scatterer(Scatter):
pass
class Drawer(FloatLayout):
pass
class MainWindowWidget(FloatLayout):
def addfunction(self, *args):
s = Scatterer()
d = Drawer()
s.size = 60 * boxscale , 111 * boxscale
# default size * scale
d.size = s.size
self.add_widget(s)
s.add_widget(d)
print "button is pressed"
def startfunc(obj):
global startenable
if startenable == False:
startenable = True
else:
startenable = False
print'startenable set to',startenable
def gethooks(self, *args):
# get hook locations
self.p = []
for child in self.children:
self.p.append(child.pos)
del self.p[(len(self.p)-3):(len(self.p))]
self.w = []
for child in self.children:
self.w.append(child.width)
del self.w[(len(self.w)-3):(len(self.w))]
self.h = []
for child in self.children:
self.h.append(child.height)
del self.h[(len(self.h)-3):(len(self.h))]
if debug_1 == True:
print 'getting hook location........'
print 'self.p:',self.p # list of positions
print 'length of self.p:',len(self.p)
print 'widths:',self.w # list of widths
print 'heights:',self.h# list of heights
print self
def hookfunc(self, *args):
# draw line based on hooks' position
self.h_01_x = \
self.p[0][0]
self.h_01_y = \
self.p[0][1] + (self.h[0]/2)
self.h_02_x = \
self.p[1][0] + self.w[1]
self.h_02_y = \
self.p[1][1] + (self.h[1]/2)
with self.canvas:
Line(bezier=(
self.h_01_x, self.h_01_y,
self.h_01_x - 20, self.h_01_y,
self.h_02_x + 20, self.h_02_y,
self.h_02_x, self.h_02_y,
), width=2)
print self
def hookupfull(self, *args):
self.gethooks()
self.hookfunc()
def update(self, *args):
global debug_1
if startenable == True:
mww= MainWindowWidget()
print mww
mww.hookupfull()
else: # if startenable is false
pass
class Test2App(App):
def build(self):
Clock.schedule_interval(MainWindowWidget.update, \
5.0/60.0)
return MainWindowWidget()
if __name__ == '__main__':
Test2App().run()
kv file:
#:kivy 1.0.9
<MainWindowWidget>
NewButton:
text: 'add'
pos: 100, 0
on_release: root.addfunction()
NewButton:
text: 'start'
pos: 200, 0
on_release: root.startfunc()
NewButton:
text: 'hook up full'
pos: 400, 0
on_release: root.hookupfull()
<NewButton#Button>:
font_size: 15
size_hint: None, None
size: 100, 100
<Scatterer>:
do_rotation: False
size_hint: None, None
size: self.size
<Drawer>:
size: self.size
canvas:
Color:
rgba: 0, 1, 0, 0.3
Rectangle:
pos: self.pos
size: self.size
What I found out was, when I clicked the button that does hookupfull(), which print self, they always return the same thing such as <__main__.MainWindowWidget object at 0x7f110fcef530> no matter how many times hookupfull() is called.
However when the same function is scheduled, self always returns different widgets.
Some other questions I'm wondering about:
Is there a way to assign id's or references to dynamically created widgets? For example, in my code, pressing the first button will create a new Scatter object. Is there a way to point to that specific Scatter object?
How do I refer to widget grandchildren? For example, in my code, I have a FloatLayout object set up as a child of a Scatter object, which is a child of the MainWindowWidget. The FloatLayout object changes in size due to the Scatter, which acts like a controller (and to my knowledge, doesn't actually change in size). I would like to access the properties such as pos and width of the FloatLayout.
arguments for the method/function declarations such as (self, *args) - what do they do?
Each function you define in your class should have self as the first parameter (except for static methods) by convention. It's the instance object of the class when the function is called. You were also creating new instances of the main widget in some functions: refer to inclement's comments above. It doesn't make much sense to do this. Please read the manual: https://docs.python.org/2/tutorial/classes.html#class-objects
Regarding your other questions:
Is there a way to assign id's or references to dynamically created
widgets?
You can assign and id directly by using the named id parameter when instantiating the object e.g. Scatterer(id='myid'). You can refer to it by its id but my personal preference would be to track the objects with a list (see below)
How do I refer to widget grandchildren?
Simply put, don't! Track them directly in a list instead and use references to their properties instead of creating secondary lists of dimensions:
def __init__(self, *args, **kwargs):
self.box_list = [] # <---- set up list to track boxes
super(MainWindowWidget, self).__init__(*args, **kwargs)
....
....
if len(self.box_list) > 1:
box1 = self.box_list[-2] # grab the last two boxes added to the list
box2 = self.box_list[-1]
h_01_x = box1.x
h_01_y = box1.y + (box1.height/2)
h_02_x = box2.x + box2.width
h_02_y = box2.y + (box2.height/2)
with self.canvas:
Line(bezier=(
h_01_x, h_01_y,
h_01_x - 20, h_01_y,
h_02_x + 20, h_02_y,
h_02_x, h_02_y,
), width=2)
I would like to access the properties such as pos and width of the
FloatLayout.
The scatter does have both size and pos. Both are updated when you change the object. It would also be better if you declared your FloatLayout directly in kivy as a child of Scatterer.. There's no need to create them separately
arguments for the method/function declarations such as (self, *args) - what do they do?
self is a reference to the instance whose function has been called. *args is the expansion of the list of unnamed parameters. Take a look at this question: *args and **kwargs?
As a final comment, if you call canvas.clear as you mentioned in your description then you'll erase the buttons and boxes too because you're using the canvas of the main widget. Create another class to contain your line and link its position to the boxes. When you call canvas.clear on that object then it'll clear only the line. If you update the line object when the box position/size changes then you can also move the rendering of the line into the kivy file and get rid of the extraneous clock scheduling.

Delete an instance of a class

I've already asked a similar question here and I received quite a helpful reply.
But since then I've modified my code, now it is more optimized I guess, it is supposed to be more flexible, but the same problem persists. I can not delete an instance of the class.
What I'm trying to do is to create a circle (on a left-click) and then I expect the program to delete the circle (on a right-click).
My code:
from tkinter import *
class Application:
def __init__(self):
self.fen = Tk()
self.fen.title('Rom-rom-roooooom')
self.butt1 = Button(self.fen, text = ' Quit ', command = self.fen.quit)
self.can1 = Canvas(self.fen, width = 300, height = 300, bg = 'ivory')
self.can1.grid(row = 1)
self.butt1.grid(row = 2)
self.fen.bind("<Button-1>", self.create_obj)
self.fen.bind("<Button-3>", self.delete_obj)
self.fen.mainloop()
def create_obj(self, event):
self.d = Oval()
self.can1.create_oval(self.d.x1, self.d.y1, self.d.x2, self.d.y2, fill='red', width = 2)
def delete_obj(self, event):
self.can1.delete(self.d)
class Oval:
def __init__(self):
self.x1 = 50
self.y1 = 50
self.x2 = 70
self.y2 = 70
appp = Application()
so, once again, the problem is that here I can not delete the object:
def delete_obj(self, event):
self.can1.delete(self.d)
One more question. Given the fact that I'm just a begginer I don't know if I chose the right approach as far as class organisation is concerned. Does it look like a well-organized code or should I change anything at this stage already?
These two lines:
self.d = Oval()
self.can1.create_oval(self.d.x1, self.d.y1, self.d.x2, self.d.y2, fill='red', width = 2)
create a new Oval object, assign that object to the name self.d, then create an oval on self.can1 that is completely unrelated (aside from having the same dimensional attributes) from the Oval object assigned to self.d. Instead, I think you want:
o = Oval()
self.d = self.can1.create_oval(o.x1, o.y1, o.x2, o.y2, fill='red', width = 2)
This retains a reference to the object on the Canvas, so you will be able to delete it. Note that Oval is more-or-less-completely pointless, as all it does is provide the dimensions.

Passing window name as parameter to a class

I created a class containing a method to position a window anywhere on the screen. I am using PyQt4 for GUI programming. I wrote following class:
from PyQt4 import QtGui
class setWindowPosition:
def __init__(self, xCoord, yCoord, windowName, parent = None):
self.x = xCoord
self.y = yCoord
self.wName = windowName;
def AdjustWindow(self):
screen = QtGui.QDesktopWidget().screenGeometry()
size = self.geometry()
self.move((screen.width()-size.width())/2, (screen.height()-size.height())/2)
This code needs correction. Any file that imports this class will pass three parameters: desired_X_Position, desired_Y_position and its own name to this class. The method AdjustWindow should accept these three parameters and position the calling window to the desired coordinates.
In the above code, though I have passed the parameters, but not following how to modify the AdjustWindow method.
It is not entirely clear what you are trying to ask, But, you access the values in the method the same way you set them in the constructor.
from PyQt4 import QtGui
class setWindowPosition:
def __init__(self, xCoord, yCoord, windowName, parent = None):
self.x = xCoord
self.y = yCoord
self.wName = windowName;
def AdjustWindow(self):
print self.x, self.y, self.wName //See Here
//now use them how you want
screen = QtGui.QDesktopWidget().screenGeometry()
size = self.geometry()
self.move((screen.width()-size.width())/2, (screen.height()-size.height())/2)
EDIT:
I found this page which seems to be where you grabbed the code from.
Your class is not inheriting from QtGui.QWidget so calls to geometry() and move() are going to fail. Once you do that, it looks like it the code would be:
def AdjustWindow(self):
self.move(self.x, self.y)
However, you still need to figure out how to have your class as the one that controls the window with windowName. It seems like this package is for making GUIs and not controlling external windows. I could be wrong as I have only read enough to make this answer.

Categories