Kivy – popup A shows over popup B

PROBLEM

I have a loop that in every passage displays a popup. I will call it the popup_A. In the loop there is a condition that when is met it triggers together another popup and a method in a thread. This second popup I call popup_B. The problem is that that the popup_B it does show but right after that the popup_A shows over the popup_B, covering it fully. To picture better the get the idea of the flow:

def myFun:     if condition1:         method1     if condition2:         method2     if condition3:         show popup_B         thread method3     thread popup_A  def popup_A:     do something     display message_A     call myFun     def popup_B:     display message_B 

CODE The method involved in the looping:

def goForward(self):         if  self.header == "someTask":             if "tasks" in self.data:    # check if the request has "tasks" in the body                 if self.counter < len(self.data["tasks"]):  # check the counter                     self.code = self.data["tasks"][self.counter].get("code")                     action = self.data["tasks"][self.counter].get("actionDescription")                                          if "myCondition" in str(action):                                                      #set the popup structure                         self.popup_B = ActivityBox(self)                         self.popup_B.open()                                                  # run the method in a thread                         t1 = threading.Thread(target = timeConsumingMethod)                         t1.start()                                          # dismiss the popup ActivityBox                          self.popup_B.dismiss()                      # call popup_A in thread                     t3 = threading.Thread(target = self.popup_A)                     t3.start()                      def do(self):     self.counter = self.counter + 1     self.popup.dismiss()     self.goForward()  def cancel(self):     self.counter = self.counter + 1     self.popup.dismiss()     self.goForward()  def popup_A(self):     self.popup = MessageBox(self)     self.popup.open() 

The ActivityBox and MessageBox popups structure in the Builder.load_string():

<MessageBox>:     size_hint: 1, .7     auto_dismiss: False     title: "MessageBoxTitle"     title_align: "center"     title_size: 30      BoxLayout:         orientation: "vertical"         Label:             font_size: '30sp'             text: "MessageBoxLabel"         BoxLayout:             orientation: "horizontal"             spacing: 10             size_hint: 1, .5             Button:                 font_size: 50                 background_color: 0,204,0,1                 text: "CONFIRM"                 on_press:                     self.disabled = True                     self.background_color = 0,255,0,1                     app.do()                     root.dismiss()             Button:                 font_size: 50                 background_color: 204,0,0,1                 text: "CANCEL"                 on_press:                     self.background_color = 255,0,0,1                     app.cancel()                     root.dismiss()  <ActivityBox>:     size_hint: 1, .7     auto_dismiss: False     title: "ActivityBoxTitle"     title_align: "center"     title_size: 30      BoxLayout:         orientation: "vertical"         Label:             font_size: '30sp'             text: "ActivityBoxLabel"         BoxLayout:             orientation: "horizontal"             spacing: 10             size_hint: 1, .5 

EXPLANATION OF THE CODE The components of the main loop are goForward and the popup_A. With every passage of the loop the popup_A appears, called in a thread. Then the popup_A calls back goForward. If the condition "work" in goForward is met, the "work in progress" popup_B shows up. The popup_B runs together with a method in a thread otherwise Kivy does not show the popup (locking GUI).

RESULTS

So far I have tried with:

  1. Run the popup_B in a thread t1 = threading.Thread(target = self.popup.open): the popup_A covers the popup_B.
  2. Using the thread .join(): the popup_A appears but the popup_B does not, .join() ignores it.
  3. Run the popup_B and the timeConsumingMethod together in a thread: the popup_A appears but the popup_B does not.
  4. Run the timeConsumingMethod as a process: the popup_A appears but the popup_B does not, the program hangs.
  5. Using mutex = threading.Lock() to lock the thread with the popup_B: the popup_A appears over the popup_B. Also the GUI gets garbled.
  6. Run the popup_A not in a thread and run popup_B and the timeConsumingMethod together in a thread: the popup_A appears over the popup_B.

QUESTION

The popup_A can show up only after the method in the thread has finished and the popup_B has been dismissed. How can I prevent the popup_A to cover the popup_B?

I went over the posts below but I did not find a solution.

— UPDATE 20200715 ————————————————

In the code I renamed the popup "work in progress" in popup_B and the popup2 in popup_A for a better understanding.

— UPDATE 20200716 ————————————————-

I modified the code using Clock.schedule_once for step2(thread for popup_A and step3(thread for the timeConsumingMethod and popup_B). The popup_B goes up but the popup_A covers it untill the last popup_A is dismissed with the button. To wait for the timeConsumingMethod to finish without the popup_A to fire up, I use a while loop. The code is below:

def goForward(self):         if  self.header == "someTask":             if "tasks" in self.data:    # check if the request has "tasks" in the body                 if self.counter < len(self.data["tasks"]):  # check the counter                     self.code = self.data["tasks"][self.counter].get("code")                     action = self.data["tasks"][self.counter].get("actionDescription")                                          if "myCondition" in str(action):                         self.returnStatus = 1 # set status                           #the two rows below give the same result                         #self.popup_B = ActivityBox(self)                         #self.popup_B.open()                          Clock.schedule_once(self.step3)                                                  # run the method in a thread                         t1 = threading.Thread(target = self.step1)                         t1.start()                          while self.returnStatus != 0:                             time.sleep(1)                                          Cloch.schedule_once(self.step2)  def step1(self):     ts1 = threading.Thread(target = self.timeConsumingMethod)     ts1.start()     ts1.join()     self.returnStatus = 0 # change status when over     return(self.returnStatus)  def step2(self, *args):     ts2 = threading.Thread(target = self.popup_A)     ts2.start()  def step3(self, *args):          #set the popup structure     self.popup = ActivityBox(self)     self.popup.open()      def popup_A(self):     self.popup = MessageBox(self)     t3 = threading.Thread(target = self.popup.open)     t3.start()  def do(self):     self.counter = self.counter + 1     self.popup.dismiss()     self.goForward()  def cancel(self):     self.counter = self.counter + 1     self.popup.dismiss()     exit() 
  1. Correct way to implement loading popup in kivy app
  2. Kivy: Dismiss One Popup From Another Popup
  3. Kivy Clock and Popup
Add Comment
1 Answer(s)

A way to get the popup_A to not popup until the time consuming thread is finished is to call popup_A at the end of the time consuming thread. Try separating the goForward() method into two parts. The first part remains almost unchanged:

def goForward(self):         if  self.header == "someTask":             if "tasks" in self.data:    # check if the request has "tasks" in the body                 if self.counter < len(self.data["tasks"]):  # check the counter                     self.code = self.data["tasks"][self.counter].get("code")                     action = self.data["tasks"][self.counter].get("actionDescription")                                          if "myCondition" in str(action):                                                      #set the popup structure                         self.popup_B = ActivityBox(self)                         self.popup_B.open()                                                  # run method in a thread                         t1 = threading.Thread(target = self.timeConsumingMethod)                         t1.start() 

I have moved the timeConsumingMethod() into the class, so it needs a call as self.timeConsumingMethod in the Thread.

Then place the rest of the old goForward() method in a separate method (I call it step2()):

def step2(self, *args):     # dismiss the popup ActivityBox     self.popup_B.dismiss()      # call popup_A in thread     t3 = threading.Thread(target=self.popup_A)     t3.start() 

Then in the timeConsumingMethod(), call step2() when it finishes:

def timeConsumingMethod(self):     time.sleep(5)     Clock.schedule_once(self.step2) 
Answered on July 16, 2020.
Add Comment

Your Answer

By posting your answer, you agree to the privacy policy and terms of service.