Not drawing Tkinter canvas - python

I have some code which I am trying to run, basically the system should draw the tkinter canvas then constantly monitor the headset results - currently it seems to only run the code once rather than (as I expected) loop through the if statement.
Any suggestions would be most appreciated.
A
import Tkinter as tk
import serial
import mindwave
import time
headset = mindwave.Headset('/dev/ttyUSB0', 'AF66')
while headset.status != 'connected':
time.sleep(0.5)
if headset.status == 'standby':
headset.connect()
print ("Retrying connect...")
print(headset.status)
arduino = serial.Serial('/dev/ttyACM0', 9600)
count = 0
value = '0'
points = [
[250,250,350,250,350,350,250,350],
[300,100,500,500,100,500],
[100,100,100,500,500,300],
[100,100,500,100,300,500],
[100,300,500,100,500,500],
]
root = tk.Tk()
# set window to middle of screen
screen_width = root.winfo_screenwidth()
screen_height = root.winfo_screenheight()
xcoord = screen_width//2 - 300
ycoord = screen_height//2 - 300
root.geometry("%dx%d+%d+%d" % (600,600,xcoord,ycoord))
#set up canvas
canvas1 = tk.Canvas(root, relief=tk.FLAT, background="blue")
canvas1.pack(fill=tk.BOTH, expand=1)
# create the polygon with tag "button"
canvas1.create_polygon(points[count], fill="darkgreen", outline="yellow", tag="button")
#monitor attention results
if headset.attention > 70:
print("Attention: %s, Meditation: %s" % (headset.attention, headset.meditation))
else:
print("attention too low")
time.sleep(.2)

Related

Tkinter making image variable into a class

I was making a game recently using tkinter and it's Canvas widget, but ran into a problem when trying to move an image. I set the canvas.create_image to a variable called character but noticed it turned it into an object. This causes issues as when I try and use the canvas.move(character), it comes back with "character not defined". Here is the full code:
from tkinter import *
from PIL import ImageTk, Image
import pygame
import threading
import time
gamestate= 'title'
waiting = False
#window init
root = Tk()
WIDTH=1280
HEIGHT=720
canvas = Canvas(root, width=WIDTH, height=HEIGHT, bg='black')
canvas.pack(expand=1, fill=BOTH)
logo = PhotoImage(file='Ghost clipart.png')
root.iconphoto(False, logo)
#setting up all the images
title_image = PhotoImage(file='title.png')
board_image = Image.open('board.png')
done_board = board_image.resize((1920,1080), Image.ANTIALIAS)
board_image = ImageTk.PhotoImage(done_board)
character_image = ImageTk.PhotoImage(file='first_run_left.png')
#display the title screen
def title_screen():
title = canvas.create_image(WIDTH/2,HEIGHT/2, anchor=CENTER, image=title_image)
#define the events after clicking play
def play():
canvas.delete("all")
board = canvas.create_image(WIDTH/2 + 100,HEIGHT/2 - 360, anchor=CENTER, image=board_image)
character = canvas.create_image(556,304,anchor=CENTER,image=character_image)
#testing for what should happen the the mouse button is clicked
def click(event):
if gamestate == 'title':
print(event.x, event.y)
if event.x > 475 and event.x < 804 and event.y > 213 and event.y < 337:
play()
elif event.x > 475 and event.x < 804 and event.y > 404 and event.y < 527:
print("skin")
def up(event):
canvas.move(character,0,10)
root.bind('<Button-1>', click)
root.bind('<Up>', up)
#audio module
def test_loop():
while True:
pygame.mixer.init()
pygame.mixer.music.load("nokia-ringtone-arabic.mp3")
pygame.mixer.music.play()
time.sleep(23)
thread = threading.Thread(target=test_loop)
thread.daemon = True
thread.start()
title_screen()
root.mainloop()
Here is a simple script that uses the arrow keys to move an image on the canvas. Note that the variable img (in your case character) must be accessible from the functions that act upon it, therefore its scope must be above these functions.
You can use this as a starting point to solve your problem.
image used:
import tkinter as tk
def up(event):
canvas.move(img, 0, -10)
def down(event):
canvas.move(img, 0, 10)
def left(event):
canvas.move(img, -10, 0)
def right(event):
canvas.move(img, 10, 0)
width = 500
height = 500
window = tk.Tk()
window.title("Moving image")
canvas = tk.Canvas(window, width=500, height=500)
canvas.pack()
my_image = tk.PhotoImage(file="pix.png")
img = canvas.create_image(260, 125, anchor=tk.NW, image=my_image)
window.bind("<Up>", up)
window.bind("<Down>", down)
window.bind("<Left>", left)
window.bind("<Right>", right)
window.mainloop()
Since character is a local variable inside play() function, it cannot be accessed inside up() function.
However you can use tags option of create_image():
def play():
...
canvas.create_image(556,304,anchor=CENTER,image=character_image, tags='character')
Then you can refer to that image item using the same tag:
def up(event):
canvas.move('character',0,-10) # use tag 'character'
create_image doesn't turn anything into an object. It simply returns an integer identifier. The original object is unchanged. character is undefined because it's a local variable and thus only visible in the method that created it.

