2015-05-24 3 views
4

Возможно ли в python завершить цикл в функции из другой функции? Это не похоже на работуЗавершить цикл в функции из другой функции в python

Вот мой код:

from tkinter import* 
root = Tk() 
def loop(): 
    global superman 
    superman=False 
    while superman==False: 
     print("It's doing something") 
def endloop(): 
    global superman 
    superman=True 

btn_1 = Button(root, text="stop", command=endloop) 
btn_1.pack() 
btn_2 = Button(root, text="start", command=loop) 
btn_2.pack() 
+1

Вы не * звоните * 'endloop'. – user2357112

+0

Как вы называете обе функции? Предполагается, что в потоках. – bereal

+0

Уверен, но это на самом деле в tkinter и кнопке, вызывающей endloop() –

ответ

2

Проблема здесь состоит в том, что ваш while цикл просто продолжает работать, не означает ни один из остальной части вашего кода никогда не получает бежать. Это включает в себя графический интерфейс Tkinter, что означает, что ваша программа не отвечает на любые пользовательские события, в том числе нажатие кнопки, поэтому endloop никогда не вызывается.

В более общем плане, вы действительно не можете иметь функцию, которая работает только навсегда, или даже более чем на долю секунды, внутри программы GUI. Однопоточная программа может делать только одно за раз; если то, что он делает, зацикливается навсегда, тогда это не делает ничего другого.

Итак, что может вы?

Есть два основных варианта:

  1. Помещенный петлю на фоне потока. Это означает, что любые общие данные теперь должны быть явно синхронизированы, и это означает, что цикл не может касаться каких-либо виджетов GUI, но в вашем случае это оказывается довольно простым.

  2. Разбить петлю. Попробуйте сделать одну итерацию (или, скажем, 100 итераций, если они действительно быстрые), а затем используйте after или after_idle, чтобы попросить Tkinter вызвать функцию, которая выполняет еще одну итерацию (или 100 итераций) и after s снова, и так далее, пока все не закончится.

Я покажу вам, как сделать первый здесь.

import threading 
from tkinter import* 
root = Tk() 

def real_loop(): 
    while True: 
     with superman_lock: 
      if not superman: 
       return 
     print("It's doing something") 
def loop(): 
    global superman 
    global superman_lock 
    superman=False 
    superman_lock = threading.Lock() 
    thread = threading.Thread(target=real_loop, daemon=True) 
def endloop(): 
    global superman 
    with superman_lock: 
     superman=True 

btn_1 = Button(root, text="stop", command=endloop) 
btn_1.pack() 
btn_2 = Button(root, text="start", command=loop) 
btn_2.pack() 

Для случая, когда только общие данные «стоп» флаг, Condition или Event часто лучше, чем Lock. Документы threading объясняют различия между различными типами объектов синхронизации, но не на начальном уровне. Статья Википедии о monitors может стать лучшей отправной точкой для изучения, но если вы можете найти хороший учебник по многопоточности (не обязательно для Python-специфичного; у Python есть в основном те же объекты синхронизации, что и библиотека C pthreads, библиотека C++ Boost, Java stdlib и т. Д.), Что, вероятно, было бы лучше.

Для более подробного обсуждения см. Why your GUI app freezes.

+0

Спасибо за этот ответ странно, моя основная программа не делает кражу tkinter всего, но endloop не убивает цикл. И на самом деле цикл отсчитывает время. –

+3

@ThierryLincoln: Пожалуйста, прочитайте более внимательно. Я не сказал, что это приведет к краху Tkinter, и это не произойдет. То, что он делает, это заставить Tkinter перестать отвечать на события. Это означает, что ваша функция endloop никогда не будет вызвана. Вот почему он не убивает цикл. – abarnert

1

Предположим, что ваша функция loop() выполняет некоторую работу в фоновом режиме, было бы неплохо поместить ее в отдельный поток. Использование потоков событий Вы можете взаимодействовать с потоком.

Этот код не проверял, но это должно дать вам представление о том, как я решил такие вещи в некоторых случаях:

class Worker(threading.Thread): 
    def __init__(self): 
     threading.Thread.__init__() 

     self.run_event = threading.Event() 

    def run(self): 
     self.run_event.wait() 

     while self.run_event.is_set(): 
      print "It's doing something!" 

Вы можете инициализировать поток, он будет вызывать запуск() метод, как только как он запущен и будет ждать в первой строке для запуска run_event.

Так если у вас есть глобальная переменная, которая ссылается на ваш рабочий поток ваш метод цикла() запускается с кнопки будет выглядеть следующим образом:

def loop(): 
    global worker_thread 
    worker_thread.run_event.set() 

, установив run_event на self.run_event.wait() получает и вводится рабочий цикл. Этот цикл while работает до тех пор, пока задано событие потоковой передачи.

И ваш ENDLOOP() может выглядеть примерно так:

dev endloop(): 
    global worker_thread 
    worker_thread.run_event.clear() 

Как только вы очистите run_event в вашем потоке, условие, пока больше не выполняется и цикл завершает работу.

Только примечание: Этот код не является полным и не проверял, но может дать вам представление о том, как это можно сделать. Не забывайте до

import threading 

как есть.

Надеюсь, это немного поможет. Greetz

+0

Спасибо, что ответили на самом деле. Я вчера открыл ничью о моей критической ошибке. Я действительно запутался сейчас, и мне нужно вернуть эту программу завтра :(: http://stackoverflow.com/questions/30413689/exception-with-tkinter -callback-потому-loop-continue-in-the-background? noredirect = 1 –

+0

Проверьте другой вопрос о stackoverflow, который вы здесь связали, я ответил на мои мысли об этом всего минуту назад ;-) – p9teufel

Смежные вопросы