Related
I am making a one man pong game and I got everything except for moving the paddle up and down and I don't really understand how to do it can someone help. Is it something to do with the "pos" or is it something to do it with the syntax of the lines. The part where it controls the movements of the paddles is def move paddle
import tkinter
# steps
# ball diagonal
# paddle draw
# paddle animation with keyboard (right/left) -> challenge up/down
# collisions (if time)
# graphic parameters
canvas_width = 400
canvas_height = 500
ball_size = 30
timer_refresh = 20
paddle_width = 100
paddle_height = 20
# ball movement
y_move = 2
x_move = 2
# paddle movement
paddle_speed = 6
# game_state
game_running = True
def end_game():
global game_running
game_running = False
canvas.create_text(canvas_width/2, canvas_height/2, text="you lost!")
# move paddle when key is pressed
def move_paddle(event):
key_symbol = event.keysym
print(key_symbol)
pos = canvas.coords(paddle)
left = pos[0]
right = pos[2]
up = pos[1]
down = pos[3]
if key_symbol == "Left" and left > 0:
canvas.move(paddle, -paddle_speed, 0)
elif key_symbol == "Right" and right <= canvas_width:
canvas.move(paddle, paddle_speed, 0)
# move paddle up
elif key_symbol == "Up" and up >= 0:
canvas.move(paddle, paddle_speed, 0)
# move paddle down
elif key_symbol == "Down" and down <= canvas_width:
canvas.move(paddle, paddle_speed, 0)*
def collision(ball_pos):
overlap_result = canvas.find_overlapping(ball_pos[0],ball_pos[1],ball_pos[2],ball_pos[3])
if paddle in overlap_result:
return True;
else:
return False;
# draw/move ball
def draw():
global y_move, x_move
canvas.move(ball1, x_move, y_move)
pos = canvas.coords(ball1)
top_y = pos[1]
bottom_y = pos[3]
left = pos[0]
right = pos[2]
if top_y <= 0:
y_move = -y_move
elif bottom_y >= canvas_height-5:
y_move = -y_move
end_game()
# did I hit left or right wall?
elif left <= 0 or right >= canvas_width-5:
x_move = -x_move
# did I collide with the paddle? if so bounce vertically
if collision(pos):
y_move = -y_move
# animation timer
def master_timer():
# draw/move ball
draw()
# tkinter processing
tk.update_idletasks()
tk.update()
if game_running:
tk.after(timer_refresh, master_timer)
tk = tkinter.Tk()
tk.title("Simplified Pong")
# block resizing window
tk.resizable(0,0)
# drawing the canvasd
canvas = tkinter.Canvas(tk, width=canvas_width, height=canvas_height, bd=0, highlightthickness=0)
canvas.pack()
ball1 = canvas.create_oval(0, 0, ball_size, ball_size, fill="red")
canvas.move(ball1, canvas_width/2, canvas_height/2)
paddle = canvas.create_rectangle(0,0,paddle_width, paddle_height, fill="black")
canvas.move(paddle, canvas_width/2, canvas_height/1.2)
canvas.bind_all("<KeyPress-Right>", move_paddle)
canvas.bind_all("<KeyPress-Left>", move_paddle)
canvas.bind_all("<KeyPress-Up>", move_paddle)
canvas.bind_all("<KeyPress-Down>", move_paddle)
master_timer()
tk.mainloop()
The problem is quite simple if you refer to the docs of move method, for tkinter, or have an understanding of how to use it. From the docs:
.move(tagOrId, xAmount, yAmount)
Moves the items specified by tagOrId by adding xAmount to their x coordinates and yAmount to their y coordinates.
So if you notice, it takes the x coords first and then the y coords. So when you want to translate(move) upon the y axis(up and down), you want to alter the y axis argument, but instead you are doing it for the x axis. So you have to pass the variable onto the correct parameter only.
elif key_symbol == "Up" and up >= 0:
canvas.move(paddle, 0, -paddle_speed)
# move paddle down
elif key_symbol == "Down" and down <= canvas_width:
canvas.move(paddle, 0, paddle_speed)
Also note that you can get rid of all the key binds and just keep a single general bind, because inside your function, you are already getting the key that is pressed dynamically, so it doesn't make much sense to bind to all possible keys:
canvas.bind_all("<KeyPress>", move_paddle) # Instead of Up, Down, Left and Right
Also another tip is, you can take advantage of * to pass the contents of the iterable as the argument to the function:
canvas.find_overlapping(*ball_pos) # Instead of ball_pos[0] and so on
I've been making a game where the player controls a ship and has to collect things around the screen. However, I found that the movement of the ship was unsatisfying and hard to control. To solve this, I want the ship to always be moving, where the player can change the direction of the ship using the arrow keys.
I thought I had found a solution, but c.bind_all won't call my function. I don't get any errors in the shell, and it will call other functions.
from tkinter import *
from random import randint
from time import sleep, time
# Window size
window_height = 500
window_width = 800
ship_speed = 10 # Sets how many pixels to move the ship when a key is pressed
min_bubble_r = 10 # Minimum size of each bubble
max_bubble_r = 30 # Maximum size of each bubble
max_bubble_speed = 4 # Maximum speed of each bubble
gap = 100 # How far across the screen to spawn the bubbles (higher is further right)
bubble_chance = 50 # The chance of a bubble being created
# Changes the direction of the ship depending on which key is pressed
def ship_direction(event):
global ship_direction # Takes the ship direction variable from outside the function
if event.keysym == "Up":
ship_direction = "Up"
elif event.keysym == "Down":
ship_direction = "Down"
elif event.keysym == "Right":
ship_direction = "Right"
elif event.keysym == "Left":
ship_direction = "Left"
# Creates a bubble and adds its info to the lists
def create_bubble():
# Sets the bubble position on the canvas
x = window_width + gap
y = randint(0, window_height)
r = randint(min_bubble_r, max_bubble_r) # Picks a random size for the bubble between the min and max
id1 = c.create_oval(x-r, y-r, x+r, y+r, outline="white") # Creates the bubble shape
# Adds the ID, radius and speed of the bubble to the lists
bubble_id.append(id1)
bubble_r.append(r)
bubble_speed.append(randint(1, max_bubble_speed))
# Moves the ship depending on its direction
ship_direction = str() # Creates an empty string for the ship's movement direction
def move_ship():
if ship_direction == "Up":
c.move(ship_part1, 0, -ship_speed)
c.move(ship_part2, 0, -ship_speed)
elif ship_direction == "Down":
c.move(ship_part1, 0, ship_speed)
c.move(ship_part2, 0, ship_speed)
elif ship_direction == "Right":
c.move(ship_part1, ship_speed, 0)
c.move(ship_part2, ship_speed, 0)
elif ship_direction == "Left":
c.move(ship_part1, -ship_speed, 0)
c.move(ship_part2, -ship_speed, 0)
# Goes through each existing bubble and moves them
def move_bubbles():
# Runs once for each existing bubble
for i in range(len(bubble_id)):
c.move(bubble_id[i], -bubble_speed[i], 0) # Moves the bubble depending on its speed
# Gets the co-ordinates of a bubble
def get_coordinates(id_number):
pos = c.coords(id_number) # Gets the co-ordinates
x = (pos[0] + pos[2])/2 # Works out the x co-ordinate of the middle of the bubble
y = (pos[1] + pos[3])/2 # Works out the y co-ordinate of the middle of the bubble
return x, y
window = Tk() # Creates a new window
window.title("Bubble Blaster") # Title in the top bar
c = Canvas(window, width=window_width, height=window_height, bg="#4269dd") # Creates a canvas that can be drawn on
c.pack()
ship_part1 = c.create_polygon(10, 10, 10, 50, 50, 10, 50, 50, fill="white") # Creates the centre part of the ship
ship_part2 = c.create_oval(3, 3, 57, 57, outline="white") # Creates the circle part of the ship
ship_r = 27 # Sets the ship's radius (for colisions
mid_x = window_width / 2 # Sets the page midway point on the X axis
mid_y = window_height / 2 # Sets the page midway point on the Y axis
c.move(ship_part1, mid_x-ship_r, mid_y-ship_r) # Moves part 1 of the ship to the centre of the page
c.move(ship_part2, mid_x-ship_r, mid_y-ship_r) # Moves part 2 of the ship to the centre of the page
c.bind_all("<Key>", ship_direction) # Runs the ship_direction function whenever a key is pressed
# Creates empty lists to store the ID, radius and speed of each bubble
bubble_id = list()
bubble_r = list()
bubble_speed = list()
# Main loop
while True:
if randint(1, bubble_chance) == 1:
create_bubble()
move_ship()
move_bubbles()
window.update() # Redraws the newly moved objects
sleep(0.01)
Your problem is simply that you have two global objects with the same name: a variable named ship_direction and a function named ship_direction. When you create the variable, it overwrites the function, so the function no longer exists.
If you rename your function from ship_direction to change_ship_direction (or any other name), and also change the binding, your code will work.
your problem was that you are trying to call a function that is not yet defined. So i did some replacing of your code but did not need to add a single character to it for it to work. here you go
from tkinter import *
from random import randint
from time import sleep, time
# Window size
window_height = 500
window_width = 800
ship_speed = 10 # Sets how many pixels to move the ship when a key is pressed
min_bubble_r = 10 # Minimum size of each bubble
max_bubble_r = 30 # Maximum size of each bubble
max_bubble_speed = 4 # Maximum speed of each bubble
gap = 100 # How far across the screen to spawn the bubbles (higher is further right)
bubble_chance = 50 # The chance of a bubble being created
window = Tk() # Creates a new window
window.title("Bubble Blaster") # Title in the top bar
c = Canvas(window, width=window_width, height=window_height, bg="#4269dd") # Creates a canvas that can be drawn on
c.pack()
ship_part1 = c.create_polygon(10, 10, 10, 50, 50, 10, 50, 50, fill="white") # Creates the centre part of the ship
ship_part2 = c.create_oval(3, 3, 57, 57, outline="white") # Creates the circle part of the ship
ship_r = 27 # Sets the ship's radius (for colisions
mid_x = window_width / 2 # Sets the page midway point on the X axis
mid_y = window_height / 2 # Sets the page midway point on the Y axis
c.move(ship_part1, mid_x-ship_r, mid_y-ship_r) # Moves part 1 of the ship to the centre of the page
c.move(ship_part2, mid_x-ship_r, mid_y-ship_r) # Moves part 2 of the ship to the centre of the page
# Changes the direction of the ship depending on which key is pressed
def ship_direction(event):
global ship_direction # Takes the ship direction variable from outside the function
if event.keysym == "Up":
ship_direction = "Up"
elif event.keysym == "Down":
ship_direction = "Down"
elif event.keysym == "Right":
ship_direction = "Right"
elif event.keysym == "Left":
ship_direction = "Left"
c.bind_all("<Key>", ship_direction) # Runs the ship_direction function whenever a key is pressed
# Creates a bubble and adds its info to the lists
def create_bubble():
# Sets the bubble position on the canvas
x = window_width + gap
y = randint(0, window_height)
r = randint(min_bubble_r, max_bubble_r) # Picks a random size for the bubble between the min and max
id1 = c.create_oval(x-r, y-r, x+r, y+r, outline="white") # Creates the bubble shape
# Adds the ID, radius and speed of the bubble to the lists
bubble_id.append(id1)
bubble_r.append(r)
bubble_speed.append(randint(1, max_bubble_speed))
# Moves the ship depending on its direction
ship_direction = str() # Creates an empty string for the ship's movement direction
def move_ship():
if ship_direction == "Up":
c.move(ship_part1, 0, -ship_speed)
c.move(ship_part2, 0, -ship_speed)
elif ship_direction == "Down":
c.move(ship_part1, 0, ship_speed)
c.move(ship_part2, 0, ship_speed)
elif ship_direction == "Right":
c.move(ship_part1, ship_speed, 0)
c.move(ship_part2, ship_speed, 0)
elif ship_direction == "Left":
c.move(ship_part1, -ship_speed, 0)
c.move(ship_part2, -ship_speed, 0)
# Goes through each existing bubble and moves them
def move_bubbles():
# Runs once for each existing bubble
for i in range(len(bubble_id)):
c.move(bubble_id[i], -bubble_speed[i], 0) # Moves the bubble depending on its speed
# Gets the co-ordinates of a bubble
def get_coordinates(id_number):
pos = c.coords(id_number) # Gets the co-ordinates
x = (pos[0] + pos[2])/2 # Works out the x co-ordinate of the middle of the bubble
y = (pos[1] + pos[3])/2 # Works out the y co-ordinate of the middle of the bubble
return x, y
# Creates empty lists to store the ID, radius and speed of each bubble
bubble_id = list()
bubble_r = list()
bubble_speed = list()
# Main loop
while True:
if randint(1, bubble_chance) == 1:
create_bubble()
move_ship()
move_bubbles()
window.update() # Redraws the newly moved objects
sleep(0.01)
What i did was moving the code that creates the window to the top so the window will be created before anything else is called, then i moved the canvas.bind_all function bellow your function that it is supposed to call
edit
Why is this answer downvoted as it provides the correct code and localises the fault in the code?
#example snake snake = [[1, 2], [1, 3], [2, 3]]
def draw():
canvas.delete('all')
for segment in snake:
y = segment[0] * 10
x = segment[1] * 10
canvas.create_rectangle(x, y, x + 10, y + 10, fill="red")
canvas.update()
I have created a simple snake game in python using tkinter but the movement slows down rapidly when the snake array contains 30+ rectangles, so i was wondering is there a better way to do the drawing of the objects so it works faster instead of constantly calling this draw function?
Another possible issue is the inefficiency of the move functions:
def move_right(event):
global left, right, up, down
if right != True:
left, right, up, down = False, True, False, False
while right == True:
if snake[0][1] >= snake[1][1]:
for x in range(len(snake) - 1, 0, -1):
snake[x] = snake[x - 1]
snake[0] = [snake[0][0], snake[0][1] + 1]
draw()
time.sleep(0.05)
This my first actual game so don't kill me :(.
You don't have to delete all rectangles from canvas. You have to remove only last rectangle and add new head rectangle.
To add new x,y in snake list you don't have to move all elements - you need only snake.insert(0, [new_x, new_y])
You can use root.after instead of while loop and sleep
Example - without checking collisions
import tkinter as tk
# === constants ===
BLOCK_SIZE = 10
TIME = 50
# === functions ===
# create all rectangles on canvas
def create_snake(canvas, snake):
snake_rect = []
for x, y in snake:
x1 = x * BLOCK_SIZE
y1 = y * BLOCK_SIZE
x2 = x1 + BLOCK_SIZE
y2 = y1 + BLOCK_SIZE
rect = canvas.create_rectangle(x1,y1,x2,y2, fill='red')
snake_rect.append(rect)
return snake_rect
# move snake - add first rectangle and remove last one
def move(canvas, snake, snake_rect, remove_last=True):
# get head
x, y = snake[0]
# new head position
if direction == 'up':
y = y-1
elif direction == 'down':
y = y+1
elif direction == 'left':
x = x-1
elif direction == 'right':
x = x+1
# add first - new head
snake.insert(0, [x, y])
x1 = x * BLOCK_SIZE
y1 = y * BLOCK_SIZE
x2 = x1 + BLOCK_SIZE
y2 = y1 + BLOCK_SIZE
rect = canvas.create_rectangle(x1,y1,x2,y2, fill='red')
snake_rect.insert(0, rect)
# remove last - tail (if snake doesn't eat 'apple')
if remove_last:
del snake[-1]
canvas.delete(snake_rect[-1])
del snake_rect[-1]
# call `move` function again after TIME miliseconds
root.after(TIME, move, canvas, snake, snake_rect)
# change direction
def change_direction(new_direction):
global direction
#print(new_direction)
if new_direction == 'left':
if direction != 'right':
direction = new_direction
elif new_direction == 'right':
if direction != 'left':
direction = new_direction
elif new_direction == 'up':
if direction != 'down':
direction = new_direction
elif new_direction == 'down':
if direction != 'up':
direction = new_direction
# === main ===
direction = 'up'
# ---
root = tk.Tk()
canvas = tk.Canvas(root)
canvas.pack()
# create long (curved) snake
snake = [[x,25] for x in range(10,35)] + [[35, y] for y in range(25, 1, -1)] + [[x, 1] for x in range(35, 1, -1)]
snake_rect = create_snake(canvas, snake)
# call `move` function after TIME miliseconds
root.after(TIME, move, canvas, snake, snake_rect)
# bind arrows to change snake direction
root.bind('<Left>', lambda event:change_direction('left'))
root.bind('<Right>', lambda event:change_direction('right'))
root.bind('<Up>', lambda event:change_direction('up'))
root.bind('<Down>', lambda event:change_direction('down'))
# start program
root.mainloop()
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()
I need to draw tens of thousands, in the future probably hundreds of thousands of simple 2d objects (circles, rectangles, some filled, labeled...) to a widget.
In the same program I need GUI widgets (buttons, text input, checkboxes).
I tried Gtk, Qt and SDL with C++ and Python. First result was to be expected: C++ and Python show the same performance, as they call the same C or C++ routines in the backend.
Second result is that none of the libraries made a big difference. In numbers: 22500 rectangles (150*150) needed approximately a second to update. As there will be constant upadating due to (a) new data, i.e. more rectangles, and (b) user interaction, i.e. zooming, panning etc., a second is way to long!
What would be a faster way. Small examples are very much appreciated. Python and C++ is good. Other libraries should be easily accessible and installable on Linux.
Maybe I am just doing it wrong.
ps. I am not posting my test codes, because I don't want to bias answers. And I don't want my code to be corrected, I want to the fastest way to do it...
edit:
Alright, I will add my gtk test:
#!/bin/env python2
import gtk
import gobject
import gtk.gdk
class GraphWidget(gtk.DrawingArea):
__gsignals__ = {
'expose-event': 'override',
'clicked' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_STRING, gtk.gdk.Event))
}
def __init__(self,window):
gtk.DrawingArea.__init__(self)
#self.win = window
self.zoom_ratio = 1.0
self.dx = 40
self.dy = 40
def do_expose_event(self, event):
cr = self.window.cairo_create()
cr.set_source_rgba(1.0, 0.9, 0.8, 1.0)
cr.paint()
cr.translate(self.dx, self.dy)
cr.scale(self.zoom_ratio,self.zoom_ratio)
self.draw(cr)
def draw(self, cr):
n = 150
cr.set_source_rgba(0.,1.,1.,1.0)
for i in range(n):
for j in range(n):
cr.arc(i*30, j*30, 10, 0, 6.2832)
cr.close_path()
cr.fill()
cr.set_source_rgba(0.,0.,1.,1.0)
for i in range(n):
for j in range(n):
cr.arc(i*30, j*30, 10, 0, 6.2832)
cr.move_to(i*30-10, j*30)
cr.show_text("hu")
cr.stroke()
def on_zoom(self, zoom_factor):
self.zoom_ratio *= zoom_factor
self.queue_draw()
def on_translate(self,dx,dy):
self.dx += dx
self.dy += dy
self.queue_draw()
class TestWindow(gtk.Window):
def __init__(self):
gtk.Window.__init__(self)
self.widget = GraphWidget(self)
self.add(self.widget)
self.show_all()
# connect key press events
self.connect('key-press-event', self.on_key_press_event)
self.connect('destroy', gtk.main_quit)
self.widget.queue_draw()
def on_key_press_event(self, widget, event):
if event.keyval == gtk.keysyms.space and not (event.state & gtk.gdk.CONTROL_MASK):
self.on_run(widget)
return True
elif event.keyval == gtk.keysyms.r:
self.on_refresh(widget)
return True
elif event.keyval == gtk.keysyms.Left:
self.widget.on_translate(-100, 0)
elif event.keyval == gtk.keysyms.Right:
self.widget.on_translate(100, 0)
elif event.keyval == gtk.keysyms.Up:
self.widget.on_translate(0, -100)
elif event.keyval == gtk.keysyms.Down:
self.widget.on_translate(0, 100)
elif event.keyval == gtk.keysyms.Page_Down:
self.widget.on_zoom(0.7)
elif event.keyval == gtk.keysyms.Page_Up:
self.widget.on_zoom(1.3)
if __name__ == '__main__':
win = TestWindow()
gtk.main()
And the SDL experiment:
#!/usr/bin/env python
import sdl2
import sdl2.ext as sdl2ext
dx = 0
dy = 0
zoom_factor = 1.
n_objects = 150
sdl2ext.init()
window = sdl2ext.Window('hallo',
size=(800, 600),
flags= sdl2.SDL_WINDOW_RESIZABLE)
window.show()
renderer = sdl2ext.RenderContext(window)
renderer.color = sdl2ext.Color(255,155,25)
def draw():
renderer.clear()
for i in xrange(n_objects):
for j in xrange(n_objects):
renderer.fill([int((i*30+dx)*zoom_factor),
int((j*30+dy)*zoom_factor),
int(20*zoom_factor),
int(20*zoom_factor)],
sdl2ext.Color(255,25,55))
renderer.draw_rect([int((i*30+dx)*zoom_factor),
int((j*30+dy)*zoom_factor),
int(20*zoom_factor),
int(20*zoom_factor)],
sdl2ext.Color(255,255,255))
renderer.present()
draw()
running = True
while running:
for e in sdl2ext.get_events():
if e.type == sdl2.SDL_QUIT:
running = False
break
if e.type == sdl2.SDL_KEYDOWN:
if e.key.keysym.sym == sdl2.SDLK_ESCAPE:
running = False
break
elif e.key.keysym.sym == sdl2.SDLK_RIGHT:
dx += 50
draw()
elif e.key.keysym.sym == sdl2.SDLK_LEFT:
dx -= 50
draw()
elif e.key.keysym.sym == sdl2.SDLK_UP:
dy += 50
draw()
elif e.key.keysym.sym == sdl2.SDLK_DOWN:
dy -= 50
draw()
elif e.key.keysym.sym == sdl2.SDLK_PAGEUP:
zoom_factor *= 1.2
draw()
elif e.key.keysym.sym == sdl2.SDLK_PAGEDOWN:
zoom_factor /= 1.2
draw()
The Qt test was done by my colleague so I don't have the code right now...
Looks like you're drawing every single object regardless of whether or not it's in the viewable area. Have you considered storing an array of the positions of each object, and then test each to determine if it's in the screen's viewable area before drawing it?
I did a quick and dirty test to try it out (the code could be much cleaner, I'm sure), and it's much more responsive drawing only what's visible:
First I created some variables to determine the visible boundary (you would update the boundaries every time move and zoom, window resize, etc happen):
boundX = [-60, 160]
boundY = [-60, 160]
In the draw event, I'm checking if the position is within the boundry before drawing. I also consolidated your loop to increase efficiency, you can draw and add text in the same iteration. Even testing this with n = 50000 is more responsive than what was there before.
def draw(self, cr):
n = 150
for i in range(n):
if min(boundX) < i * 30 < max(boundX):
for j in range(n):
if min(boundY) < j * 30 < max(boundY):
cr.set_source_rgba(0.,1.,1.,1.0)
cr.arc(i*30, j*30, 10, 0, 6.2832)
cr.close_path()
cr.fill()
cr.set_source_rgba(0.,0.,1.,1.0)
cr.arc(i*30, j*30, 10, 0, 6.2832)
cr.move_to(i*30-10, j*30)
cr.show_text("hu")
cr.stroke()
And in the keypress events, just increment and decrement the boundry by the amount the widget is being translated:
def on_key_press_event(self, widget, event):
if event.keyval == gtk.keysyms.space and not (event.state & gtk.gdk.CONTROL_MASK):
self.on_run(widget)
return True
elif event.keyval == gtk.keysyms.r:
self.on_refresh(widget)
return True
elif event.keyval == gtk.keysyms.Left:
boundX[0] += 100
boundX[1] += 100
self.widget.on_translate(-100, 0)
elif event.keyval == gtk.keysyms.Right:
boundX[0] -= 100
boundX[1] -= 100
self.widget.on_translate(100, 0)
elif event.keyval == gtk.keysyms.Up:
boundY[0] += 100
boundY[1] += 100
self.widget.on_translate(0, -100)
elif event.keyval == gtk.keysyms.Down:
boundY[0] -= 100
boundY[1] -= 100
self.widget.on_translate(0, 100)
elif event.keyval == gtk.keysyms.Page_Down:
self.widget.on_zoom(0.7)
elif event.keyval == gtk.keysyms.Page_Up:
self.widget.on_zoom(1.3)