Swinging pendulum does not work - python

I am having a little trouble with this project. I have to create a pendulum using key handles and the code I have for the key's up and down don't seem to be working. "up" is suppose to make the pendulum go faster and "down" makes it go slower. This is the code that I have so far. can somebody please help.
from tkinter import * # Import tkinter
import math
width = 200
height = 200
pendulumRadius = 150
ballRadius = 10
leftAngle = 120
rightAngle = 60
class MainGUI:
def __init__(self):
self.window = Tk() # Create a window, we may call it root, parent, etc
self.window.title("Pendulum") # Set a title
self.canvas = Canvas(self.window, bg = "white",
width = width, height = height)
self.canvas.pack()
self.angle = leftAngle # Start from leftAngle
self.angleDelta = -1 # Swing interval
self.delay = 200
self.window.bind("<Key>",self.key)
self.displayPendulum()
self.done = False
while not self.done:
self.canvas.delete("pendulum") # we used delete(ALL) in previous lab
# here we only delete pendulum object
# in displayPendulum we give the tag
# to the ovals and line (pendulum)
self.displayPendulum() # redraw
self.canvas.after(self.delay) # Sleep for 100 milliseconds
self.canvas.update() # Update canvas
self.window.mainloop() # Create an event loop
def displayPendulum(self):
x1 = width // 2;
y1 = 20;
if self.angle < rightAngle:
self.angleDelta = 1 # Swing to the left
elif self.angle > leftAngle:
self.angleDelta = -1 # Swing to the right
self.angle += self.angleDelta
x = x1 + pendulumRadius * math.cos(math.radians(self.angle))
y = y1 + pendulumRadius * math.sin(math.radians(self.angle))
self.canvas.create_line(x1, y1, x, y, fill="blue", tags = "pendulum")
self.canvas.create_oval(x1 - 2, y1 - 2, x1 + 2, y1 + 2,
fill = "red", tags = "pendulum")
self.canvas.create_oval(x - ballRadius, y - ballRadius,
x + ballRadius, y + ballRadius,
fill = "green", tags = "pendulum")
def key(self,event):
print(event.keysym)
print(self.delay)
if event.keysym == 'up':
print("up arrow key pressed, delay is",self.delay)
if self.delay >10:
self.delay -= 1
if event.keysym == 'Down':
print("Down arrow key pressed,delay is",self.delay)
if self.delay < 200:
self.delay += 1
if event.keysym=='q':
print ("press q")
self.done = True
self.window.destroy()
MainGUI()

The root of the problem is that the event keysym is "Up" but you are comparing it to the all-lowercase "up". Naturally, the comparison fails every time. Change the if statement to if event.keysym == 'Up':
Improving the animation
In case you're interested, there is a better way to do animation in tkinter than to write your own infinite loop. Tkinter already has an infinite loop running (mainloop), so you can take advantage of that.
Create a function that draws one frame, then have that function arrange for itself to be called again at some point in the future. Specifically, remove your entire "while" loop with a single call to displayFrame(), and then define displayFrame like this:
def drawFrame(self):
if not self.done:
self.canvas.delete("pendulum")
self.displayPendulum()
self.canvas.after(self.delay, self.drawFrame)
Improving the bindings
Generally speaking, your code will be marginally easier to manage and test if you have specific bindings for specific keys. So instead of a single catch-all function, you can have specific functions for each key.
Since your app supports the up key, the down key, and the "q" key, I recommend three bindings:
self.window.bind('<Up>', self.onUp)
self.window.bind('<Down>', self.onDown)
self.window.bind('<q>', self.quit)
You can then define each function to do exactly one thing:
def onUp(self, event):
if self.delay > 10:
self.delay -= 1
def onDown(self, event):
if self.delay < 200:
self.delay += 1
def onQuit(self, event):
self.done = True
self.window.destroy()

Related

How can I add collisions to a list of objects (circles) in tkinter?

