Two ModalScreens are shown in the wrong sequence #4926
-
Hi, first of all: I solved my problem with a kind of workaround, but I would like to understand what's going on here. I have to ask the user two questions before my App should show all the Widgets. First the user has to login (username/password). Then - in case of valid login data - the user has to chose out of a list of countries. The second question depends on who logged in, that means one user will get other countries to select from as another user. So I built two ModalScreens: one ( Then in The Problem: The second ModalScreen ( Why? Here my code. It contains two Variants. Variant A is the one I talked about, Variant B see below. I am working with from textual import on, work
from textual.app import App
from textual.containers import Grid
from textual.screen import ModalScreen
from textual.widgets import Button, Input, Label, Select, Static
class DialogLogin(ModalScreen):
"""DialogLogin - Dialog returning (username, password)
"""
CSS = """
DialogLogin {
align: center middle;
}
Grid {
grid-size: 2;
padding: 0 1;
width: 60;
height: 25;
border: thick $background 80%;
background: $surface;
}
Button {
width: 100%;
}
"""
def compose(self):
with Grid():
yield Label('Username:')
yield Input(id='username')
yield Label('Password:')
yield Input(id='password')
yield Button('Cancel', id='cancel')
yield Button('Login', id='login')
def on_button_pressed(self, event):
if event.button.id == 'login':
self.dismiss((
self.query_one('#username').value.strip(),
self.query_one('#password').value.strip()))
else:
self.app.exit()
class DialogSelect(ModalScreen):
"""DialogSelect - Dialog returning one value out of choices
Gets a list of (label, value) pairs, returns one of the values.
Parameter
options options for the Select widget
"""
CSS = """
DialogSelect {
align: center middle;
}
Grid {
grid-size: 2;
padding: 0 1;
width: 60;
height: 25;
border: thick $background 80%;
background: $surface;
}
Select {
width: 100%;
}
"""
def __init__(self, options):
self.options = options
super().__init__()
def compose(self):
with Grid():
yield Static('Make your choice!')
yield Select(self.options, allow_blank=True)
yield Button('Cancel', id='cancel')
@on(Select.Changed)
def selectChanged(self, event):
self.dismiss(event.value)
@on(Button.Pressed)
def buttonPressed(self):
self.app.exit()
class Main(App):
"""An app with two modal dialogs."""
def compose(self):
yield Label('Main App, just to show something.')
def on_mount(self):
#
# Variant A with two workers:
#
self.requestLogin()
countries = (('Germany', 'DE'), ('Great Britan', 'GB'), ('Danmark', 'DK'))
self.selectFromOptions(countries)
#
# Variant B with one worker:
#
# self.loginAndSelectCountry()
def checkLogin(self, loginDaten):
"""checkLogin - in production: checks loginData against a PostgreSQL Database
Here we just return True
"""
return True
#
# Variant A: using two workers:
#
@work
async def requestLogin(self):
loginSuccess = False
while not loginSuccess:
self.loginData = await self.push_screen_wait(DialogLogin())
if self.checkLogin(self.loginData):
loginSuccess = True
self.notify('Login successfull.')
else:
self.notify('Login not successfull.')
@work
async def selectFromOptions(self, options):
self.country = await self.push_screen_wait(DialogSelect(options))
self.notify(f'country: {self.country}')
#
# Variant B: using one worker:
#
@work
async def loginAndSelectCountry(self):
loginSuccess = False
while not loginSuccess:
self.loginData = await self.push_screen_wait(DialogLogin())
if self.checkLogin(self.loginData):
loginSuccess = True
self.notify('Login successfull.')
else:
self.notify('Login not successfull.')
countries = (('Germany', 'DE'), ('Great Britan', 'GB'), ('Danmark', 'DK'))
self.country = await self.push_screen_wait(DialogSelect(countries))
self.notify(f'Country: {self.country}')
if __name__ == "__main__":
app = Main()
app.run() When searching for the error, I suspected that perhaps the two workers were irritating each other. So I reduced the code to one worker, which is shown in Variant B. That solves my problem. But I would like to understand what's going on here. |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment
-
If you invoke the two workers they will run concurrently, i.e. at the same time. You can't guarantee which Your second variant is pushing one screen and waiting for it to return, and then pushing the next screen, thus ensuring the order. |
Beta Was this translation helpful? Give feedback.
If you invoke the two workers they will run concurrently, i.e. at the same time. You can't guarantee which
push_screen_wait
will execute first.Your second variant is pushing one screen and waiting for it to return, and then pushing the next screen, thus ensuring the order.