Tkinter process not shown during run - python

I am running a creature simulator in python 2.7 using tkinter as my visualizer. The map is made up of squares, where colors represent land types, and a red square represents the creature. I use canvas.move, to move that red square around the board. It has to move quite a lot. But I know exactly where it should start and where it should end. I have run the simulation, bit by bit, and when it is regulated to maybe two moves ie. the sim isn't really running I'm just testing it. I can see the movements. But when I really run the sim, everything buzzes by and all I can see of the canvas is the map, but no creature and certainly no creature movement. So my question is this. Firstly, how can I possibly slow down the process so that I can see the movements? Or why would the simulation run and now show any of the tkinter?
The simulation is quite large and it would be hard to pick out just the important bits, so the code below is more of a simplification. But it matches how I did the tkinter stuff. My sim just added more calculations and loops. It's worth noting that this example works perfectly.
Driver.py:
from Tkinter import *
import animation
class Alien(object):
def __init__(self):
#Set up canvas
self.root = Tk()
self.canvas = Canvas(self.root, width=400, height=400)
self.canvas.pack()
#Vars
self.map = [[1, 0, 0, 1, 0], [0, 1, 0, 1, 0], [0, 0, 1, 0, 0], [0, 1, 1, 0, 0], [1, 0, 0, 1, 0]]
self.x = 0
self.y = 0
r = 50
self.land = {}
#Draw Init
for i, row in enumerate(self.map):
for j, cell in enumerate(row):
color = "black" if cell else "green"
self.canvas.create_rectangle(r * i, r * j, r * (i + 1), r * (j + 1),
outline=color, fill=color)
self.land[(i, j)] = self.canvas.create_text(r * i, r * j, anchor=NE, fill="white", text="1", tag=str((i, j)))
self.creature = self.canvas.create_rectangle(r * self.x, r * self.y, r * (self.x + 1), r * (self.y + 1),
outline="red", fill="red")
self.canvas.pack(fill=BOTH, expand=1)
#Action
movement = animation.Animation(self.root, self.canvas, self.creature, self.land)
self.root.after(0, movement.animate)
#Clost TK
self.root.mainloop()
a = Alien()
animation.py:
from random import randrange
import sys
class Animation():
def __init__(self, root, canvas, creature, land):
self.x = self.y = 0
self.ctr = 10
self.canvas = canvas
self.creature = creature
self.root = root
self.land = land
#self.root.after(250, self.animate)
self.canvas.move(self.creature, 2 * 50, 2 * 50)
def animate(self):
self.ctr -= 1
if self.ctr > 0:
for i in range(2):
i = randrange(1, 5)
if i == 1:
self.y = -1
elif i == 2:
self.y = 1
elif i == 3:
self.x = -1
elif i == 4:
self.x = 1
#root.after(250, self.animate(canvas, creature))
"""Moves creature around canvas"""
self.movement()
self.root.after(250, self.animate)
def movement(self):
self.canvas.move(self.creature, self.x * 50, self.y * 50)

you can slow down the process using tkinters after method which can be used by any difrent widget.
canvas.after(time, dosomething) #the first parameter is how many milliseconds it will wait before it will call the second parameter.
where your dosomething function should be your way of updating the your cnavas.
http://infohost.nmt.edu/tcc/help/pubs/tkinter/web/universal.html

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()

How to display and update a score on screen with Tkinter? [duplicate]