import time
from tkinter import *
import random
class SpaceField:
def __init__(self):
self.window = Tk()
self.window.title("Asteriods")
self.canvas = self.canvas_display() #creates canvas
self.asteriods = self.asteriod_creation_seperation() #creates asteroids
self.active = True
self.move_active() #Moves asteroids
self.canvas.update()
def asteriod_creation_seperation(self): #creation of multple asteriods
asteriod_spacingx = random.randint(1,800)
asteriod_spacingy = random.randint(1,800)
asteriod_list = list() # could list([])
for i in range(15):
asteriod = self.canvas.create_oval( 30, 50 , 80 , 100 , tags="asteriod", width=2, outline="white")
asteriod_list.append("asteriod")
self.canvas.move(asteriod, asteriod_spacingx, asteriod_spacingy)
asteriod_spacingx = random.randint(1,500)
asteriod_spacingy = random.randint(1,500)
print(asteriod_spacingy)
return asteriod_list
Asteroid Creation. Creates asteroids and gives them random positions.
def asteriod_update(self): #asteriods movement method #MAin problem
x12 = 1
self.canvas.move("asteriod", 3, x12)
pos = self.canvas.coords("asteriod")
print(pos)
if (pos)[2] > 500:
x12 *= 5
I think this is where I need to add the collision detection. I just have no idea how to combine the lists of the circles and the collisions.
def move_active(self): #Basically a true loop
if self.active:
self.asteriod_update()
self.window.after(40, self.move_active)
def canvas_display(self): #canvas
canvas = Canvas(self.window, width=500, height=400, background='black')
canvas.pack(expand=True, fill="both")
canvas.update()
return canvas
Canvas display nothing special
def run(self):
self.window.mainloop()
if __name__ == '__main__':
SpaceF = SpaceField()
SpaceF.run()
Asteroids is a classic game but there were a number of problems in your code. The main one was calling move_active during initialization. This prevented the code from completing its mainloop initialization.
The other problem was the asteroid_update method that basically didn't do anything, also using tags to control all asteroids didn't work either.
Everything else was OK, although you might consider using polygons.
Here is one way to produce a bouncing objects program. I've inserted remarks that describe the methods used.
Objects change the speed and direction when they hit the boundary so their trajectories are randomized.
from tkinter import *
from random import randint as rnd
class SpaceField:
def __init__(self):
self.window = Tk()
self.window.title("Asteriods")
# Define canvas size and active flag
self.wide, self.high, self.active = 500, 400, True
self.canvas_display()
self.asteriod_creation_seperation()
def asteriod_creation_seperation(self):
self.asteroids, self.speed = [], []
size, radius = 50, 25
for i in range(15):
spacex = rnd(size, self.wide - size)
spacey = rnd(size, self.high - size)
self.asteroids.append( # Store oval item id
self.canvas.create_oval(
spacex, spacey, spacex+size, spacey+size,
width=2, tags = "asteriod", outline = "white"))
self.speed.append((rnd(1,4),rnd(1,4))) # Store random speed x, y
def asteriod_update(self): # MAIN DRIVER: Work on ALL asteroids
for i, a in enumerate(self.asteroids):
xx, yy = self.speed[i] # get speed data
x, y, w, h = self.canvas.coords(a)
# check for boundary hit then change direction and speed
if x < 0 or w > self.wide:
xx = -xx * rnd(1, 4)
if y < 0 or h > self.high:
yy = -yy * rnd(1, 4)
# Limit max and min speed then store it
self.speed[i] = (max( -4, min( xx, 4)), max( -4, min( yy, 4 )))
self.canvas.move(a, xx, yy) # update asteroid position
def move_active(self):
if self.active:
self.asteriod_update()
self.window.after(40, self.move_active)
def canvas_display(self):
self.canvas = Canvas(
self.window, width = self.wide,
height = self.high, background = "black")
self.canvas.pack(expand = True, fill = "both")
def run(self): # Begin asteroids here so that mainloop is executed
self.window.after(200, self.move_active)
self.window.mainloop()
if __name__ == "__main__":
SpaceF = SpaceField()
SpaceF.run()

Simple Box game not Moving python [duplicate]

