Невозможно остановить после метода в tkinter

Я новичок в питоне и извиняюсь, если решение моего вопроса очевидно. Ни одно из других решений, которые я пробовал, похоже, не сработало, и я был в тупике по этой проблеме почти весь день. Во всяком случае, я пытаюсь создать простой автоматический кликер, который щелкает через случайные промежутки времени с заданным диапазоном. Функционально у меня все работает, но я не могу заставить свою программу перестать щелкать после ее запуска.

Вот моя функция нажатия:

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()


Отвечает ли это на ваш вопрос? Создать основной цикл с помощью tkinter?

Thingamabobs 13.12.2020 09:49

Нет, я уже использую .after в своей программе. Я пытаюсь, чтобы root.after(random_range_s_to_ms, start_clicking) произошло, когда if running: правда. Однако, похоже, он просто работает независимо от того, так это или нет. Я тоже пробовал after_cancel раньше, но не смог заставить его работать.

Hoswoo 13.12.2020 10:04

Вы читали ответ на ту тему, где используется after_cancel(id)? Знаете ли вы, что ваш будильник все еще регистрируется, когда вы делаете это таким образом, и проверяет, нужен он вам или нет? Если так. Почему вы не используете if running: в функции start_clicking?

Thingamabobs 13.12.2020 10:07

Я снова попытался использовать 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:, кажется, не влияет на результат для меня.

Hoswoo 13.12.2020 10:20

Я не понимаю, как ваш код вообще работает: потому что вы никогда раньше не определяли running, поэтому он должен выдавать NameError. Пожалуйста, предоставьте минимальный воспроизводимый пример

Thingamabobs 13.12.2020 10:32

Я предполагаю, что один из ваших модулей, который вы используете, работает с глобальными переменными, и вы перезаписываете его, как с внутренними min и max, что, кстати, является плохим поведением. И пока вы его используете, он переопределяет его в соответствии с потребностями модулей.

Thingamabobs 13.12.2020 10:37

Все в порядке, чувак, уже очень поздно, и мне нужно поспать. Я ценю помощь, хотя. Я пытался создать минимальный воспроизводимый пример, и это, похоже, вызвало у меня еще больше вопросов, поэтому мне придется взглянуть на это снова, когда я проснусь. Может быть, я разберусь с этим, пока буду работать над созданием минимального примера.

Hoswoo 13.12.2020 10:57
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
7
65
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Ваш код вызовет исключение при выполнении 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.

Hoswoo 13.12.2020 18:55

Я изменил свой полный код на минимальный воспроизводимый пример, возможно, это прояснит мой вопрос. Должно быть что-то, что я неправильно понимаю.

Hoswoo 13.12.2020 19:10

Собственно, пройдя минимальный пример, я понял, что я сделал не так. Вы на самом деле решили это, и я, должно быть, напечатал это прошлой ночью неправильно. Поскольку в start_clicking() я объявлял global равным true, он каждый раз рекурсивно устанавливал его в true, поэтому он продолжал работать. Спасибо большое.

Hoswoo 13.12.2020 20:10

Другие вопросы по теме