Python using turtle button - python

I am attempting for a homework assignment to implement Simon Says in python. I'm trying to do it using the turtle library (a requirement).
However, I've run into a stumbling block in that while I can get the screen to register click events (currently just printing the x,y coordinates) I can't get it to wait for a click event.
Specifically what I'm planning on doing is having areas on the screen that when they click within that location it is considered as if they had clicked a button. Screen clears and game does whatever.
However, in experiments in trying to get a working 'button' all that it does is set it so it prints the x,y coordinates but the rest of the program finishes. Didn't wait for the user to click anything. I tried a blocking method of...
while clicked == False:
pass
or
while clicked == False:
time.sleep(1)
but both methods hangs the program until I manually interrupt and then it'll print the clicks.
Am I missing an option somewhere?

Turtles don´t have buttons, but they do have callbacks for clicks.
Furthermore, you should use onclick for Screen to detect general clicks and onclick for turtle to detect clicking in turtles. You can, for example, make a 4 BIG turtles with different colors by using a dynamic shape.
Also, turtle is based on Tk, so you must be aware of things like mainloop()
The following program give some hints for Python 2.7.5.
import turtle as t
from random import randint
class MyTurtle(t.Turtle) :
def __init__(self,**args) :
t.Turtle.__init__(self,**args)
def mygoto(self,x,y) :
t1.goto(x,y)
print x,y
def randonics(self,x,y) :
self.left(randint(90,270))
def minegoto(x,y) :
print x,y
t1.goto(x,y)
wt=t.Screen()
t1=MyTurtle()
wt.register_shape("big",((0,0),(30,0),(30,30),(0,30)))
t1.shape("big")
wt.onclick(t1.mygoto,btn=1)
wt.onclick(minegoto,btn=2)
t1.onclick(t1.randonics,btn=3)
t1.goto(100,100)
t.mainloop()

So after extensive search there isn't necessarily a way pause execution of the code in python while using turtle to wait for some click event. Maybe in Tk I could do that but not in turtle.
However, there is a way to get around that. As an example. A method sets up the fake button on the screen, sets the click event, and terminates. The click event when clicked calls the next method needed for execution. So until the button is clicked the actual code isn't doing anything but remains in memory for use.
So more specifically.
1. Create a 'button'.
2. Have your program behave normally until it needs to wait for a click event.
3. Set up the on screen click (or on turtle) in such a way when the 'button' is clicked the next part of the code is run.
Special note. The code in question can't depend on waiting for a click event for later on in code. Instead, the click causes the next part of the execution of your code.

You can make the function registered with onclick() test the x,y position. If it is inside some region you do whatever you must.
I don´t see the difference between what you want to do and what this code does, the modification of turtle position is just an example, you can do anything when a click is captured by onclick(), even start a thread if you really need it (using Creating Threads in python)
import turtle as t
from random import randint
from threading import Thread
from time import sleep
def threaded_function(arg,t1):
for i in range(arg):
print "running",i
sleep(1)
t1.forward(i*10)
def minegoto(x,y) :
print x,y
t1.goto(x,y)
thread = Thread(target = threaded_function, args = (10,t1 ))
thread.start()
thread.join()
print "thread finished...exiting"
wt=t.Screen()
t1=t.Turtle()
wt.register_shape("big",((0,0),(30,0),(30,30),(0,30)))
t1.shape("big")
wt.onclick(minegoto,btn=1)
t1.goto(100,100)
t.mainloop()

Related

pyautogui and pydirectinput not working properly on Halo 5?