This question already has answers here:
How can I make a sprite move when key is held down
(6 answers)
Closed 1 year ago.
Hi am new fairly new to programming so I tried to learn by making my hands dirty. I tried making a simple box game program all was working fine until I made my player Class which inherit from the base class of Box_User for some reasons my box(player class object) is no longer moving I tried to see what it prints and it seems like none of the keys work when I press them
Can anyone explain what happened?
import pygame
pygame.init()
# Classes
class Window():
def __init__(self, width, height):
self.width = width
self.height = height
self.window_init() # this functions is to get the window up
def window_init(self):
self.window = pygame.display.set_mode((self.width, self.height)) # this is the main window
self.background = pygame.Surface((self.window.get_size())) # this one is named background but should be use like the main window
self.background.fill((255, 255, 255))
#staticmethod
def draw_objects_to_Screen():
win.window.blit(win.background, (0,0))
win.window.blit(box.box, (box.pos_x - scroll[0], box.pos_y + scroll[1])) # the scroll is used to make it look like its moving
win.window.blit(box2.box, (box2.pos_x - scroll[0], box2.pos_y + scroll[1]))
pygame.display.update()
class Box_User():
jump = 10
jump_status = False
def __init__(self, x, y, height, width):
self.pos_x = x
self.pos_y = y
self.box_height = height
self.box_width = width
self.box = pygame.Surface((self.box_height, self.box_width))
self.color = (0, 20, 0)
self.draw_box()
def draw_box(self):
pygame.draw.rect(self.box, self.color, pygame.Rect(self.pos_x, self.pos_y, self.box_height, self.box_width))
#staticmethod
def _jump():
if Box_User.jump >= -10:
box.pos_y -= (Box_User.jump * abs(Box_User.jump)) * 0.3
scroll[1] += (Box_User.jump * abs(Box_User.jump)) * 0.3
Box_User.jump -= 1
else:
Box_User.jump = 10
Box_User.jump_status = False
class Player(Box_User):
key_pressed = pygame.key.get_pressed()
def __init__(self, x, y, height, width):
super().__init__(x, y, height, width)
# self.pos_x = x
#self.pos_y = y
def movements(self):
if self.key_pressed[pygame.K_a]:
self.pos_x -= 5
if self.key_pressed[pygame.K_d]:
self.pos_x += 5
# place here things that you dont want to move while the box is jumping
if not self.jump_status:
if self.key_pressed[pygame.K_w]:
self.jump_status = True
else:
self._jump() # the box jumps here
class Auto_Box(Box_User):
def __init__(self, x, y, height, width):
super().__init__(x, y, height, width)
pass
# Variables
# window
win = Window(700, 500)
clock = pygame.time.Clock()
FPS = 60
# boxes
box = Player(30, 200, 64, 64)
box2 = Box_User(300, 200, 100, 120)
# The Scroll which controls the things when the box is moving
scroll = [0, 0]
# Functions
# Main Loop
def main():
run = True
while run:
clock.tick(FPS)
# value of the scroll is updated here
scroll[0] += (box.pos_x - scroll[0]-250)
#print("coordinate are")
#print(scroll)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
box.movements()
print(box.pos_x, box.pos_y)
Window.draw_objects_to_Screen()
if __name__ == "__main__":
main()
pygame.key.get_pressed() returns a list of booleans that represent the state of the keyboard when you call the function.
If you continually check that list, no keys will ever change state. It's just a list of Trues and Falses.
You need to reset the values of key_pressed in the loop, updating it with fresh values from pygame.key.get_pressed().
This should do the trick:
def movements(self):
self.key_pressed = pygame.key.get_pressed()
if self.key_pressed[pygame.K_a]:
self.pos_x -= 5
...
The key_pressed class variables is only initialized once. It is never changed. Therefore the pressed keys are not detected.
pygame.key.get_pressed() returns a iterable with the current state of all keyboard buttons. You must get the states of the keys in every frame:
class Player(Box_User):
def __init__(self, x, y, height, width):
super().__init__(x, y, height, width)
def movements(self):
# get the current states of the keys
self.key_pressed = pygame.key.get_pressed()
if self.key_pressed[pygame.K_a]:
self.pos_x -= 5
if self.key_pressed[pygame.K_d]:
self.pos_x += 5
# place here things that you dont want to move while the box is jumping
if not self.jump_status:
if self.key_pressed[pygame.K_w]:
self.jump_status = True
else:
self._jump() # the box jumps here

