为什么python不等待我的功能完成?

时间:2017-03-20 19:48:21

标签: python multithreading function tkinter

所以我正在使用tkinter和Python编写一个应用程序来控制串口上的某些设备。在这种情况下,我控制温度浴,我有一个简单的功能,等待浴缸达到我设置的温度wait_for_temp()。我认为我在下面列出的control_fcn()会等到bc.wait_for_temp()调用完成,但是如果只是遍历control_fcn()的其余部分。谁知道为什么?这是因为我打电话给app.after()吗?

def control_fcn()
  bc = Bath_Controller()

  # set the temperature I want
  set_temp = 16.0
  bc.write_temp(set_temp)

  print("Commanding temp to " + str(set_temp))

  bc.turn_on()

  # wait for bath temp to be within .1 of the commanded temp
  # this function prints out a "Temperature reached. Carry on." statement when done
  bc.wait_for_temp(set_temp)

  print("I didn't wait for you friend.")

# in my Bath_Controller() class I have the following function
def wait_for_temp(self, set_temp):
  current_temp = self.read_temp()
  if (current_temp < set_temp + .05) and (current_temp > set_temp - .05):
    print("Temperature reached. Carry on.")
    return
  else:
    print("Waiting for temperature equilibration.\n Current temp is:")
    # app here is my root window in tkinter
    app.after(10000, lambda: self.wait_for_temp(set_temp))

---更新---

感谢一些有用的答案,我终于得到了一个合理且更易于维护的方法来等待我的串行设备完成他们的任务,但却没有冻结GUI。我不得不画一个小状态机,但万一有人好奇这是我目前的工作代码,处理更换一些压力阀,改变温度,等待温度变化,等待用户从生产线取样一些水,以及为多种温度和压力做这件事。

from collections import deque

def calibrate_master():

    bc = Bath_Controller()
    vc = Valve_Controller()
    sc = Sampling_Controller()

    pressure_queue  = deque([0, 1, 2, 3, 4, 5])
    temp_queue      = deque([6, 4, 2])

    app.ready_for_pres_change   = True  # this starts True, and is reset True after last temp and sampling
    app.ready_for_temp_change   = False # this is set after pres change and after sampling
    app.waiting_for_temp        = False # this is set true when waiting for temp
    app.waiting_for_sample      = False # this is set true after temp reached and before sampling is done

    calibrate_slave(app, bc, vc, pc, pressure_queue, temp_queue)

def calibrate_slave(app, bc, vc, pc, pressure_queue, temp_queue):

    if app.ready_for_pres_change:
        vc.set_pressure(pressure_queue.popleft())
        app.ready_for_pres_change = False
        app.ready_for_temp_change = True
    elif app.ready_for_temp_change:
        bc.set_temp(temp_queue.popleft())
        app.ready_for_temp_change = False
        app.waiting_for_temp = True
    elif app.waiting_for_temp:
        bc.check_temp() # check_temp() will change waiting_for_temp to False and waiting_for_sample to True once temp is met
    elif app.waiting_for_sample:
        sc.check_sampling() # this checks if the user has pressed a button to indicate sampling complete
        if not app.waiting_for_sample:
            if temp_queue: # if there are temps left in the temp queue keep running through at same temp
                app.ready_for_temp_change = True
            elif pressure_queue: # if there are pressures left refill temp queue and move on to next pres
                temp_queue = deque([6, 4, 2])
                app.ready_for_pres_change = True
            else: # if there are not temps and no pressures left the calibration is complete
                break 

    # check back every five seconds
    app.after(5000, lambda: calibrate_slave(app, bc, vc, pc, pressure_queue, temp_queue))

3 个答案:

答案 0 :(得分:4)

等待wait_for_temp完成。但是,wait_for_temp几乎会立即返回。调用after并不会阻止它返回。恰恰相反,调用after允许它无需等待即可返回。

编写GUI与编写非GUI程序不同。 GUI已处于永久等待状态。您需要编写可以响应事件的代码,而不是编写自己等待的函数。

例如,您可以创建一个<<TemperatureReached>>事件,当温度达到给定值时可以触发该事件。然后,您可以绑定要在该事件触发时调用的函数。

它看起来像这样:

def control_fcn()
    bc = Bath_Controller()

    # set the temperature I want
    set_temp = 16.0
    bc.write_temp(set_temp)

    # arrange for notifyUser to be called when the target
    # temperature has been reached
    app.bind("<<TemperatureReached>>", self.notifyUser)

    # periodically check the temp
    self.check_temp(set_temp)

def notifyUser(event):
    print("Temperature reached")

def check_temp(self, set_temp):
    current_temp = self.read_temp()
    if (current_temp < set_temp + .05) and (current_temp > set_temp - .05):
        # temperature reached; send an event
        app.event_generate("<<TemperatureReached>>")
    else:
        # temperature not reached. Check again in 10 seconds
        app.after(10000, self.check_temp, set_temp)

答案 1 :(得分:1)

after方法旨在立即返回。之后的使用点而不是while True:循环是它立即返回并且不会锁定GUI。为了得到你想要的东西你将不得不拆分它:

def control_fcn()
  bc = Bath_Controller()

  # set the temperature I want
  set_temp = 16.0
  bc.write_temp(set_temp)

  print("Commanding temp to " + str(set_temp))

  bc.turn_on()

  # wait for bath temp to be within .1 of the commanded temp
  # this function prints out a "Temperature reached. Carry on." statement when done
  bc.wait_for_temp(set_temp)

def done():
  print("I didn't wait for you friend.")

# in my Bath_Controller() class I have the following function
def wait_for_temp(self, set_temp):
  current_temp = self.read_temp()
  if (current_temp < set_temp + .05) and (current_temp > set_temp - .05):
    print("Temperature reached. Carry on.")
    done()
  else:
    print("Waiting for temperature equilibration.\n Current temp is:")
    # app here is my root window in tkinter
    app.after(10000, lambda: self.wait_for_temp(set_temp))

答案 2 :(得分:-1)

app.after()中,你基本上是说在10秒后运行这段代码,但是你并没有指示处理器与wait 10 seconds结合使用。

简单的解决方案是将函数wait_for_temp()包装在正确的while loop中,并等到你有所需的输出,即:

# in my Bath_Controller() class I have the following function
def wait_for_temp(self, set_temp):
    while 1:
        current_temp = self.read_temp()
        if (current_temp < set_temp + .05) and (current_temp > set_temp - .05):
            print("Temperature reached. Carry on.")
            return
        time.sleep(1)