I am trying to write a script in python that would take control of Halo 5 forge in order to automatically create and script an in-game script brain object and use the game's built-in scripting system to script the object to randomize the movement of 2 agents (will be added later) in order to procedurally generate a map, saving me time in doing tedious manual work on creating a method from scratch I have repeated many times in Halo 5 Forge.
Basically I am trying to create a class representing this script brain object in python and under the init method it is supposed to follow all of the steps in Forge to create a script brain object.
At first I tried pyautogui as it initially worked previously on Halo 5. But now it doesn't seem to work. It is a known issue that pyautogui doesn't input certain things properly on directX games and was suggested pydirectinput as an alternative.
So I did and while the mouse responds properly in the game and opens the object menu using the press() method, it doesn't actually seem to respond to leftClick() for some reason, only repositioning the mouse to the correct position but not actually clicking the object menu like I had hoped.
Here is the code:
import os
import pyautogui as pygui
import pydirectinput as pydi
import time
class forgeObjectRandomizer:
def __init__(self):
#CREATE THE SCRIPT BRAIN
time.sleep(5)
screenWidth, screenHeight = pydi.size()
pydi.moveTo(round(screenWidth / 2), round(screenHeight / 2))
time.sleep(1)
# pydi.leftClick(1749, 44)
pydi.press('o')
time.sleep(0.5)
pydi.leftClick(1554, 337) # --- Extras
time.sleep(0.5)
pydi.leftClick(1605, 266) # --- Scripting
time.sleep(0.5)
pydi.leftClick(1630, 236) # --- Script Brain
time.sleep(0.5)
pydi.press('p') # --- Properties
time.sleep(0.5)
pydi.mouseDown(1886, 231) # --- Scroll down
pydi.moveTo(1886, 453) # --- Scroll down
pydi.mouseUp()
brainRandomizer = forgeObjectRandomizer()
You can use to move mouse
import win32api,win32con
def click():
win32api.mouse_event(win32con.MOUSEEVENTF_MOVE, int(x), int(y), 15, 15)
for mouse click equivalent, but I only had problems with moving the mouse
This is kinda a blind shot since u said pyautogui was working fine before on halo 5...are you running the game as administrator? If so make sure that ur code does too, because i had same problem in a game named asda global

Is there a way to wait for a condition to be true?

I'm attempting to log coordinates 3 separate times when a user clicks on the turtle screen, then continue running other commands once that is completed. Clicking 3 times does nothing, and the shell keeps printing that it's waiting, while one additional click causes the whole thing to not work and I get a "not Responding" message from the turtle graphics window.
import turtle as t
import time
canvas=t.getcanvas()
xlist=[]
ylist=[]
listcomplete=False
def getPos(x,y):
xlist.append(canvas.winfo_pointerx()) ##Logs the x and y coords when mouse is clicked
ylist.append(canvas.winfo_pointery())
print('appended the lists.')
if len(xlist)==3:
listcomplete=True
t.onscreenclick(getPos)
def main():
while listcomplete==False:
time.sleep(1)
print('waiting...') ##Prints periodically just to let me know it's still running
main()
print('list complete.') ##Prints to alert the list has been finished
print(xlist)
(Insert rest of code to follow)
listcomplete=True within getPos() will not change the global variable, instead it will create a new varable of the same name within the local scope.
To change the global variable, you have to tell python to use it from the global scope:
def getPos(x,y):
global listcomplete # tell python to use the variable from the global scope
xlist.append(canvas.winfo_pointerx()) ##Logs the x and y coords when mouse is clicked
ylist.append(canvas.winfo_pointery())
print('appended the lists.')
if len(xlist)==3:
listcomplete=True
That's due to the default behavior of the assignment operator (=).
Other operators, such as the comparision operator (==) will lookup the variable from the enclosing scope(s) if it's not found within the local scope, thus you may use while listcomplete==False: within main() w/o telling pyton to use the variable from the global scope.
But ideally, you do not even have to use that global variable. Instead run the turtle main loop and exit the turtle window when your condition is met:
import turtle as t
canvas=t.getcanvas()
xlist=[]
ylist=[]
def getPos(x,y):
xlist.append(canvas.winfo_pointerx()) ##Logs the x and y coords when mouse is clicked
ylist.append(canvas.winfo_pointery())
print('appended the lists.')
if len(xlist)==3:
t.bye() # exit turtle window
t.onscreenclick(getPos)
t.Screen().mainloop() # will wait until turtle window is closed
print('list complete.') ##Prints to alert the list has been finished
print(xlist)
Is it possible to continue running the turtle window after the lists
have been created?
Things get difficult in turtle when you fight it's event-based model as you're trying to do. Work with the model, and things get easier. The code below presents a blank window, after you click on it in three places, it will connect your points to make a triangle:
from turtle import Screen, Turtle, mainloop
def getPosition(x, y):
screen.onscreenclick(None) # disable the handler inside the handler
positions.append((x, y))
if len(positions) == 3:
screen.ontimer(listComplete) # sometime after this function completes
else:
screen.onscreenclick(getPosition) # restore the handler
def listComplete():
for position in positions:
turtle.goto(position)
turtle.pendown()
turtle.goto(positions[0]) # close our triangle
# (Insert rest of code to follow)
positions = []
turtle = Turtle()
turtle.hideturtle()
turtle.penup()
screen = Screen()
screen.onscreenclick(getPosition)
mainloop() # invoke as function to make Python 2 friendly as well
The key is that "rest of code to follow" will be in functions, not top level code.
def handle_click(mouse_x,mouse_y): # <== I think thats what ya need right dere, I dont think it knows you're clicking
newpin = [[mouse_x,mouse_y], [0,0], 0,0,20, 1000000]
I tried putting a print statement after the click I couldn't get it to even print a test click. That may be me not trying hard enough tho ;) I just remember using something like above to handle a mouse click. (in my situation it created a pin for a pinball game) if you look up the turtle api for circle you can see the [0,0],0,0,20, 100000] means.
But ultimately that last number the 10000 whatever is "mass" so the more of it the less it moves. again my situation. turtle.onscreenclick(handle_click). Thats at least an idea :) also yes u can do a wait after an if. Throw in print statements.

