Python AutoGUI not clicking - bots

Hey I'm pretty bad at coding, but I wanted to automate a task in a game, specifically fishing. Therefore, you have to click a button, another one pops up with a white circle around it. This circle varies its size and when its the size of the button, the circle changes its color and you have to click the button to catch a fish. Therefore I searched the first button (fishing hole), and clicked on its position. Then I look at a specific pixel around the second button, that changes color from white to pale red?. If the color changes, I want to click the button. Everything works besides this last step, it moves there if I use the pyautogui.click(x,y), but it doesn't click. Any way to make it work (bear in mind I'm not an experienced programmer)?. I tried pyautogui.click(x,y), clicking several times and so on. Thank you.
import pyautogui as pa
import time
counter = 0
def main():
global counter
# Countdown to open the game
for i in reversed(range(0,5)):
print(i)
time.sleep(1)
# Finding the fishing hole using a prior screenshot
fishing_hole_location = pa.locateOnScreen("fishing_hole.png",confidence = 0.5)
# Getting the center of the hole
fishing_hole_center = pa.center(fishing_hole_location)
# Clicking the hole
pa.click(fishing_hole_center[0], fishing_hole_center[1])
time.sleep(3)
# Locating the second button (even though it appears in the middle of the screen)
fishing_button_location = pa.locateOnScreen("fishing_button.png",confidence = 0.5)
# Finding the buttons center
fishing_button_center = pa.center(fishing_button_location)
while True:
# Looking at the pixel at the edge of the button
pix = pa.pixel(int(fishing_button_center[0]+24), int(fishing_button_center[1]))
# If the pixel doesn´t have its usual colour 5 times in a row, I want to click the button
if pix != (75,99,118):
counter+= 1
if counter >= 5:
pa.moveTo(fishing_button_center[0], fishing_button_center[1])
time.sleep(2)
pa.click()
break
else:
counter = 0
time.sleep(0.1)
main()

Try making a function like this instead of using the pre-built one -
def click(button):
pyautogui.mouseDown(button=button)
pyautogui.mouseUp(button=button)
It worked for me when I had this same problem

Related

Making parts of canvas transparent while still detecting and blocking mouse clicks in transparent areas in tkinter?

I'm trying to make a program where the user can paint on the screen. So I want to make an invisible canvas window in fullscreen where only the user's pen marks on the canvas will be visible. The closest thing I found is this function: root.attributes("-transparentcolor","color code here"), which will make all the parts of the window that's in the color you give transparent. So if I give the second parameter the background color of the canvas, then only the pen strokes on the canvas will be visible. This is so close to what I want, except for one thing, the transparent areas can't detect or block mouse clicks! Any mouse clicks will just go through to whatever is behind the tkinter window. Is there a way to make it so the transparent areas will still block mouse clicks? I really need help on this!
Here is a much better way to do this using only tkinter. Explanation is in code comments. Basically uses two windows, one for "blocking" the mouse and being transparent using the "-alpha" attribute and the other window for "hosting" canvas and having one completely transparent color while keeping others opaque using "-transparentcolor" attribute. That also means that this is cross-platform solution too (except I think the -transparentcolor attribute differs a little bit on other OS like Linux where I think it is -splash or sth and maybe something different on MacOS):
from tkinter import Tk, Toplevel, Canvas
# setting the starting coordinate of the line so that
# on motion it is possible to immediately draw it
def set_first(event):
points.extend([event.x, event.y])
# on motion append new coordinates to the list and if there are
# 4 (the minimum), create a new line and save the id
# otherwise update the existing line
def append_and_draw(event):
global line
points.extend([event.x, event.y])
if len(points) == 4:
line = canvas.create_line(points, **line_options)
else:
canvas.coords(line, points)
# when released clear the list to not waste space
# and not necessarily but also set "id" to None
def clear_list(event=None):
global line
points.clear()
line = None
line = None # this is a reference to the current line (id)
points = [] # list to keep track of current line coordinates
line_options = {} # dictionary to allow easier change of line options
# just a variable to more easily store the transparent color
transparent_color = 'grey15'
# creating the root window which will help with drawing the line
# because it will "block" mouse because `-alpha` (0.01 seems to be the lowest value)
# attribute is used, however it makes everything transparent on the window
# so need another window to "host" the canvas
root = Tk()
root.attributes('-alpha', 0.01)
root.attributes('-topmost', True)
root.attributes('-fullscreen', True)
# just press Esc key to close the whole thing, otherwise
# it is only doable by pressing Alt + F4 or turning off
# the computer
root.bind('<Escape>', lambda e: root.quit())
# create the host window, because it allows to have only
# one transparent color while keeping the other opaque and
# visible
top = Toplevel(root)
top.attributes('-transparentcolor', transparent_color)
top.attributes('-topmost', True)
top.attributes('-fullscreen', True)
# set the focus to root because that is where events are bound
root.focus_set()
# create the canvas to draw on
canvas = Canvas(top, bg=transparent_color, highlightthickness=0)
canvas.pack(fill='both', expand=True)
# bind all the events to `root` which "blocks" mouse
# but is also almost (because it has a very small alpha value
# it is not entirely invisible but human eye won't notice much)
# invisible
root.bind('<Button-1>', set_first)
root.bind('<B1-Motion>', append_and_draw)
root.bind('<ButtonRelease-1>', clear_list)
root.mainloop()
Here is an improvable example (you may need to pip install pyautogui, ctypes is a built-in library), it is also Windows only as far as I know:
Note: The other answer using two windows, however, is a lot better but I will keep this too just for the information.
from tkinter import Tk, Canvas
import pyautogui as pag
import ctypes
data = {
'draw': True,
'cur_line_points': [],
'cur_line_id': None
}
# function taken mainly from here: https://stackoverflow.com/a/46596592/14531062
def is_pressed(btn: str = 'left') -> bool:
if btn == 'left':
btn = 0x01
elif btn == 'right':
btn = 0x02
else:
raise Warning("incorrect argument, should be 'left' or 'right'")
return ctypes.windll.user32.GetKeyState(btn) not in (0, 1)
def draw_line(canvas_):
if not data['draw']:
root.after(10, draw_line, canvas_)
return
pressed = is_pressed('left')
cur_line_points = data['cur_line_points']
cur_line_id = data['cur_line_id']
if not pressed:
if cur_line_id is not None:
canvas_.coords(cur_line_id, cur_line_points)
data['cur_line_id'] = None
cur_line_points.clear()
else:
mouse_x, mouse_y = pag.position()
cur_line_points.extend((mouse_x, mouse_y))
len_points = len(cur_line_points)
if len_points == 4:
data['cur_line_id'] = canvas_.create_line(cur_line_points)
elif len_points > 4:
canvas_.coords(cur_line_id, cur_line_points)
root.after(10, draw_line, canvas_)
transparent_color = 'grey15'
root = Tk()
root.config(bg=transparent_color)
root.attributes('-transparentcolor', transparent_color)
root.attributes('-topmost', True)
root.attributes('-fullscreen', True)
canvas = Canvas(root, bg=transparent_color, highlightthickness=0)
canvas.pack(fill='both', expand=True)
draw_line(canvas)
root.mainloop()
Basically detects if mouse button is pressed using the built-in library ctypes and if it is adds the current mouse coordinates (does that using pyautogui library which may need be installed) to a list and then draws a line based on that list (it also keeps the reference of the currently drawn line and simply changes its coordinates instead of drawing a new line each time it loops), the only slight issue is that while drawing the mouse is also interacting with the window below, highlighting text and stuff, couldn't really figure out how to remove that yet but at least you get to draw a line.

PySimpleGUI - not show background when window change

In my application I have multiple windows that change based on events(one close and another open) and show only one window at a time. During one window close and another open its take some time since fetch data from database and prepare for window.
Here problem is that during the time of one window close and another open user can see and feel that one is being open and another is being close by seeing the background.
What I want, until second screen is not fully loaded, first window be visible on the screen.
My current code is something like,
import PySimpleGUI as sg
layout = [[sg.Button('Users', key='show_user_list')]]
window = sg.Window('users').Layout(layout)
while True:
event, values = window.Read()
if event == 'show_user_list':
window.Close()
# code ommited here for simplicity
# do mysql stuff to fetch data
# layout2 = ...
# window2 = sg.Window('user listing').Layout(layout2)
# while True:
# event, values = window2.Read()
# ...
# like that I have multiple windows
else:
pass
How I can give users feel like the window content is changing not one window closes and another opens?
Sure, you can make sure the background is not seen by first opening your Window 2, which will be created on top of Window 1, THEN closing your Window 1.
To do this, add a .Finalize() onto the Window 2 creation. This will cause the window to immediate show up. Then on the next line, close Window 1.
import PySimpleGUI as sg
layout = [[sg.Button('Users', key='show_user_list')]]
window = sg.Window('users').Layout(layout)
while True:
event, values = window.Read()
if event == 'show_user_list':
# code ommited here for simplicity
# do mysql stuff to fetch data
# layout2 = ...
# window2 = sg.Window('user listing').Layout(layout2).Finalize()
# window.Close()
# while True:
# event, values = window2.Read()
# ...
# like that I have multiple windows
else:
pass
The key to making this kind of window update work is to create the windows at the same location. The default is to make windows that are centered on the screen. This means if your windows are not the same size then you'll likely notice a small "blip" as you change from one to the other. But it shouldn't look bad because it'll happen so quickly.
If you really want to get fancy, you can add another step which will make the switch between the windows even smoother. This new step involves creating window 2 with Alpha=0, meaning that it's invisible, then after it's fully formed (using Finalize()) you change the Alpha to 1 which will make the window appear.
import PySimpleGUI as sg
layout = [[sg.Text('Example of window-replacement')],
[sg.Combo(['abdeffg', 'rrrfwwew'], size=(10, 4))],
[sg.B('Enable Filter'), sg.B('Warning'), sg.B('Reopen')],]
window = sg.Window('My Text Editor', layout)
while True: # Event Loop
event, values = window.Read()
if event is None:
break
print(event, values)
if event == 'Reopen':
layout2 = [[sg.Text('This is a completely different window')],
[sg.Combo(['abdeffg', 'rrrfwwew'], size=(10, 4))],
[sg.B('Enable Filter'), sg.B('Warning'), sg.B('Reopen')], ]
window2 = sg.Window('My Text Editor', layout2, alpha_channel=0).Finalize()
window2.SetAlpha(1)
window.Close()
window = window2
window.Close()
This removed some of the "painting" of the window that I was seeing. That shouldn't happen because I use this same trick when creating the window to begin with. Alpha is used to hide the window while it's being created.

Why does tkinter sometimes work without mainloop in Python 3?

Here's code for a little game. You're supposed to hit buttons that say 'click' to score points and avoid hitting buttons that say 'clack' or 'cluck' which cause you to lose points. The first weird thing is that the program works fine under IDLE even though it doesn't include a call to mainloop. The second is that it stops working if we add the following line at the bottom:
input('Hit enter to continue')
No game window appears, even after we hit enter. Adding a different line at the end like
print('hello')
does not have this effect.
Can anyone explain to me what's going on? I know that GUI programs used to run under IDLE's own mainloop, but that was a long time ago in Python 2 and definitely wasn't generally true under Python 3, at least last time I checked.
from tkinter import *
import random
score = 0
root = Tk()
scoreFrame = Frame(root)
scoreFrame.pack(expand = YES, fill = BOTH)
scoreLabel = Label(scoreFrame)
scoreLabel.pack()
def showScore():
scoreLabel['text'] = 'Score: {0}'.format(score)
scoreLabel.pack()
clickFrame = Frame(root)
clickFrame.pack(side = BOTTOM, expand = YES, fill = BOTH)
def changeLabels():
for button in buttons:
button['text'] = random.choice(['click', 'clack', 'cluck'])
button['bg'] = buttonDefaultColor
root.after(1500, changeLabels)
def makeButton():
button = Button(clickFrame)
def cmd():
global score
if button['bg'] == buttonDefaultColor:
if button['text'] == 'click':
score += 10
button['bg'] = 'light green'
else:
score -= 10
button['bg'] = 'light yellow'
button['command'] = cmd
button.pack(side = LEFT, expand = YES, fill = BOTH)
return button
buttons = [makeButton() for i in range(5)]
buttonDefaultColor = buttons[0]['bg']
changeLabels()
showScore()
Added: The first three comments all suggest that IDLE is running the event loop for me, making mainloop unnecessary but (1) I remember clearly when this suddenly stopped being true some years back (not that IDLE stopped running mainloop, but that GUI programs running under it did not need to run mainloop themselves) and (2) no one has explained why the input statement at the end breaks the program.

Tkinter Entry widget color fading

In my new application I want when the mouse is over the entry() widget to change color (this I know how to do it) but I want the color to change gradually, not immediately.
This is my code:
# User_Line Focus In/Out
def User_Line_Focus_In(self, event):
self.User_Line.configure(bg = "#DCDCDC")
def User_Line_Focus_Out(self, event):
self.User_Line.configure(bg = "#FFFFFF")
You need to create a method which increments the colour and you need to use tkinter's after which registers an alarm callback that is called after a given time. You then need to reference it recursively in order to get the fading effect you want.
def incrementHex(hex_str, increment): #with hex_str in format "#FFFFFF" or any colour
red = int(hex_str[1:3],16) #specifies base for integer conversion
green = int(hex_str[3:5],16)
blue = int(hex_str[5:],16)
red += increment #increment can be negative
green += increment
blue += increment
new_hex_str = "#" + str(hex(red)) + str(hex(blue)) + str(hex(green))
return new_hex_str
def Fade(self, start_hex, increment):
new_hex = self.incrementHex(start_hex, increment)
self.User_Line.configure(bg = new_hex)
#where self.master is the parent widget as defined in the __init__ method...
self.master.after(50,lambda: self.Fade(new_hex, increment)) #or any time interval in milliseconds
#you'll probably need some code to stop it fading here, but I'll let you tackle that one :)
def User_Line_Focus_In(self, event):
self.Fade("#FFFFFF",-1) #could be any colour and increment
I haven't been able to test it, but I think it should work in principle. An extension of this would be to have different increments for red, green and blue.
I think you are going to have to pull up your socks on this one and do some coding (tkinter doesn't have this built in)
So what you are looking for is :
An algorithm to go from color one to color two and get intermediate colors. (Hex values are just numbers in base 16 but they can be added or subtracted like normal numbers)
The simplest solution would be to just run the algorithm (color_difference here)
def fade_colors(event, new_color):
old_color = event.widget.cget('bg')
for color in color_difference(old_color, new_color):
event.widget.configure(color)
time.sleep(0.1)
widget.bind('<Enter>', lambda event: fade_colors(event, color))
You might also like to cancel the operation if the user leaves the widget. Take a look at the built in sched module.
If you find your gui becomes unresponsive during the fading you could consider using the after method, you can read this excellent blog post on non blocking gui techniques in python and tkinter. This may not be an issue if you cancel the callback as soon as the user leaves the widget (thus freeing up tkinter to handle his other actions)

Getting text from a entry window in Python graphics

from graphics import *
win = GraphWin("Hangman", 600, 600)
win.setBackground("yellow")
textEntry = Entry(Point(233,200),10)
textEntry.draw(win)
text = textEntry.getText()
testText = Text(Point(150,15), text)
testText.draw(win)
exitText = Text(Point(200,50), 'Click anywhere to quit')
exitText.draw(win)
win.getMouse()
win.close()
I'm trying to obtain text from the user in Python graphics and be able to use that input, such as manipulate it, search for it in a list, etc. To test that, I created a entry window in graphics and tried to obtain the text from that entry window and simply display it in the window, just to check if it sucessfully obtained the text.
Unfortunately, it isn't working, it just shows the 'Click anywhere to quit' and then the empty window and despite writing text in it it does nothing. What am I doing wrong?
The following comes from the documentation.
The way the underlying events are hidden in graphics.py, there is no signal when the user is done entering text in an Entry box. To signal the program, a mouse press is used above. In this case the location of the mouse press is not relevant, but once the mouse press is processed, execution can go on and read the Entry text.
You're getting the text right after you draw the Entry, so it will be empty. You need to wait for a signal, then read the Entry. This excerpt from the documentation says to wait for a mouse click then read the Entry.
So try to add the
win.getMouse()
to your code as follows
from graphics import *
win = GraphWin("Hangman", 600, 600)
win.setBackground("yellow")
textEntry = Entry(Point(233,200),50)
textEntry.draw(win)
# click the mouse to signal done entering text
win.getMouse()
text = textEntry.getText()
testText = Text(Point(150,15), text)
testText.draw(win)
exitText = Text(Point(200,50), 'Click anywhere to quit')
exitText.draw(win)
win.getMouse()
win.close()
Here's what the output looks like. Note: I made the Entry 50 wide.

Resources