Dynamically updating polygon shape in tkinter window

I'm writing a program for my raspberry pi4 for my Master's project which sounded so simple at first but for some reason Python is causing me grief.
In short - I have created a tkinter canvas with a polygon centred on the canvas. The shape of this polygon will depend upon the value of a counter.
The count is controlled by a blink event from a neurosky mindwave headset - this count is working (mostly).
What I want to then do is update the canvas to put the new points for the polygon into the pack but nothing I have tried seems to work. The closest I got was trying a .redraw() command which drew an infinite number of windows before I pulled the plug.
I am not a complete novice to coding having taught many languages in my time but have never used python before and am clearly missing a very simple step which will cause everything to fall out.
I will try to modify the code to use a keyboard press rather than a headset and add it below later if folk think it will help.
import keyboard
import time
from tkinter import *
count = 0
points = [250,250,350,250,350,350,250,350]
root = Tk()
while True:
# set window to middle of screen
screen_width = root.winfo_screenwidth()
screen_height = root.winfo_screenheight()
xcoord = screen_width/2-300
ycoord = screen_height/2 - 300
root.geometry("%dx%d+%d+%d" % (600,600,xcoord,ycoord))
#set up canvas size and background colour
canvas1 = Canvas(root, relief = FLAT,width = 600, height = 600, background = "blue")
#set up buttons shape and colour
button = canvas1.create_polygon(points, fill="darkgreen", outline="yellow")
canvas1.pack()
if keyboard.is_pressed("f"):
if count < 4:
count += 1
elif count == 4:
count = 0
time.sleep(0.1)
if count == 0:
points = [250,250,350,250,350,350,250,350]
elif count == 1:
points = [300,100,500,500,100,500]
elif count == 2:
points = [200,100,400,100,300,500]
elif count == 3:
points = [100,300,500,100,500,500]
elif count == 4:
points = [100,100,100,500,500,300]
print(count)
root.update()
You need to delete the old polygon and create new one. Also don't use while loop in tkinter application. For your case, you can bind a callback on <Key> event and update the polygon in the callback:
import tkinter as tk
count = 0
points = [
[250,250,350,250,350,350,250,350],
[300,100,500,500,100,500],
[200,100,400,100,300,500],
[100,300,500,100,500,500],
[100,100,100,500,500,300],
]
root = tk.Tk()
# set window to middle of screen
screen_width = root.winfo_screenwidth()
screen_height = root.winfo_screenheight()
xcoord = screen_width//2 - 300
ycoord = screen_height//2 - 300
root.geometry("%dx%d+%d+%d" % (600,600,xcoord,ycoord))
#set up canvas
canvas1 = tk.Canvas(root, relief=tk.FLAT, background="blue")
canvas1.pack(fill=tk.BOTH, expand=1)
# create the polygon with tag "button"
canvas1.create_polygon(points[count], fill="darkgreen", outline="yellow", tag="button")
def on_key(event):
global count
if event.char == 'f':
count = (count + 1) % len(points)
print(count)
canvas1.delete("button") # delete the old polygon
canvas1.create_polygon(points[count], fill="darkgreen", outline="yellow", tag="button")
root.bind("<Key>", on_key)
root.mainloop()
You can update any parameters of a shape.
canvas.itemconfigure(shape1_id_or_tag, fill="green")
canvas.itemconfigure(shape2_id_or_tag, fill="#900", outline="red", width=3)
But for your situation try '.coords()':
canvas1.coords("Button", points[count])
Source: https://anzeljg.github.io/rin2/book2/2405/docs/tkinter/canvas-methods.html

