add a helper to wait and show a dialog if needed

use this in snaplist view. cancellation with asyncio is mindbending!
This commit is contained in:
Michael Hudson-Doyle 2020-09-18 21:51:14 +12:00
parent a7bcc7faf0
commit 9b414df0eb
3 changed files with 76 additions and 46 deletions

View File

@ -13,6 +13,7 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import asyncio
import datetime
import logging
@ -246,34 +247,6 @@ class SnapInfoView(WidgetWrap):
return self.pile.render(size, focus)
class FetchingInfo(WidgetWrap):
def __init__(self, parent, snap, aio_loop):
self.parent = parent
self.spinner = Spinner(aio_loop, style='dots')
self.spinner.start()
self.closed = False
text = _("Fetching info for {snap}").format(snap=snap.name)
# | text |
# 12 34
self.width = len(text) + 4
cancel = cancel_btn(label=_("Cancel"), on_press=self.close)
super().__init__(
LineBox(
Pile([
('pack', Text(' ' + text)),
('pack', self.spinner),
('pack', button_pile([cancel])),
])))
def close(self, sender=None):
if self.closed:
return
self.closed = True
self.spinner.stop()
self.parent.remove_overlay()
class FetchingFailed(WidgetWrap):
def __init__(self, row, snap):
@ -294,7 +267,7 @@ class FetchingFailed(WidgetWrap):
def load(self, sender=None):
self.close()
self.row.load_info()
schedule_task(self.row.load_info())
def close(self, sender=None):
if self.closed:
@ -331,25 +304,18 @@ class SnapCheckBox(CheckBox):
on_press=self.parent.show_main_screen)],
focus_buttons=False))
async def wait(self, t, fi):
await t
fi.close()
async def load_info(self):
app = self.parent.controller.app
await app.wait_with_text_dialog(
asyncio.shield(
self.parent.controller.get_snap_info_task(self.snap)),
_("Fetching info for {snap}").format(snap=self.snap.name),
can_cancel=True)
self.loaded()
def load_info(self):
t = self.parent.controller.get_snap_info_task(self.snap)
if t.done():
self.loaded()
return
fi = FetchingInfo(
self.parent, self.snap, self.parent.controller.app.aio_loop)
self.parent.show_overlay(fi, width=fi.width)
schedule_task(self.wait(t, fi))
def keypress(self, size, key):
if key.startswith("enter"):
self.load_info()
schedule_task(self.load_info())
else:
return super().keypress(size, key)

View File

@ -29,6 +29,7 @@ from subiquitycore.palette import (
)
from subiquitycore.screen import make_screen
from subiquitycore.tuicontroller import Skip
from subiquitycore.ui.utils import LoadingDialog
from subiquitycore.ui.frame import SubiquityCoreUI
from subiquitycore.utils import arun_command
@ -161,6 +162,30 @@ class TuiApplication(Application):
def show_progress(self):
raise NotImplementedError
async def wait_with_text_dialog(self, awaitable, message,
*, can_cancel=False):
ld = None
task_to_cancel = None
if can_cancel:
if not isinstance(awaitable, asyncio.Task):
async def w():
return await awaitable
task_to_cancel = self.aio_loop.create_task(w())
else:
task_to_cancel = awaitable
def show_load():
nonlocal ld
ld = LoadingDialog(
self.ui.body, self.aio_loop, message, task_to_cancel)
self.ui.body.show_overlay(ld, width=ld.width)
def hide_load():
ld.close()
await self._wait_with_indication(awaitable, show_load, hide_load)
async def _move_screen(self, increment, coro):
if coro is not None:
await coro

View File

@ -23,6 +23,7 @@ from urwid import (
AttrMap,
CompositeCanvas,
connect_signal,
LineBox,
Padding as _Padding,
SelectableIcon,
Text,
@ -30,8 +31,16 @@ from urwid import (
WidgetDisable,
)
from subiquitycore.ui.buttons import other_btn
from subiquitycore.ui.container import ListBox, Pile
from subiquitycore.ui.buttons import (
cancel_btn,
other_btn,
)
from subiquitycore.ui.container import (
ListBox,
Pile,
WidgetWrap,
)
from subiquitycore.ui.spinner import Spinner
from subiquitycore.ui.stretchy import Stretchy
from subiquitycore.ui.table import TableRow
from subiquitycore.ui.width import widget_width
@ -352,3 +361,33 @@ class SomethingFailed(Stretchy):
def close(self, sender):
self.parent.remove_overlay()
class LoadingDialog(WidgetWrap):
def __init__(self, parent, aio_loop, msg, task_to_cancel):
self.parent = parent
self.spinner = Spinner(aio_loop, style='dots')
self.spinner.start()
self.closed = False
# | text |
# 12 34
self.width = len(msg) + 4
widgets = [
('pack', Text(' ' + msg)),
('pack', self.spinner),
]
if task_to_cancel is not None:
self.task_to_cancel = task_to_cancel
cancel = cancel_btn(label=_("Cancel"), on_press=self.close)
widgets.append(('pack', button_pile([cancel])))
super().__init__(LineBox(Pile(widgets)))
def close(self, sender=None):
if self.closed:
return
if sender is not None:
self.task_to_cancel.cancel()
self.closed = True
self.spinner.stop()
self.parent.remove_overlay()