3

This is part of a longer program. All these methods are within the same class. What this program basically does is that whenever the start button is pressed the program runs within a 30 second loop. During these 30 seconds, every time a touch sensor is touched the mouse gets water for 2 seconds. There's also a 'GiveWater' button that allows the mouse to drink every time I press it.

I used tkinter to create the butttons

However, the button only works when the program is not within the 30 second loop. How can I can the 'GiveWater' button work even while the program is within the loop?

I know there's something about multi-threading but I'm new to python and I don't really know what it is or how it works.

I would greatly appreciate if anyone can explain in simple English how I can use multi-threading for this program or an alternative way to make this program work.

def createStartButton(self):
    """creates a button to start the program"""
    #Whenever button is pressed the 'touchSensor' method is invoked.
    self.StartButton = Button(self, text = 'Run touch sensor', command = self.touchSensor)        
    self.StartButton.grid()

def createGiveWaterButton(self):
    """creates a button that gives water to mouse whenever pressed"""
    #Whenever button is pressed the 'giveWater' method is invoked.
    self.WaterButton = Button(self, text = 'Give water', command = self.giveWater).        
    self.WaterButton.grid()

def giveWater(self):
    #Every time setOutputState(n, True) the output sends a signal..
    #..allowing the mouse to get water until it's set to False.
    self.myObject.setOutputState(0, True)
    time.sleep(0.1)
    self.myObject.setOutputState(0, False)

def touchSensor(self):
    """controls how the touch sensor runs"""

    time_at_program_start = time.time()

    #Every time setOutputState(n, True) it the output sends..
    #..a signal until it's set to False

    while True:
        self.myObject.setOutputState(0, False)

        #If the sensorValue > 900 (i.e. it's touched) the output is True       
        #(i.e. sends a signal allowing the mouse to drink.)
        if self.myObject.getSensorValue(2) >900:
            self.myObject.setOutputState(0, True)
            #waits 2 seconds
            time.sleep(2)
            #sets OutputState to False so the signal stops.
            self.myObject.setOutputState(0, False)

        #checks if the program already runs for 30 seconds.
        #If it does the loop/program stops.
        if time.time() - time_at_program_start >= 30:
            break
pupa
  • 71
  • 9
  • 3
    Don't use `time.sleep()` in `tkinter` applications. Use the `after` method instead. – TigerhawkT3 Jul 10 '15 at 17:06
  • What's the difference? – pupa Jul 10 '15 at 17:09
  • 1
    sleep is blocking... – Eric Levieil Jul 10 '15 at 17:11
  • 1
    `sleep` stops execution of the program for a certain amount of time. [`after`](http://effbot.org/tkinterbook/widget.htm#Tkinter.Widget.after-method) puts a task on the application's "to-do list." – TigerhawkT3 Jul 10 '15 at 17:14
  • See [_Tkinter and multi-threading_](http://stackoverflow.com/questions/15057341/tkinter-and-multi-threading). – martineau Jul 10 '15 at 17:24
  • I'm new to python; i never heard of the 'after' method before. Is 'after' also from the time module? @TigerhawkT3 – pupa Jul 10 '15 at 17:24
  • `after()` is a universal Tkinter widget method. See [this document](http://effbot.org/tkinterbook/widget.htm) and [this one](http://infohost.nmt.edu/tcc/help/pubs/tkinter/web/universal.html). – martineau Jul 10 '15 at 17:28

1 Answers1

2

Replace your touchSensor method with the following two methods and use them instead:

def touchSensor(self):
    self.after_idle(self.touchSensorLoop, time.time())

def touchSensorLoop(self, time_at_program_start):
    self.myObject.setOutputState(0, False)
    if time.time() - time_at_program_start < 30:
        if self.myObject.getSensorValue(2) > 900:
            self.myObject.setOutputState(0, True)
            self.after(2000, self.touchSensorLoop, time_at_program_start)
        else:
            self.after_idle(self.touchSensorLoop, time_at_program_start)
Noctis Skytower
  • 21,433
  • 16
  • 79
  • 117
  • You're assuming `self` has `after()` and `after_idle()` methods. – martineau Jul 10 '15 at 18:14
  • @martineau: It's probably a correct assumption. `self` appears to be a Frame or some other container, because it's used as the first argument when creating buttons... Still, it might be good to spell out that assumption in the answer. – Bryan Oakley Jul 10 '15 at 18:38