Tkinker circumference of a circle coords handling python 3

I'm making a little alien project to help to learn graphics in tkinter and I have come across a problem. I am trying to make the aliens eyeball stay inside the eye but still move around however that requires me to detect the edge of the eyeball which is a circle. Not really sure how coords work in tkinter (other than the basics) so any help appreciated. Thanks!
from tkinter import *
from threading import Timer
import random
import time
global canvas, root
root = Tk()
root.title("Alien")
root.attributes("-topmost", 1)
canvas = Canvas(root, height=300, width =400)
canvas.pack()
Blinking = False
class Alien:
def __init__(self):
global canvas, root
self.body = canvas.create_oval(100,150,300,250, fill = "green")
self.eye = canvas.create_oval(170,70,230,130, fill = "white")
self.eyeball = canvas.create_oval(190,90,210,110, fill = "black")
self.mouth = canvas.create_oval(150,220,250,240, fill = "red")
self.neck = canvas.create_line(200,150,200,130)
self.hat = canvas.create_polygon(180,75,220,75,200,20, fill = "blue")
self.words = canvas.create_text(200,800, text = "I'm an alien!", anchor="nw")
root.update()
def openMouth(self):
global canvas, root
canvas.itemconfig(self.mouth, fill = "black")
root.update()
def closeMouth(self):
global canvas, root
canvas.itemconfig(self.mouth, fill = "red")
root.update()
def burp(self, event):
self.openMouth()
canvas.itemconfig(self.words, text = "BURRRRP!")
time.sleep(0.5)
self.closeMouth()
def moveEye(self,event):
global root, canvas
canvas.move(alien.eyeball , random.randint(-1,1) , random.randint(-1,1))
root.update()
def blink(self,event):
canvas.itemconfig(self.eye, fill = "green")
canvas.itemconfig(self.eyeball, state=HIDDEN)
Blinking = True
root.update()
def unblink(self,event):
canvas.itemconfig(self.eye, fill = "white")
canvas.itemconfig(self.eyeball, state=NORMAL)
Blinking = False
root.update()
alien = Alien()
alien.openMouth()
time.sleep(1)
alien.closeMouth()
canvas.bind_all("<Button-1>", alien.burp)
canvas.bind_all("<KeyPress-a>", alien.blink)
Timer(2, alien.moveEye).start()
while not Blinking:
alien.moveEye(event)
if alien.moveEye.event.x > 190:
canvas.move(alien.eyeball, -1 , 0)
Small circle with radius r lies inside big circle with radius R, if
(BigCenter.X - SmallCenter.x)^2 + (BigCenter.Y - SmallCenter.Y)^2 < (R - r)^2
^2 denotes squared value

Python tkinter canvas transparent