My question is about displaying and updating text, in order to display the score on screen.
I would like to create a score like the real game that would appear on the screen. But after researching Google, I have not found anyone wishing to increase a score on the screen ...
Indeed, I would like the score to increase each time the bird passes between the pipes and therefore whenever the pipes have an X of 67 pixels. So does anyone know how to do this?
from tkinter import *
import random
from random import randint
def sauter(event):
canvas.move(image_oiseau, 0, -10*DY)
def deplacement():
global tuyx,tuyx2,h,H,oisx,oisy,solx,sol2x
x0, y0, x1, y1 = canvas.bbox(image_oiseau)
if y1 < 416:
canvas.move(image_oiseau, 0, DY)
canvas.coords(image_sol,solx,512)
if solx >= -144:
solx=solx-5
else:
solx=144
canvas.coords(image_sol2,sol2x,512)
if sol2x >= 144:
sol2x=sol2x-5
else:
sol2x=432
canvas.coords(image_tuyau_haut,tuyx,h)
canvas.coords(image_tuyau_bas,tuyx,h-241)
if tuyx>=-28:
tuyx=tuyx-5
else:
tuyx=316
h=randint(256,505)
canvas.coords(image_tuyau_haut2,tuyx2,H)
canvas.coords(image_tuyau_bas2,tuyx2,H-241)
if tuyx2>=-28:
tuyx2=tuyx2-5
else:
tuyx2=316
H=randint(256,505)
canvas.after(40,deplacement)
LARGEUR = 286
HAUTEUR = 510
DY = 5
tuyx=316
tuyx2=488
h=randint(256,505)
H=randint(256,505)
oisx=67
oisy=244
solx=144
sol2x=432
fenetre = Tk()
canvas = Canvas(fenetre, width=LARGEUR, height=HAUTEUR)
fond = PhotoImage(file="background-day.png")
fond2 = PhotoImage(file="background-night.png")
fond=[fond,fond2]
F= random.choice(fond)
canvas.create_image(144,256, anchor=CENTER,image=F)
tuyau_haut = PhotoImage(file="tuyau_vers_le_haut.png")
image_tuyau_haut = canvas.create_image(tuyx,h,anchor=CENTER,image=tuyau_haut)
image_tuyau_haut2 = canvas.create_image(tuyx2,H,anchor=CENTER,image=tuyau_haut)
tuyau_bas = PhotoImage(file="tuyau_vers_le_bas.png")
image_tuyau_bas = canvas.create_image(tuyx,h,anchor=CENTER,image=tuyau_bas)
image_tuyau_bas2 = canvas.create_image(tuyx2,H,anchor=CENTER,image=tuyau_bas)
sol = PhotoImage(file="sol-day.png")
image_sol = canvas.create_image(144,512, anchor=S,image=sol)
image_sol2 = canvas.create_image(432,512, anchor=S,image=sol)
oiseau = PhotoImage(file="yellowbird-midflap.png")
oiseau2 = PhotoImage(file="bluebird-midflap.png")
oiseau3 = PhotoImage(file="redbird-midflap.png")
oiseau=[oiseau,oiseau2,oiseau3]
O=random.choice(oiseau)
image_oiseau=canvas.create_image(oisx,oisy, anchor=W,image=O)
deplacement()
canvas.pack()
canvas.focus_set()
canvas.bind("<space>",sauter)
fenetre.mainloop()
Could someone explain the problem to me because I thought it would be easy :(
Here are the pictures of the game :)
Here are the pictures of the game
Here is one approach to display the scores: It uses a tk.Label, that is updated at the same time the score increases.
The trigger that increases the score is currently a random call to on_change; you can modify this to be a test if a pipe x coordinates becomes lower than the bird x coordinates (the bird successfully crossed the obstacle)
You can, if you want relocate the score label on the canvas.
import random
import tkinter as tk
WIDTH, HEIGHT = 500, 500
def create_pipes():
pipes = []
for x in range(0, WIDTH, 40):
y1 = random.randrange(50, HEIGHT - 50)
y0 = y1 + 50
pipes.append(canvas.create_line(x, 0, x, y1))
pipes.append(canvas.create_line(x, y0, x, HEIGHT))
return pipes
def move_pipes():
for pipe in pipes:
canvas.move(pipe, -2, 0)
x, y0, _, y1 = canvas.coords(pipe)
if x < 0:
canvas.coords(pipe, WIDTH+20, y0, WIDTH+20, y1)
if random.randrange(0, 20) == 10:
on_change()
root.after(40, move_pipes)
def on_change():
global score
score += 1
score_variable.set(f'score: {score}')
root = tk.Tk()
tk.Button(root, text='start', command=move_pipes).pack()
score = 0
score_variable = tk.StringVar(root, f'score: {score}')
score_lbl = tk.Label(root, textvariable=score_variable)
score_lbl.pack()
canvas = tk.Canvas(root, width=WIDTH, height=HEIGHT, bg="cyan")
canvas.pack()
pipes = create_pipes()
root.mainloop()

tkinter not letting 2 threads run at once