Tkinter: remove keyboard double-click prevention

I'm programming a python version of Asteroids (https://en.wikipedia.org/wiki/Asteroids_(video_game)) using Tkinter.
This is the piece of code that let the ship to move ahead:
def move(self, sx=0, sy=0, ms=2):
try:
self.root.after_cancel(self.m)
except AttributeError:
pass
ms += 1
if ms > 30:
return
self.parent.move(self.ship, sx, sy)
self.m = self.root.after(ms, lambda sx1=sx, sy1=sy, millisec=ms: self.move(sx1, sy1, ms))
And here there's the actual fuction that runs when Up Arrow is pressed:
def avanti(self, event):
self.s = -2.5
x = self.s * math.sin(math.radians(self.angle)) * -1
y = self.s * math.cos(math.radians(self.angle))
self.move(x, y)
When you press UpArrow multiple times, the code works pretty well, the only problem is that when you hold it down, the ship moves once, then there's a small break like 0.2s long, and then it starts going ahead regularly, until you release the key. I think that the computer prevents you to double-click worngly and then, when it sees that you actually want to, it removes this prevenction.
Is there a way to remove this block since the first press?
EDIT: You can actually change the typing delay on the entire computed by going on Keyboard -> typing delay but what I want to do is to remove the delay only on the python program.
You can use <Up> to set move_up = True and <KeyRelease-Up> to set move_up = False and then you can use after to run function which will check move_up and move object.
Working example - code from other question (about moving platform)
import tkinter as tk
# --- constants ---
DISPLAY_WIDTH = 800
DISPLAY_HEIGHT = 600
CENTER_X = DISPLAY_WIDTH//2
CENTER_Y = DISPLAY_HEIGHT//2
# --- functions ---
# for smooth move of platform
def up_press(event):
global platform_up
platform_up = True
def up_release(event):
global platform_up
platform_up = False
def down_press(event):
global platform_down
platform_down = True
def down_release(event):
global platform_down
platform_down = False
def eventloop():
# move platform
if platform_up:
# move
canvas.move(platform, 0, -20)
# check if not leave canvas
x1, y1, x2, y2 = canvas.coords(platform)
if y1 < 0:
# move back
canvas.move(platform, 0, 0-y1)
if platform_down:
# move
canvas.move(platform, 0, 20)
# check if not leave canvas
x1, y1, x2, y2 = canvas.coords(platform)
if y2 > DISPLAY_HEIGHT:
# move back
canvas.move(platform, 0, -(y2-DISPLAY_HEIGHT))
root.after(25, eventloop)
# --- main ---
# - init -
root = tk.Tk()
canvas = tk.Canvas(root, width=DISPLAY_WIDTH, height=DISPLAY_HEIGHT)
canvas.pack()
# - objects -
platform = canvas.create_rectangle(CENTER_X-15, CENTER_Y-15, CENTER_X+15, CENTER_Y+15, fill="green")
platform_up = False
platform_down = False
root.bind('<Up>', up_press)
root.bind('<KeyRelease-Up>', up_release)
root.bind('<Down>', down_press)
root.bind('<KeyRelease-Down>', down_release)
# - mainloop -
root.after(25, eventloop)
root.mainloop()

Tkinter canvas animation flicker