I am looking to make the background of the tkinter canvas transparent, but still have Mouse events of the canvas, here is my code, I am on Windows 10, Python 3.6:
from tkinter import *
import time
WIDTH = 500
HEIGHT = 500
LINEWIDTH = 1
TRANSCOLOUR = 'gray'
global old
old = ()
tk = Tk()
tk.title('Virtual whiteboard')
tk.wm_attributes('-transparentcolor', TRANSCOLOUR)
canvas = Canvas(tk, width=WIDTH, height=HEIGHT)
canvas.pack()
canvas.config(cursor='tcross')
canvas.create_rectangle(0, 0, WIDTH, HEIGHT, fill=TRANSCOLOUR, outline=TRANSCOLOUR)
def buttonmotion(evt):
global old
if old == ():
old = (evt.x, evt.y)
return
else:
canvas.create_line(old[0], old[1], evt.x, evt.y, width=LINEWIDTH)
old = (evt.x, evt.y)
def buttonclick(evt):
global old
canvas.create_line(evt.x-1, evt.y-1, evt.x, evt.y, width=LINEWIDTH)
old = (evt.x, evt.y)
canvas.bind('<Button-1>', buttonmotion)
canvas.bind('<B1-Motion>', buttonclick)
while True:
tk.update()
time.sleep(0.01)
When run the code, it makes a transparent background, but I select the things under, instead of the canvas.
I build a little workaround with the help of the win api, here is my suggestion:
from tkinter import *
import time
import win32gui
import win32api
WIDTH = 500
HEIGHT = 500
LINEWIDTH = 1
TRANSCOLOUR = 'gray'
title = 'Virtual whiteboard'
global old
old = ()
global HWND_t
HWND_t = 0
tk = Tk()
tk.title(title)
tk.lift()
tk.wm_attributes("-topmost", True)
tk.wm_attributes("-transparentcolor", TRANSCOLOUR)
state_left = win32api.GetKeyState(0x01) # Left button down = 0 or 1. Button up = -127 or -128
canvas = Canvas(tk, width=WIDTH, height=HEIGHT)
canvas.pack()
canvas.config(cursor='tcross')
canvas.create_rectangle(0, 0, WIDTH, HEIGHT, fill=TRANSCOLOUR, outline=TRANSCOLOUR)
def putOnTop(event):
event.widget.unbind('<Visibility>')
event.widget.update()
event.widget.lift()
event.widget.bind('<Visibility>', putOnTop)
def drawline(data):
global old
if old !=():
canvas.create_line(old[0], old[1], data[0], data[1], width=LINEWIDTH)
old = (data[0], data[1])
def enumHandler(hwnd, lParam):
global HWND_t
if win32gui.IsWindowVisible(hwnd):
if title in win32gui.GetWindowText(hwnd):
HWND_t = hwnd
win32gui.EnumWindows(enumHandler, None)
tk.bind('<Visibility>', putOnTop)
tk.focus()
running = 1
while running == 1:
try:
tk.update()
time.sleep(0.01)
if HWND_t != 0:
windowborder = win32gui.GetWindowRect(HWND_t)
cur_pos = win32api.GetCursorPos()
state_left_new = win32api.GetKeyState(0x01)
if state_left_new != state_left:
if windowborder[0] < cur_pos[0] and windowborder[2] > cur_pos[0] and windowborder[1] < cur_pos[1] and windowborder[3] > cur_pos[1]:
drawline((cur_pos[0] - windowborder[0] - 5, cur_pos[1] - windowborder[1] - 30))
else:
old = ()
except Exception as e:
running = 0
print("error %r" % (e))
Shot explanation of the new code bits:
tk.lift()
tk.wm_attributes("-topmost", True)
...
def putOnTop(event):
event.widget.unbind('<Visibility>')
event.widget.update()
event.widget.lift()
event.widget.bind('<Visibility>', putOnTop)
...
tk.bind('<Visibility>', putOnTop)
tk.focus()
These lines ensure, that the window will be always be on top of all other windows.
global HWND_t
HWND_t = 0
...
def enumHandler(hwnd, lParam):
global HWND_t
if win32gui.IsWindowVisible(hwnd):
if title in win32gui.GetWindowText(hwnd):
HWND_t = hwnd
win32gui.EnumWindows(enumHandler, None)
This code bit will go through all the windows currently displayed and catches the handle of the whiteboard window (make sure the title is unique, or this could capture the wrong handle).
state_left = win32api.GetKeyState(0x01)
...
if HWND_t != 0:
windowborder = win32gui.GetWindowRect(HWND_t)
cur_pos = win32api.GetCursorPos()
state_left_new = win32api.GetKeyState(0x01)
if state_left_new != state_left:
if windowborder[0] < cur_pos[0] and windowborder[2] > cur_pos[0] and windowborder[1] < cur_pos[1] and windowborder[3] > cur_pos[1]:
drawline((cur_pos[0] - windowborder[0] - 5, cur_pos[1] - windowborder[1] - 30))
else:
old = ()
This
Checks, if the handle is found
Checks, if mouse button 1 is clicked or not
Checks, if mouse is inside the window
if all is true, it takes the mouse data and draws the line
the current mode is, that it doesn't draw anything till the button is clicked and then draws until the button is clicked again.
I'm sure you've thought of this, but have you tried setting the hexadecimal colour as ""?
i.e
canvas = tk.Canvas(width, height, bg = "")
That works for my version of python.

