0

I am making a menu that runs on an LCD screen powered by a Raspberry Pi. I am trying to use the threading module to make the text, on the LCD, update until the menu position changes.

The menu is made up of a list of functions that are called when the menu position is changed. The switch_menu() function is called from outside the class, using an event handler, and is used to call the correct menu function. With some of these functions(item2); I want them to loop, and with others(item1); just display static text. The important thing is that they stop looping when switch_menu() is called again. How can I do this?

(here is a simplified version of my code)

class Menu:
    def __init__(self):
        self.LCD = Adafruit_CharLCD()

        self.m_pos = 0      

        self.items = [self.item1,self.item2]

        self.switch_menu(0)

    def switch_menu(self,operation):   
        # 2. And here I want to stop it.   
        m_pos = self.m_pos

        pos = m_pos
        max_pos = len(self.items) - 1
        m_pos = self.loop_selection(pos,max_pos,operation)

        # 1. Here I want to start looping the function below.
        self.items[m_pos]()

        self.m_pos = m_pos

    def loop_selection(self,pos,max_pos,operation):
        if pos >= max_pos and operation == 1:
            pos = 0
        elif pos <= 0 and operation == -1:
            pos = max_pos
        else:
            pos += operation
        return pos

    def item1(self):
        self.LCD.clear()
        text = "item1"
        self.LCD.message(text)

    def item2(self):
        while True:
            self.LCD.clear()
            text = "item2"
            self.LCD.message(text)
            time.sleep(10)

2 Answers2

0

There are many ways to achieve this, one simple way is to make the while loop in a variable and then set it to False outside the loop (for example, when calling switch_menu) once you want to stop it. Just beware of any race conditions that may be caused, of which I can't talk much more about since I don't know the rest of your code.

  • Thank you for your quick reply. I had thought of this, however, if the function is sleeping when the menu is changed; the program will seem unresponsive. I am also unsure where to put the multi-threading code and how I should use it. – Joe Kendall Mar 08 '16 at 12:22
  • Not directly regarding your question, one little thing you could consider, is in _loop_selection_ try to make advantage of `pos = (pos + 1) % (max_pos + 1)`. – Rodrigo M. Racanicci Mar 08 '16 at 15:32
  • Regarding the multi-threading issue, is triggered a new thread per event that will call _switch_menu_? – Rodrigo M. Racanicci Mar 08 '16 at 15:41
  • Rodrigo: I do not understand how I should use 'pos = (pos + 1) % (max_pos + 1)', could you give me an example? With the multi-threading issue, switch_menu() should stop the previous thread and start a new one. I only need to use multi-threading because I need switch_menu() to be called while item1()/item2() is running. – Joe Kendall Mar 08 '16 at 16:11
0

Typical, I have been trying to get this to work for days and as soon as I post a question; I find the answer.

Here is where I found my answer: Stopping a thread after a certain amount of time

And this is what I did to make it work:

class Menu:
    def __init__(self):
        self.LCD = Adafruit_CharLCD()

        self.m_pos = 0      

        self.items = [self.item1,self.item2]

        self.switch_menu(0)

    def switch_menu(self,operation):  
        try:
            self.t_stop.set()
        except:
            pass 

        m_pos = self.m_pos

        pos = m_pos
        max_pos = len(self.items) - 1
        m_pos = self.loop_selection(pos,max_pos,operation)

        item = self.items[m_pos][0]
        self.t_stop = threading.Event()
        self.t = threading.Thread(target=item,args=(1,self.t_stop))
        self.t.start()

        self.m_pos = m_pos

    def loop_selection(self,pos,max_pos,operation):
        if pos >= max_pos and operation == 1:
            pos = 0
        elif pos <= 0 and operation == -1:
            pos = max_pos
        else:
            pos += operation
        return pos

     def item1(self,arg):
        while not stop_event.is_set():
            text = "item1"
            self.LCD.clear() 
            if not stop_event.is_set(): self.LCD.message(text)
            stop_event.wait(10)

    def item2(self,arg):
        while not stop_event.is_set():
            text = "item2"
            self.LCD.clear()
            if not stop_event.is_set(): self.LCD.message(text)
            stop_event.wait(10)

I used a try/except to bypass the initial execution of switch_menu():

try:
    self.t_stop.set()
except:
    pass

I check the condition a second time as a workaround, to prevent race conditions:

if not stop_event.is_set(): self.LCD.message(text)

And I don't know why I had to pass in an argument when creating a thread, but it gave me errors when I didn't:

self.t = threading.Thread(target=item,args=(1,self.t_stop))

I know it needs some tidying up, but it works. If anyone has a more elegant solution feel free to post it.

Community
  • 1
  • 1