This question already has an answer here:
thread starts running before calling Thread.start
(1 answer)
Closed 3 months ago.
I want to make a small program which plays a song and an image pops up while the mouse cursor is moved. I have 3 functions for 3 actions and I want to run them at the same time but I can't accomplish it. Could you help me?
import random
import threading
import pyautogui
import pygame
from tkinter import *
def play_song():
file = 'Troll_Song.ogg'
pygame.mixer.init()
pygame.mixer.music.load(file)
pygame.mixer.music.play()
while pygame.mixer.music.get_busy():
pygame.time.Clock().tick(10)
def create_window():
while True:
root = Tk()
root.title('Trololo...')
photo = PhotoImage(file='trollface.gif')
label = Label(root, image=photo)
label.pack()
w = 620 # width for the Tk root
h = 620 # height for the Tk root
# get screen width and height
ws = root.winfo_screenwidth() # width of the screen
hs = root.winfo_screenheight() # height of the screen
# random positions of the window
x = random.randint(0, ws - 620)
y = random.randint(0, hs - 620)
# set the dimensions of the screen
# and where it is placed
root.geometry('%dx%d+%d+%d' % (w, h, x, y))
root.mainloop()
def mouse_move():
width, height = pyautogui.size()
while True:
x = random.randint(0, width)
y = random.randint(0, height)
pyautogui.moveTo(x, y, duration=0.3)
if __name__ == '__main__':
t1 = threading.Thread(target=create_window())
t2 = threading.Thread(target=play_song())
t3 = threading.Thread(target=mouse_move())
t1.start()
t2.start()
t3.start()
I don't know if it's the only problem with your code, but I can tell about threading -- target has to be a function, instead you call functions, making them run in main thread. So if first function is an infinite loop -- program will not create any threads, because it will stuck executing the first function. Here is how you do it:
t1 = threading.Thread(target=create_window)
t2 = threading.Thread(target=play_song)
t3 = threading.Thread(target=mouse_move)
Related
I am made a little window that helps me play a game. But when I click somewhere else the window just minimizes or goes to the back. How do I make sure that my pygame window stays on top of the screen?
My answer is taken from Bring a pygame window to front
from os import environ
environ['PYGAME_HIDE_SUPPORT_PROMPT'] = "hide"
import pygame # import after disabling environ prompt
from win32gui import SetWindowPos
import tkinter as tk
root = tk.Tk() # create only one instance for Tk()
root.withdraw() # keep the root window from appearing
screen_w, screen_h = root.winfo_screenwidth(), root.winfo_screenheight()
win_w = 250
win_h = 300
x = round((screen_w - win_w) / 2)
y = round((screen_h - win_h) / 2 * 0.8) # 80 % of the actual height
# pygame screen parameter for further use in code
screen = pygame.display.set_mode((win_w, win_h))
# Set window position center-screen and on top of other windows
# Here 2nd parameter (-1) is essential for putting window on top
SetWindowPos(pygame.display.get_wm_info()['window'], -1, x, y, 0, 0, 1)
# regular pygame loop
done = False
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
done = True
Hope it helps!☺
from os import environ
environ['PYGAME_HIDE_SUPPORT_PROMPT'] = "hide"
import pygame # import after disabling prompt
screen = pygame.display.set_mode((800, 800))
screen.fill((50, 50, 50)) # Dark gray color
pygame.display.update()
Yes, I did my research already, and couldn't find anything helpful: hence this question.
Every time I run the program the pygame window opens below other windows. I want it to behave in 2 ways based on code: Pin the window on top and spawn on top but no pin.
Here is the simplest solution I found:
(It also requires tkinter to get system screen metrics)
from os import environ
environ['PYGAME_HIDE_SUPPORT_PROMPT'] = "hide"
import pygame # import after disabling environ prompt
from win32gui import SetWindowPos
import tkinter as tk
root = tk.Tk() # create only one instance for Tk()
root.withdraw() # keep the root window from appearing
screen_w, screen_h = root.winfo_screenwidth(), root.winfo_screenheight()
win_w = 250
win_h = 300
x = round((screen_w - win_w) / 2)
y = round((screen_h - win_h) / 2 * 0.8) # 80 % of the actual height
# pygame screen parameter for further use in code
screen = pygame.display.set_mode((win_w, win_h))
# Set window position center-screen and on top of other windows
# Here 2nd parameter (-1) is essential for putting window on top
SetWindowPos(pygame.display.get_wm_info()['window'], -1, x, y, 0, 0, 1)
# regular pygame loop
done = False
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
done = True
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
I just finished my first Python project, an analog clock. I managed to reach a state where I implemented everything I had in mind and fixed the issues that followed. The last thing I added in is a dragging capability for my clock and while it works it seems slow or at least just unsmooth. Right now I lack the knowledge and experience to understand why its happening and let alone fix it.. Any help or ideas would be appreciated, Thanks in advance. :)
Here is my code:
# Imported tkinter for its methods
import tkinter as tk
# Imported turtle for drawing the clock's hands
import turtle
# Imported time to handle setting and updating the time
import time
# Declared and set the color used for transparency
transparent_clr = '#FB00FF'
# Setup a borderless window with transparent background
# and always on top flag
root = tk.Tk()
root.overrideredirect(True)
root.wm_attributes('-topmost', 1)
root.deiconify()
root.attributes('-transparentcolor', transparent_clr)
# Setup the clock's face using an image
Clock_bg = tk.PhotoImage(file='Clock_bg.png')
canvas = tk.Canvas(width=300, height=300, highlightthickness=0)
screen = turtle.TurtleScreen(canvas)
canvas.create_image(0, 0, image=Clock_bg)
canvas.pack()
screen.tracer(0)
screen.bgcolor(transparent_clr)
# Configure the pen used for the clock's hands
draw = turtle.RawTurtle(screen)
draw.hideturtle()
draw.speed(0)
draw.pensize(3)
# Retain Windows TaskBar visibility and function such as exiting
# the app
wn = tk.Toplevel(root)
wn.iconify()
wn.iconbitmap('Clock_icon.ico')
wn.attributes('-alpha', 0.0)
def wn_destroy():
wn.protocol('WM_DELETE_WINDOW', exit_func)
def exit_func():
root.destroy()
wn_destroy()
# Make the clock draggable
def draggable():
root._offsetx = 0
root._offsety = 0
root.bind('<Button-1>', winclick)
root.bind('<B1-Motion>', windrag)
def windrag(event):
x = root.winfo_pointerx() - root._offsetx
y = root.winfo_pointery() - root._offsety
root.geometry('+{x}+{y}'.format(x=x, y=y))
def winclick(event):
root._offsetx = event.x
root._offsety = event.y
draggable()
# Draw the clock and its hands
def draw_clock(h, m, s, draw):
# Draw the hours hand
draw.penup()
draw.goto(0, 0)
draw.color('black')
draw.setheading(90)
angle = (h / 12) * 360 + (m / 60) * 30
draw.rt(angle)
draw.pendown()
draw.fd(70)
# Draw the minutes hand
draw.penup()
draw.goto(0, 0)
draw.color('black')
draw.setheading(90)
angle = (m / 60) * 360 + (s / 60) * 6
draw.rt(angle)
draw.pendown()
draw.fd(100)
# Draw the seconds hand
draw.penup()
draw.goto(0, 0)
draw.color('red')
draw.setheading(90)
angle = (s / 60) * 360
draw.rt(angle)
draw.pendown()
draw.fd(60)
# Update the time in real time
while True:
# Declared and set the hour, minutes and seconds
h = int(time.strftime('%I'))
m = int(time.strftime('%M'))
s = int(time.strftime('%S'))
draw_clock(h, m, s, draw)
screen.update()
time.sleep(1)
draw.clear()
"You are using your own mainloop with time.sleep(1), therefore root.geometry( gets only called every second. –stovfl"
Thanks a billion, stovf1!
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.