how do I build a loop with a button + textbox

I'm new, on python 2.7 how do I build a loop with a button + textbox?
for example:
I want the user to enter is name and then press "ok" button, I want his name to be printed 5 times.
I dont want the button to stuck while it perform the command.
thanks!
If you perform a loop on the button_click event then the button will get stuck, because the Main thread of your program is looping, then it is like a stand-by.
You can solve this problem creating another thread containing the loop action.
#add this on the top of your code to import the thread library
import thread
After that you are able to create the thread, but first you need to define the loop function:
#this is the thread function
def loopFunction():
for x in range(5):
#textbook_value is the variable where you have previously stored
#the name inserted into the textbook
print textbook_value
#insert that in your button click event
try:
thread.start_new_thread(loopFunction)
except:
print "Unable to start the thread!"
The second thread will run and your button will not get stuck, because the Main thread is not busy.
To improve your skills about multithreading programming look here.
You can also refer to this link.

Simple animation with Tkinter Python

I've searched for a simple animation code with Tkinter but I've found very different examples and I can't understand the correct way to write an animation.
Here my working code to display a simple moving circle:
import tkinter as tk
import time
root=tk.Tk()
canvas=tk.Canvas(root,width=400,height=400)
canvas.pack()
circle=canvas.create_oval(50,50,80,80,outline="white",fill="blue")
def redraw():
canvas.after(100,redraw)
canvas.move(circle,5,5)
canvas.update()
canvas.after(100,redraw)
root.mainloop()
In this code I can't correctly understand: how the after method works, where correctly put the update and the move method (before after method ?), is there another way to write an animation code? may you post me another example and comment the code please?
Thanks :)
Calling update
You should not call canvas.update(). As a general rule of thumb you should never call update. For a short essay on why, see this essay written by one of the original developers of the underlying tcl interpreter.
If you take out the call to canvas.update(), you have the proper way to do animation in a tkinter program.
Calling after to start the animation
You don't need to call after immediately before calling root.mainloop(). This works just as well:
...
redraw()
root.mainloop()
The choice to use or not use after in this specific case is dependent on if you want the animation to start immediately (possibly even before the widget is visible) or if you want it to happen after a short delay (possibly after the widget is made visible)
How after works
mainloop is nothing more than an infinite loop that checks the event queue for events. When it finds an event, it pops it off of the list and processes it. after is nothing more than making a request that says "in 100 ms, please add a new event to the queue". When the time limit expires, an event is added to the queue that says, in effect, "run this command". The next time the loop checks for events, it sees this event, pulls it off of the queue, and runs the command.
When you call after from within a method that itself was called by after, you're saying in effect "wait 100ms and do it again", creating an infinite loop. If you put the call to after before moving the object, you're saying "every 100ms run this function". If you put it after you're saying "run this function 100 ms after the last time it was run". The difference is very subtle and usually not perceptible unless your function takes a long time to run.
my code is:
from tkinter import *
import time
tk = Tk()
płótno = Canvas(tk, width=500, height=500)
płótno.pack()
płótno.create_polygon(10,10,10,70,70,10,fill="blue",outline="black")
for x in range(0,51):
płótno.move(1,5,0)
płótno.update()
rest(0.05)
płótno means canvas