I have a game and I have the world moving towards you, and you can jump at the same time. both functions work separately, but they don't work when used together. sometimes it gives me an error(ValueError: could not convert string to float: expected) couldnt convert, sometimes the character does a weird jittery motion, sometimes it does both, and sometimes it just crashes. this is my code
import Tkinter as tkinter
import thread
from time import sleep
def is_even(n):
if n % 2 == 0:
retVal = True
else:
retVal = False
return retVal
class gameScreen:
def move(self, event):
d = self.getdir(event.keysym)
self.canvas.move(self.char, d[0], d[1])
self.canvas.update()
if self.collision():
self.canvas.move(self.char, -d[0], -d[1])
def fall(self):
speed = .01
x = 0
while not self.collision():
self.canvas.move(self.char, 0, 1)
self.canvas.update()
sleep(speed)
if x == 2:
speed -= speed *.04
x = 0
else:
x += 1
self.canvas.move(self.char, 0, -1)
def jump(self):
j = 0
from time import sleep
jump = [6, 6, 6, 6, 4, 4, 2, 1, 0]
for i in range(0, (len(jump)*2)):
x = jump[j]
self.canvas.move(self.char, 0, -x)
if not is_even(i):
j +=1
self.canvas.update()
sleep(.05)
self.fall()
def getLast(self, lvl):
x = 0
for i in lvl:
coords = self.canvas.coords(i)
if x < coords[3]:
x = coords[3]
last = i
return last
def gameThread(self, event):
thread.start_new_thread(self.gameStart, ())
def gameStart(self, event=None):
from time import sleep
last = self.getLast(self.lvl1)
coords = self.canvas.coords(last)
while coords[2] > 0:
print coords[2]
for i in self.lvl1:
self.canvas.move(i, -1, 0)
coords = self.canvas.coords(last)
self.canvas.update()
sleep(.002)
if not self.touchingGround(event):
self.fall()
def touchingGround(self, event = None):
self.canvas.move(self.char, 0, 5)
if self.collision():
retVal = True
else:
retVal = False
self.canvas.move(self.char, 0, -5)
return retVal
def onKey(self, event):
if self.touchingGround(event):
thread.start_new_thread(self.jump, ())
def collision(self):
coords = self.canvas.coords(self.char)
collision = len(self.canvas.find_overlapping(coords[0]-1,
coords[1]-1,
coords[2]-1,
coords[3]-1)) >=2
return collision
def getdir(self, s):
direction = {"Left":[-5, 0], "Right":[5, 0]}
try:
retVal = direction[s]
except KeyError:
retVal =False
return retVal
def __init__(self, master):
self.lvl1 = []
self.objects = []
self.master = master
master.attributes("-fullscreen", True)
master.title("Game")
self.width=master.winfo_screenwidth()
self.height=master.winfo_screenheight()
self.canvas = tkinter.Canvas(master, width=self.width,
height=self.height)
self.canvas.pack(expand="YES",fill="both")
self.objects.append(self.canvas.create_rectangle(0, self.height,
self.width,
self.height-100,
fill="grey"))
self.lvl1.append(self.canvas.create_rectangle(self.width,
self.height-100,
self.width + 100,
self.height-150,
fill="grey"))
self.objects.extend(self.lvl1)
self.char = self.canvas.create_rectangle((self.width/2)-25,
self.height- 100,
(self.width/2)+25,
self.height-150,
fill="red")
self.x = 0
self.y = 0
master.bind("<Key-Up>", self.onKey)
master.bind("<Return>", self.onKey)
def destroy(event):
root.destroy()
root = tkinter.Tk()
my_gui = gameScreen(root)
root.bind("<Escape>", destroy)
root.mainloop()
i tried adding the afters like tadhg said, but it still only jumps sometimes, and most of the time it dosent do a full jump, or just goes up one or two pixels, then goes back down, then repeats for the amount of time it should be jumping.

Doing traces behind balls with Tkinter

So i've this code who create moving balls :
from Tkinter import *
from random import randrange
from threading import Thread
Matrice = (600*400)*[0]
class Ball(Frame):
def __init__(self, can, posx, posy, name):
self.can = can
self.largeur_can = int(self.can.cget("width"))
self.hauteur_can = int(self.can.cget("height"))
self.posx = posx
self.posy = posy
self.name = name
self.ball1 = self.can.create_oval(self.posy, self.posx, self.posy+10, self.posx+10, outline="red", fill=self.name, width=2)
self.nx = randrange(-10,10,1)
self.nx /= 2.0
self.ny = randrange(-10,10,1)
self.ny /= 2.0
self.move()
def move(self):
global Matrice
self.pos_ball = self.can.coords(self.ball1)
self.posx_ball = self.pos_ball[0]
self.posy_ball = self.pos_ball[1]
if self.posx_ball < 0 or (self.posx_ball + 10) > self.largeur_can:
self.nx = -self.nx
if self.posy_ball < 0 or (self.posy_ball + 10) > self.hauteur_can:
self.ny = -self.ny
self.can.move(self.ball1, self.nx, self.ny)
Matrice[int(self.posy_ball)*600 + int(self.posx_ball)] += 100
self.can.after(10, self.move)
root=Tk()
can=Canvas(root,width=600,height=400,bg="black")
for x in range(10):
x=Ball(can,100,400, "blue")
x=Ball(can,100,400, "green")
can.pack()
root.mainloop()
And i would create traces behind balls, i created a matrix Matrice where I recorded where each ball is passed and now i want to show it un the background but i don't know how.
Nota : values in the matrix could decrease or be changed somewhere other than in move.
So anyone have an idea how i could do that ?
This is an inefficient way of doing it, but it's the simplest. Every time you move a ball just draw a line from where it used to be to where it currently is.
self.can.move(self.ball1, self.nx, self.ny)
new_pos = self.can.coords(self.ball1)
self.can.create_line(self.posx_ball, self.posy_ball, new_pos[0], new_pos[1], fill='red')
self.can.after(10, self.move)
Note that this line will follow the top left of the sprite - you can adjust the coordinates if you want it to follow the middle of the sprite.