I wrote a Python program with Tkinter that makes a ball bounce around the screen. It works great, except for one problem: the outermost edges of the ball flicker as the ball moves.
I understand Tkinter automatically does double buffering, so I thought I shouldn't be having problems with tearing. I'm not sure where the error is coming from. Any ideas on how I can get rid of it?
Code is below. Here's the gist of it: class Ball extends Tk and gets created when the program is run. It sets up the game, encapsulating it in a GModel. GModel then runs the game with run() and just loops over and over, calling update().
import threading
from time import sleep, clock
from tkinter import *
from tkinter import ttk
SPEED = 150 # Pixels per second
WIDTH = 400
HEIGHT = 500
RAD = 25
PAUSE = 0 # Time added between frames. Slows things down if necessary
class GModel:
# Contains the game data.
def __init__(self, can):
# can is Canvas to draw on
self.can = can
self.circ = can.create_oval(0,0, 2*RAD, 2*RAD)
# Position and velocity of the ball
self.x, self.y = RAD, RAD
self.dx, self.dy = SPEED, SPEED
# Ball is moving?
self.is_running = True
def update(self, elapsed_time):
# Updates the dynamic game variables. elapsed_time is in seconds.
change_x = self.dx * elapsed_time
change_y = self.dy * elapsed_time
self.can.move(self.circ, change_x, change_y)
self.x += change_x
self.y += change_y
self.resolve_collisions()
def resolve_collisions(self):
# If the ball goes off the edge, put it back and reverse velocity
if self.x - RAD < 0:
self.x = RAD
self.dx = SPEED
elif self.x + RAD > WIDTH:
self.x = WIDTH - RAD
self.dx = -SPEED
if self.y - RAD < 0:
self.y = RAD
self.dy = SPEED
elif self.y + RAD > HEIGHT:
self.y = HEIGHT - RAD
self.dy = -SPEED
def end_game(self):
self.is_running = False
def run(self):
last_time = 0.0
while self.is_running:
# Get number of seconds since last iteration of this loop
this_time = clock()
elapsed_time = this_time - last_time
last_time = this_time
try: # Use this here in case the window gets X'd while t still runs
self.update(elapsed_time)
except:
self.is_running = False
if (PAUSE > 0):
sleep(PAUSE) # Slow things down if necessary
class Ball(Tk):
def __init__(self):
Tk.__init__(self)
self.can = Canvas(self, width=WIDTH, height=HEIGHT)
self.can.grid(row=0, column=0, sticky=(N, W, E, S))
self.gm = GModel(self.can)
def mainloop(self):
t = threading.Thread(target=self.gm.run, daemon=True)
t.start()
Tk.mainloop(self)
def main():
Ball().mainloop()
if __name__ == "__main__":
main()

When moving tkinter object other image disappears

Both items will move fine however, whenever I am using the bound keys to the event handler the other image (enemy) will disappear until i stop moving the player with the keys. Is there a way around this? I've tried the canvas method .move also and tried searching didn't find a solution from the searches I tried.
def move_player(self, event):
if event.keysym.lower() == "a":
self.player_x -= self.player_delta_x
if event.keysym.lower() == "d":
self.player_x += self.player_delta_x
self.canvas.delete(self.player)
self.make_player()
def move_enemy(self):
self.enemy_x += self.enemy_delta_x
self.enemy_y -= self.enemy_delta_y
#Keep the enemy within the bounds of the screen
if self.enemy_x > self.width - self.enemy_radius:
self.enemy_delta_x *= -1
if self.enemy_x < 0:
self.enemy_delta_x *= -1
if (self.enemy_y >= height / 2 or
self.enemy_y <= 0 + self.enemy_radius):
self.enemy_delta_y *= -1
self.canvas.delete(self.enemy)
self.make_enemy()
if __name__ == "__main__":
root = tk.Tk()
canvas = tk.Canvas(root, bg = "black")
width = root.winfo_screenwidth()
height = root.winfo_screenheight() - TASKBAR_OFFSET
root.geometry("%dx%d" % (width, height))
game = Game(root, canvas, width, height)
game.make_menu()
game.make_background()
game.make_player()
game.make_enemy()
while True:
game.move_enemy()
game.canvas.update()
time.sleep(0.025)
root.mainloop()
First of all, instead of using a while True loop, use root.after(0, animateFunction).
And then inside of the animate function:
def animateFunction():
#Movement Code
moveEnemyFunction()
checkPlayerMovement()
root.after(animateFunction, 10)
If you loop like this, it should complete the movement task each frame and ultimately move the player without losing the enemy each frame the player moves.
Now, there is no guarantee this will work, because I have no idea how your movement works and how you bound it, but I believe this should solve your problem.

Categories

Resources