How to stop a warning dialog from halting execution of a Python program that's controlling it?

Using Win32GUI and Watsup, I'm writing a bit of Python code to automate a search across a database that is accessed through a program that doesn't come with an interface for it. As such, I can take a string from a list and then input it into the search box and press 'lookup'.
However, when the search returns more than 1000 results, the program throws a warning dialog --which is simply a notification of the number of results--which halts the execution of the Python code. I can't get the code to progress past the line where it presses lookup.
At a guess, this would be because it doesn't expect a window or know how to handle a warning--but I don't either, other than manually accepting it. Below is the relevent sample of code, though it's probably not very enlightening. After "clickButton(LookupButton)", the execution halts.
LookupButtonlocation = elemstring.find("Lookup", AuthNameFieldlocation) - 15
#Use Regex search to find handles
number_regex = re.compile(';(\d+);')
AuthNameEdit = int(number_regex.search(elemstring[AuthNameFieldlocation:]).group(1))
LookupButton = int(number_regex.search(elemstring[LookupButtonlocation:]).group(1))
#Input new Author into Edit Field
setEditText(AuthNameEdit, "John Campbell")
#Click lookup button
clickButton(LookupButton)
I'm not a WATSUP user, but I do something very similar using pywinauto - in my case I'm running a number of automated tests that open various 3rd party programs that, in a similar way, throw up inconvenient warning dialogs. It's a bit difficult to deal with dialogs that you don't know about, however if you do know which dialogs appear, but not when they appear, you can start a thread to just deal with those pop-ups. The following is a simple example from what I'm doing, and uses pywinauto but you could adapt the approach for WATSUP:
import time
import threading
class ClearPopupThread(threading.Thread):
def __init__(self, window_name, button_name, quit_event):
threading.Thread.__init__(self)
self.quit_event = quit_event
self.window_name = window_name
self.button_name = button_name
def run(self):
from pywinauto import application, findwindows
while True:
try:
handles = findwindows.find_windows(title=self.window_name)
except findwindows.WindowNotFoundError:
pass #Just do nothing if the pop-up dialog was not found
else: #The window was found, so click the button
for hwnd in handles:
app = application.Application()
app.Connect(handle=hwnd)
popup = app[self.window_name]
button = getattr(popup, self.button_name)
button.Click()
if self.quit_event.is_set():
break
time.sleep(1) #should help reduce cpu load a little for this thread
Essentially this thread is just an infinite loop that looks for a pop-up window by name, and if it finds it, it clicks on a button to close the window. If you have many pop-up windows you can open one thread per popup (bug that's not overly efficient, though). Because it's an infinite loop, I have the thread looking to see if an event is set, to allow me to stop the thread from my main program. So, in the main program I do something like this:
#Start the thread
quit_event = threading.Event()
mythread = ClearPopupThread('Window Popup Title', 'Yes button', quit_event)
# ...
# My program does it's thing here
# ...
# When my program is done I need to end the thread
quit_event.set()
This is not necessarily the only way to deal with your issue, but is a way that's worked for me. Sorry I can't really help you much with dealing with WATSUP (I always found pywinauto a bit easier to use), but I noticed on the WATSUP homepage (http://www.tizmoi.net/watsup/intro.html), Example 2 does something similar without using threads, i.e., looks for a named window and clicks a specific button on that window.

Categories

Resources