Tkinter events outside of the mainloop?

The program I am writing has a tkinter window that is constantly being fed with data manually rather than being part of a mainloop. It also needs to track mouse location. I havn't found a workaround for tracking the mouse outside of mainloop yet, but if you have one please do tell.
from Tkinter import *
import random
import time
def getCoords(event):
xm, ym = event.x, event.y
str1 = "mouse at x=%d y=%d" % (xm, ym)
print str1
class iciclePhysics(object):
def __init__(self, fallrange, speed=5):
self.speed = speed
self.xpos = random.choice(range(0,fallrange))
self.ypos = 0
def draw(self,canvas):
try:
self.id = canvas.create_polygon(self.xpos-10, self.ypos, self.xpos+10, self.ypos, self.xpos, self.ypos+25, fill = 'lightblue')
except:
pass
def fall(self,canvas):
self.ypos+=self.speed
canvas.move(self.id, 0, self.ypos)
root = Tk()
mainFrame = Frame(root, bg= 'yellow', width=300, height=200)
mainFrame.pack()
mainCanvas = Canvas(mainFrame, bg = 'black', height = 500, width = 500, cursor = 'circle')
mainCanvas.bind("<Motion>", getCoords)
mainCanvas.pack()
root.resizable(0, 0)
difficulty = 1500
#root.mainloop()
currentIcicles = [iciclePhysics(difficulty)]
root.update()
currentIcicles[0].draw(mainCanvas)
root.update_idletasks()
time.sleep(0.1)
currentIcicles[0].fall(mainCanvas)
root.update_idletasks()
tracker = 0
sleeptime = 0.04
while True:
tracker+=1
time.sleep(sleeptime)
if tracker % 3 == 0 and difficulty > 500:
difficulty -= 1
elif difficulty <= 500:
sleeptime-=.00002
currentIcicles.append(iciclePhysics(difficulty))
currentIcicles[len(currentIcicles)-1].draw(mainCanvas)
for i in range(len(currentIcicles)):
currentIcicles[i].fall(mainCanvas)
root.update_idletasks()
for i in currentIcicles:
if i.ypos >= 90:
currentIcicles.remove(i)
root.update_idletasks()
There is no way. Mouse movement is presented to the GUI as a series of events. In order to process events, the event loop must be running.
Also, you should pretty much never do a sleep inside a GUI application. All that does is freeze the GUI during the sleep.
Another hint: you only need to create an icicle once; to make it fall you can use the move method of the canvas.
If you are having problems understanding event based programming, the solution isn't to avoid the event loop, the solution is to learn how event loops work. You pretty much can't create a GUI without it.

Categories