Python - Adding a Tkinter Graph to a PyQt Widget

We currently have a fully functional Gui written created using PyQt. My partner wrote a function that graphs a dataSet in Tkinter. My question is, how do we combine the two so they work together?
Here is the graphing function:
def createGraph(self):
import tkinter as tk
# Send in data as param, OR
#data = [17, 20, 15, 10, 7, 5, 4, 3, 2, 1, 1, 0]
# Recieve data within function
s.send("loadgraph")
inputString = repr(s.recv(MSGSIZE))
#inputString = "-20 15 10 7 5 -4 3 2 1 1 0"
print(inputString)
data = [int(x) for x in inputString.split()]
root = tk.Tk()
root.title("FSPwners")
screen_width = 400
screen_height = 700
screen = tk.Canvas(root, width=screen_width, height=screen_height, bg= 'white')
screen.pack()
# highest y = max_data_value * y_stretch
y_stretch = 15
# gap between lower canvas edge and x axis
y_gap = 350
# stretch enough to get all data items in
x_stretch = 10
x_width = 20
# gap between left canvas edge and y axis
x_gap = 20
for x, y in enumerate(data):
# calculate reactangle coordinates (integers) for each bar
x0 = x * x_stretch + x * x_width + x_gap
y0 = screen_height - (y * y_stretch + y_gap)
x1 = x * x_stretch + x * x_width + x_width + x_gap
y1 = screen_height - y_gap
# draw the bar
print(x0, y0, x1, y1)
if y < 0:
screen.create_rectangle(x0, y0, x1, y1, fill="red")
else:
screen.create_rectangle(x0, y0, x1, y1, fill="green")
# put the y value above each bar
screen.create_text(x0+2, y0, anchor=tk.SW, text=str(y))
root.mainloop()
When that method is run by itself, it creates a popup box with the graph. Now we want it to create a popup graph when a button is pressed in our current gui. How can we get it to work? If we just call createGraph() when a button is clicked in our GUI, we get the error:
unrecognized selector sent to instance x009x...
What is the problem? Thanks!
Here's a PyQt port:
from PyQt4 import QtCore, QtGui
class Graph(QtGui.QWidget):
def __init__(self, data, parent=None):
QtGui.QWidget.__init__(self, parent)
self._data = data
self.resize(400, 700)
self.setWindowTitle('FSPwners')
self.setAutoFillBackground(True)
self.setBackgroundRole(QtGui.QPalette.Base)
def paintEvent(self, event):
painter = QtGui.QPainter()
painter.begin(self)
screen_width = self.width()
screen_height = self.height()
# highest y = max_data_value * y_stretch
y_stretch = 15
# gap between lower canvas edge and x axis
y_gap = 350
# stretch enough to get all data items in
x_stretch = 10
x_width = 20
# gap between left canvas edge and y axis
x_gap = 20
for x, y in enumerate(self._data):
# calculate reactangle coordinates (integers) for each bar
x0 = x * x_stretch + x * x_width + x_gap
y0 = screen_height - (y * y_stretch + y_gap)
x1 = x0 + x_width
y1 = screen_height - y_gap
if y < 0:
painter.setBrush(QtCore.Qt.red)
else:
painter.setBrush(QtCore.Qt.green)
painter.drawRect(QtCore.QRectF(
QtCore.QPointF(x0, y0), QtCore.QPointF(x1, y1)))
print (x0, y0, x1, y1)
# put the y value above each bar
painter.drawText(x0 + 2, y0 - 2, str(y))
painter.end()
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
# data to be graphed
data = [-20, 15, 10, 7, 5, -4, 3, 2, 1, 1, 0]
window = Graph(data)
window.show()
sys.exit(app.exec_())
Qt and Tkinter don't play along quite well, as you can perceive -
I had once played along Python graphical toolkits, and wrote
a 4 operation calculator that would run in either Qt, GTK or Tkinter -
or even display all at once.
In order to have both the Tkinter and Qt versions working simultaneously,
I had to fork the process - and start each toolkit in a separate
running instance;
Your case is not identical, as the Qt GUI will be already running, but maybe
having this to start up with you can come along with a work-around.
The 3-calculators code listing can be found here:
https://web.archive.org/web/20101122232402/http://www.python.org.br/wiki/CalculadoraTkGtkQt

Categories

Resources