Reputation: 329
I'm currently working on an application written in Kivy in python. I have 2 multiprocessing.Processes running:
I use queues to communicate with my main thread, and that works great. The problem I'm facing at the moment is I need to introduce a learning process to my Kivy program. I have 4 screens which I call
This is the function that gets called when I push the button inside the initialscreen:
def startLearningProcess(self, lockeramount):
self.lockeramount = int(lockeramount)
with Database(self.databasepath) as db:
db.truncate_table('LOCKERS')
# resetting primary key sequence to make the ids start from 1
db.reset_primary_key_sequence('LOCKERS')
for locker in range(int(lockeramount)):
# use a with statement to automatically close the db after the operations
with Database(self.databasepath) as db:
# inserting the given amount lockers to the database as rows.
db.insertLockerRow(locker+1,'Pieter')
if I add the following to the function, 5 popups get opened all at once:
while self.lockeramount != 0:
popup = Popup(title='Test popup', auto_dismiss=True, content=Label(text='Hello world'), size_hint=(None, None), size=(400, 400))
popup.open()
self.lockeramount -= 1
When I input the number 5 into my interface, I want to have 5 popups to open up for me one by one. How can I make it so when I push a button I open up 1 popup, instead of all 5 at once? I apologize for my grammar, english is not my first language.
EDIT: while John's answer worked perfectly, I was looking for another solution that did not use threading. I solved it by doing the following:
In my class InitialScreen(Screen):
I added 2 variables, a bool that starts out with False (booleanUp
) and a int variable that starts at 0 (lockeramount
).
When I enter my def startLearningProcess
I set the lockeramount
variable to the number I input into my screen. I added an interval to the startLearningProcess function: Clock.schedule_interval(lambda dt: self.schedule_popups(), 1)
. I then added the following functions:
def close_popup(self, instance):
self.booleanUp = False
def schedule_popups(self):
if self.lockeramount > 0 and not self.booleanUp:
print(f'opening up popup {self.lockeramount}')
popup = Popup(title='MyPopup', content=Label(text='Abba ' + str(self.lockeramount)), size_hint=(0.5, 0.5))
popup.bind(on_dismiss=self.close_popup)
self.lockeramount -= 1
self.booleanUp = True
popup.open()
else:
print('not opening another popup')
When I open a new popup, I set the boolean to true, so that with the next interval it won't open another interval. I made an on_dismiss event that resets the variable back to False and bound it to my popup.
Upvotes: 0
Views: 350
Reputation: 38837
You can use a Queue
to make the Popups
wait. Define a custom Popup
that accepts a Queue
in its __init__()
method, and sends something (could even be None
) to the Queue
when it is dismissed. And your loop can use the Queue
to wait for the Popups
to be dismissed.
Here is a custom Popup
that uses a Queue
:
class MyPopup(Popup):
queue = ObjectProperty(None)
def dismiss(self, *_args, **kwargs):
super(MyPopup, self).dismiss(*_args, **kwargs)
if self.queue:
self.queue.put(None)
For this to work, you must run it in another thread. Otherwise, waiting for the Queue
on the main thread will never end, because holding the main thread will prevent the Popup
from dismissing. Here is some code that shows 5 Popups
in succession, one at a time:
def doit(self):
threading.Thread(target=self.popup_thread).start()
def popup_thread(self):
self.queue = Queue()
for i in range(5):
Clock.schedule_once(self.show_popup)
self.queue.get()
def show_popup(self, dt):
popup = MyPopup(title='MyPopup', content=Label(text='Abba ' + str(dt)), size_hint=(0.5, 0.5), queue=self.queue)
popup.open()
To start the Popups
, just call the doit()
method, probably as an action associated with a Button
.
Upvotes: 1