Я новичок в питоне и извиняюсь, если решение моего вопроса очевидно. Ни одно из других решений, которые я пробовал, похоже, не сработало, и я был в тупике по этой проблеме почти весь день. Во всяком случае, я пытаюсь создать простой автоматический кликер, который щелкает через случайные промежутки времени с заданным диапазоном. Функционально у меня все работает, но я не могу заставить свою программу перестать щелкать после ее запуска.
Вот моя функция нажатия:
def start_clicking(event=None):
min = min_var.get()
max = max_var.get()
def report_click():
text1.insert(tk.INSERT, "\nTime since last click: " + range_str_s + " (" + range_str_ms + ")\n")
text1.config(state = "normal")
try: # Show exception to user
min = float(min)
max = float(max)
except ValueError:
text1.insert(tk.INSERT, "One or both of your inputs are invalid.")
if running: # For some reason, this runs even though running is False. I only want this to run while running is true
random_range = random.uniform(min, max)
random_range_s_to_ms = int(random_range * 1000) # Convert seconds to ms
range_str_s = "{:,.2f}s".format(random_range)
range_str_ms = "{:,.2f}ms".format(random_range_s_to_ms)
print(running)
report_click()
pyautogui.click()
root.after(random_range_s_to_ms, start_clicking) # Recursively call start_clicking
text1.see("end")
text1.config(state = "disabled")
Это моя функция остановки:
def stop_clicking(event=None):
text1.config(state = "normal")
text1.insert(tk.INSERT, "\nStopping...\n")
global running
running = False # This is not doing what I want it to do.
text1.see("end")
text1.config(state = "disabled")
Некоторые вещи, которые я заметил, пытаясь заставить это работать, заключаются в том, что весь код в моем условии выполнения в моей функции start_clicking() все еще работает, несмотря на то, что он ложный (или я так думаю). Я подтвердил это, напечатав его в консоли. Независимо от того, является ли запуск истинным или ложным, похоже, это не влияет на мой код. Я прокомментировал это в своей программе, чтобы уточнить, в чем проблема.
Вот минимальный воспроизводимый пример:
import pyautogui, time, random, keyboard
import tkinter as tk
root = tk.Tk()
root.geometry("530x320")
min_var = tk.StringVar()
max_var = tk.StringVar()
min_var_entry = tk.Entry(root, textvariable=min_var, font=('calibre', 10, 'normal'), width=8)
max_var_entry = tk.Entry(root, textvariable=max_var, font=('calibre', 10, 'normal'), width=8)
min_var_entry.insert(tk.INSERT, "0")
max_var_entry.insert(tk.INSERT, "1")
def start_clicking_task(event=None):
global running
running = True
start_clicking()
def stop_clicking(event=None):
text1.insert(tk.INSERT, "\nStopping...\n")
global running
running = False
print("Running is currently ", running)
text1.see("end")
text1.config(state = "disabled")
def force_close(even=None):
start_btn.master.destroy() # This destroys the entire window
def start_clicking_event(event=None):
global running
running = True
def start_clicking(event=None):
global running
running = True
min = min_var.get()
max = max_var.get()
min = float(min)
max = float(max)
if running is True:
random_range = random.uniform(min, max)
random_range_s_to_ms = int(random_range * 1000) # Convert seconds to ms
range_str_s = "{:,.2f}s".format(random_range)
range_str_ms = "{:,.2f}ms".format(random_range_s_to_ms)
pyautogui.click()
root.after(random_range_s_to_ms, start_clicking) # This continues to run
text1.insert(tk.INSERT, "\nTime since last click: " + range_str_s + " (" + range_str_ms + ")\n") # But this stops printing
print("Running is currently ", running)
text1 = tk.Text(root, height=20, width=41, bg = "#A4B0BD")
keyboard.add_hotkey('f2', start_clicking)
keyboard.add_hotkey('f3', stop_clicking)
keyboard.add_hotkey('esc', force_close)
text1.config(state = "normal")
text1.grid(row=1, column=0, rowspan=10,padx=10)
range_label = tk.Label(root, text = "Click Interval\n(seconds)", font=('calibre', 10, 'bold'), bg = "#0A3D62", width=11, fg = "#7ddeff")
min_label = tk.Label(root, text = "Min", font=('calibre', 10, 'bold'), bg = "#0A3D62", width=3, fg = "#7ddeff")
max_label = tk.Label(root, text = "Max", font=('calibre', 10, 'bold'), bg = "#0A3D62", width=3, fg = "#7ddeff")
start_btn = tk.Button(root, text='Start(F2)', bg = "#2C3335", width=7, fg = "#7ddeff", command=start_clicking_event)
stop_btn = tk.Button(root, text='Stop(F3)', bg = "#2C3335", width=7, fg = "#7ddeff", command=stop_clicking)
force_close_btn = tk.Button(root, text='Force close\n(Esc)', bg = "#2C3335", width=8, fg = "#7ddeff", command=force_close)
range_label.grid(row=1, column=2)
min_label.grid(row=2, column=1)
min_var_entry.grid(row=2, column=2)
max_label.grid(row=3, column=1)
max_var_entry.grid(row=3, column=2)
start_btn.grid(row=2, column=3, padx=3)
stop_btn.grid(row=3, column=3, padx=3)
force_close_btn.grid(row=8, column=2)
root.mainloop()
Нет, я уже использую .after в своей программе. Я пытаюсь, чтобы root.after(random_range_s_to_ms, start_clicking)
произошло, когда if running:
правда. Однако, похоже, он просто работает независимо от того, так это или нет. Я тоже пробовал after_cancel раньше, но не смог заставить его работать.
Вы читали ответ на ту тему, где используется after_cancel(id)
? Знаете ли вы, что ваш будильник все еще регистрируется, когда вы делаете это таким образом, и проверяет, нужен он вам или нет? Если так. Почему вы не используете if running:
в функции start_clicking
?
Я снова попытался использовать after_cancel, под def start_clicking(event=None):
добавил global call_click
и call_click = root.after(random_range_s_to_ms, start_clicking)
, а под def stop_clicking(event=None):
добавил stop_clicking.after_cancel(call_click)
. Это неправильно? Это то, что я сделал ранее, и это ничего не останавливает. Кроме того, независимо от того, использую ли я if running
в функции start_clicking
, она просто продолжает щелкать. Помещение root.after(...)
под if running:
, кажется, не влияет на результат для меня.
Я не понимаю, как ваш код вообще работает: потому что вы никогда раньше не определяли running
, поэтому он должен выдавать NameError
. Пожалуйста, предоставьте минимальный воспроизводимый пример
Я предполагаю, что один из ваших модулей, который вы используете, работает с глобальными переменными, и вы перезаписываете его, как с внутренними min
и max
, что, кстати, является плохим поведением. И пока вы его используете, он переопределяет его в соответствии с потребностями модулей.
Все в порядке, чувак, уже очень поздно, и мне нужно поспать. Я ценю помощь, хотя. Я пытался создать минимальный воспроизводимый пример, и это, похоже, вызвало у меня еще больше вопросов, поэтому мне придется взглянуть на это снова, когда я проснусь. Может быть, я разберусь с этим, пока буду работать над созданием минимального примера.
Ваш код вызовет исключение при выполнении start_clicking()
:
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Python38\lib\tkinter\__init__.py", line 1883, in __call__
return self.func(*args)
File "D:\Temp\python\tmp2.py", line 68, in start_clicking
if running: # For some reason, this runs even though running is False. I want this to stop when running is false
NameError: name 'running' is not defined
Поэтому вам нужно инициализировать running
перед выполнением start_clicking()
. Я предлагаю создать еще одну функцию, которая инициализирует running
и выполняет start_clicking()
при нажатии F2
или нажатии кнопки Start
:
def start_clicking_task(event=None):
global running
running = True
start_clicking()
...
keyboard.add_hotkey('f2', start_clicking_task)
...
start_btn = tk.Button(root, text='Start(F2)', bg = "#2C3335", width=7, fg = "#7ddeff", command=start_clicking_task)
Здравствуйте, спасибо за ваш ответ. Я пытался сделать это по-вашему, но я считаю, что уже пробовал это немного по-другому, но безрезультатно. Пока я тестировал это, я печатаю running
в консоли. Один раз за каждый раз start_clicking
повторяется и один раз каждый раз, когда я нажимаю стоп. Кажется, что каждый раз, когда он печатает, логическое условие сообщает True и False соответственно. Однако, несмотря на то, что running
является ложным, «root.after(random_range_s_to_ms, start_clicking)» продолжает работать, несмотря на условие running
.
Я изменил свой полный код на минимальный воспроизводимый пример, возможно, это прояснит мой вопрос. Должно быть что-то, что я неправильно понимаю.
Собственно, пройдя минимальный пример, я понял, что я сделал не так. Вы на самом деле решили это, и я, должно быть, напечатал это прошлой ночью неправильно. Поскольку в start_clicking()
я объявлял global равным true, он каждый раз рекурсивно устанавливал его в true, поэтому он продолжал работать. Спасибо большое.
Отвечает ли это на ваш вопрос? Создать основной цикл с помощью tkinter?