Как планировать выполнение задачи раз в неделю с даты, определенной пользователем с помощью tkinter?

#python #python-3.x #tkinter #scheduled-tasks

Вопрос:

Мне нужно загружать некоторые файлы еженедельно в указанное пользователем время и день, как показано на скриншоте. Я борюсь с интеграцией внутреннего цикла в отношении root.mainloop() . До сих пор я пытался с .after помощью и с помощью такой структуры:

 def scaricaAuto(myDay, myHour, myMinute):
    while True:
    day = datetime.datetime.now().strftime("%A")
    hour = datetime.datetime.now().strftime("%H")
    minute = datetime.datetime.now().strftime("%M")
    if day == myDay and str(int(hour)) == myHour and str(int(minute)) == myMinute:
       DownloadButton.invoke()
       break
    root.after(60000, scaricaAuto(clickedDays.get(), HourSpin.get(), MinSpin.get()))
 

Использование только .after приводит к Python: maximum recursion depth exceeded while calling a Python object тому, что цикл while ведет себя неправильно, когда значение if равно true, он застревает там и продолжает вызывать кнопку загрузки.

Кроме того, OS X показывает, что мой скрипт «не отвечает», когда функция кнопки длится дольше нескольких секунд, есть ли для этого решение?

Заранее спасибо тем, кто нашел время прочитать мой вопрос и, возможно, сможет мне помочь!

Комментарии:

1. вопрос с тем, как вы вызываете функцию, в root.after() вы называете то, что scariaAuto функция возвращает , во-первых, что будет запустить его снова рекурсивно, а затем он никогда не останавливаться и достигать максимальной глубины рекурсии, чтобы передать аргументы, делать это так: root.after(60000, scaricaAuto, clickedDays.get(), HourSpin.get(), MinSpin.get()) однако это потребует вашего скрипта Python, чтобы работать все время , кроме того, что это за while True петля? его там не должно быть, и его также datetime.datetime.now() следует вызывать только один раз. В Windows cmd есть команда schtasks , так что посмотрите ее

2. @Matiiss, если я попытаюсь передать аргументы, как вы сказали, я получу (` root.after(60000, scaricaAuto(), clickedDays.get(), HourSpin.get(), MinSpin.get())#scaricaAuto(clickedDays.get(), HourSpin.get(), MinSpin.get ())) Ошибка типа: scaricaAuto() отсутствуют 3 обязательных позиционных аргумента: «Мой день», «Мой час» и «Моя минута»). Кроме того, цикл while был попыткой избежать использования рекурсии, так как он не работает для меня, потому что я превышаю глубину рекурсии. Спасибо большое!

3. это не так, как я говорил вам передавать аргументы, прочитайте внимательно, это должно быть так: root.after(60000, scaricaAuto, clickedDays.get(), HourSpin.get(), MinSpin.get()) именно так, вам не нужно вызывать функцию, как вы это делаете

4. Боже, большое тебе спасибо, это работает!! Как мне прокомментировать ваше решение? Кстати, datetime.datetime.now() должен быть там каждый раз, когда вызывается функция, потому что она сравнивает текущее время с целевым временем.

5. @вы можете сделать что-то подобное current_time = datetime.datetime.now() , а затем получить доступ current_time , например day = current_time.strftime("%A") , таким образом, вам понадобится только один вызов функции. вам не нужно делать комментарий решением (да это и невозможно), потому что на это, вероятно, уже где-то дан ответ или, по крайней мере, в документации, я просто зашел и указал, где вы ошиблись

Ответ №1:

В вашем коде есть пара проблем. Во-первых, вам нужно удалить while True . Это не служит никакой цели. Tkinter уже выполняет бесконечный цикл посредством mainloop .

Во-вторых, after требуется функция. Вы вызываете функцию и передаете результат after .

Другими словами, это:

 root.after(60000, scaricaAuto(clickedDays.get(), HourSpin.get(), MinSpin.get()))
 

… функционально идентичен этому:

 result = scaricaAuto(clicedDays.get(), HourSpin.get(), MinSpin.get())
root.after(60000, result)
 

Вместо этого вам нужно либо передать в scaricaAuto качестве аргумента, либо создать новую функцию, которая вызывает вашу функцию. В первом случае вы можете передать аргументы функции, передав эти аргументы after .

Например,

 root.after(60000, scaricaAuto, clickedDays.get(), HourSpin.get(), MinSpin.get())
 

Обратите внимание, что это вызывает .get() функции немедленно, а не ждет целых 60 секунд.

Лучшее решение состоит в том, чтобы не передавать никаких аргументов scaricaAuto , а вместо этого вызывать .get() методы правильно, когда ему нужны значения:

 def scaricaAuto():
    myDay = clickedDays.get()
    myHour = HourSpin.get()
    myMinute = MinSpin.get()

    now = datetime.datetime.now()
    day = now.strftime("%A")
    hour = now.strftime("%H")
    minute = now.strftime("%M")

    if day == myDay and str(int(hour)) == myHour and str(int(minute)) == myMinute:
       DownloadButton.invoke()
    root.after(60000, scaricaAuto)
 

Приведенный выше код будет выполняться до тех пор, пока работает сам графический интерфейс, при условии, что вы mainloop в какой-то момент позвонили.

Комментарии:

1. Спасибо! Ваше решение работает 🙂 Я не могу поднять его, потому что я новичок в stackoverflow..

2. @AndreaPython, но вы можете отметить это как правильный ответ

3. datetime.datetime.now() может быть вызван только один раз и значение, присвоенное переменной, чтобы не вызывать ее три раза