feat: abandon UserControl
as parent class for custom controls
since that is now deprecated; see also - https://flet.dev/blog/flet-fastapi-and-async-api-improvements#custom-controls-api-normalized - https://flet.dev/docs/getting-started/custom-controls/ also, the docs now have a good "counter" example which inspired a change to the timestamp control, hopefully this means smarter thread management and no more event loop errors..?
This commit is contained in:
parent
2a835d9bcb
commit
1df3327d9b
|
@ -1,58 +0,0 @@
|
||||||
# -*- coding: utf-8; -*-
|
|
||||||
################################################################################
|
|
||||||
#
|
|
||||||
# WuttaPOS -- Pythonic Point of Sale System
|
|
||||||
# Copyright © 2023 Lance Edgar
|
|
||||||
#
|
|
||||||
# This file is part of WuttaPOS.
|
|
||||||
#
|
|
||||||
# WuttaPOS is free software: you can redistribute it and/or modify it under the
|
|
||||||
# terms of the GNU General Public License as published by the Free Software
|
|
||||||
# Foundation, either version 3 of the License, or (at your option) any later
|
|
||||||
# version.
|
|
||||||
#
|
|
||||||
# WuttaPOS is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
||||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
|
||||||
# details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License along with
|
|
||||||
# WuttaPOS. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
#
|
|
||||||
################################################################################
|
|
||||||
"""
|
|
||||||
WuttaPOS - custom controls (base class)
|
|
||||||
"""
|
|
||||||
|
|
||||||
import flet as ft
|
|
||||||
|
|
||||||
from wuttapos.util import make_button, show_snackbar
|
|
||||||
|
|
||||||
|
|
||||||
class WuttaControl(ft.UserControl):
|
|
||||||
|
|
||||||
def __init__(self, config, page=None, *args, **kwargs):
|
|
||||||
self.on_reset = kwargs.pop('on_reset', None)
|
|
||||||
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
self.config = config
|
|
||||||
self.app = config.get_app()
|
|
||||||
self.enum = self.app.enum
|
|
||||||
|
|
||||||
# TODO: why must we save this aside from self.page ?
|
|
||||||
# but sometimes self.page gets set to None, so we must..
|
|
||||||
self.mypage = page
|
|
||||||
|
|
||||||
def informed_refresh(self, **kwargs):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def make_button(self, *args, **kwargs):
|
|
||||||
return make_button(*args, **kwargs)
|
|
||||||
|
|
||||||
def reset(self, e=None):
|
|
||||||
if self.on_reset:
|
|
||||||
self.on_reset(e=e)
|
|
||||||
|
|
||||||
def show_snackbar(self, text, bgcolor='yellow'):
|
|
||||||
show_snackbar(self.mypage, text, bgcolor=bgcolor)
|
|
|
@ -2,7 +2,7 @@
|
||||||
################################################################################
|
################################################################################
|
||||||
#
|
#
|
||||||
# WuttaPOS -- Pythonic Point of Sale System
|
# WuttaPOS -- Pythonic Point of Sale System
|
||||||
# Copyright © 2023 Lance Edgar
|
# Copyright © 2023-2024 Lance Edgar
|
||||||
#
|
#
|
||||||
# This file is part of WuttaPOS.
|
# This file is part of WuttaPOS.
|
||||||
#
|
#
|
||||||
|
@ -28,34 +28,46 @@ import time
|
||||||
|
|
||||||
import flet as ft
|
import flet as ft
|
||||||
|
|
||||||
from .base import WuttaControl
|
|
||||||
from .keyboard import WuttaKeyboard
|
from .keyboard import WuttaKeyboard
|
||||||
|
from wuttapos.util import show_snackbar
|
||||||
|
|
||||||
|
|
||||||
class WuttaFeedback(WuttaControl):
|
class WuttaFeedback(ft.Container):
|
||||||
|
|
||||||
default_font_size = 20
|
default_font_size = 20
|
||||||
default_button_height = 60
|
default_button_height = 60
|
||||||
|
|
||||||
def __init__(self, config, *args, **kwargs):
|
def __init__(self, config, page=None, *args, **kwargs):
|
||||||
|
self.on_reset = kwargs.pop('on_reset', None)
|
||||||
self.on_send = kwargs.pop('on_send', None)
|
self.on_send = kwargs.pop('on_send', None)
|
||||||
self.on_cancel = kwargs.pop('on_cancel', None)
|
self.on_cancel = kwargs.pop('on_cancel', None)
|
||||||
|
|
||||||
super().__init__(config, *args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
def build(self):
|
self.config = config
|
||||||
|
self.app = config.get_app()
|
||||||
|
self.enum = self.app.enum
|
||||||
|
|
||||||
self.button = ft.Container(content=ft.Text("Feedback", size=self.default_font_size,
|
# TODO: why must we save this aside from self.page ?
|
||||||
weight=ft.FontWeight.BOLD),
|
# but sometimes self.page gets set to None, so we must..
|
||||||
height=self.default_button_height,
|
self.mypage = page
|
||||||
width=self.default_button_height * 3,
|
|
||||||
on_click=self.initial_click,
|
|
||||||
alignment=ft.alignment.center,
|
|
||||||
border=ft.border.all(1, 'black'),
|
|
||||||
border_radius=ft.border_radius.all(5),
|
|
||||||
bgcolor='blue')
|
|
||||||
|
|
||||||
return self.button
|
self.content = ft.Text("Feedback", size=self.default_font_size,
|
||||||
|
weight=ft.FontWeight.BOLD)
|
||||||
|
self.height = self.default_button_height
|
||||||
|
self.width = self.default_button_height * 3
|
||||||
|
self.on_click = self.initial_click
|
||||||
|
self.alignment = ft.alignment.center
|
||||||
|
self.border = ft.border.all(1, 'black')
|
||||||
|
self.border_radius = ft.border_radius.all(5)
|
||||||
|
self.bgcolor = 'blue'
|
||||||
|
|
||||||
|
def informed_refresh(self, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def reset(self, e=None):
|
||||||
|
if self.on_reset:
|
||||||
|
self.on_reset(e=e)
|
||||||
|
|
||||||
def initial_click(self, e):
|
def initial_click(self, e):
|
||||||
|
|
||||||
|
@ -155,7 +167,7 @@ class WuttaFeedback(WuttaControl):
|
||||||
})
|
})
|
||||||
|
|
||||||
self.dlg.open = False
|
self.dlg.open = False
|
||||||
self.show_snackbar("MESSAGE WAS SENT", bgcolor='green')
|
show_snackbar(self.mypage, "MESSAGE WAS SENT", bgcolor='green')
|
||||||
self.mypage.update()
|
self.mypage.update()
|
||||||
|
|
||||||
if self.on_send:
|
if self.on_send:
|
||||||
|
|
|
@ -29,18 +29,23 @@ import rattail
|
||||||
import flet as ft
|
import flet as ft
|
||||||
|
|
||||||
import wuttapos
|
import wuttapos
|
||||||
from .base import WuttaControl
|
|
||||||
from .timestamp import WuttaTimestamp
|
from .timestamp import WuttaTimestamp
|
||||||
from .feedback import WuttaFeedback
|
from .feedback import WuttaFeedback
|
||||||
|
from wuttapos.util import make_button
|
||||||
|
|
||||||
|
|
||||||
class WuttaHeader(WuttaControl):
|
class WuttaHeader(ft.Stack):
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, config, page=None, *args, **kwargs):
|
||||||
self.terminal_id = kwargs.pop('terminal_id', None)
|
self.terminal_id = kwargs.pop('terminal_id', None)
|
||||||
|
self.on_reset = kwargs.pop('on_reset', None)
|
||||||
|
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
def build(self):
|
self.config = config
|
||||||
|
self.app = config.get_app()
|
||||||
|
self.enum = self.app.enum
|
||||||
|
|
||||||
self.txn_display = ft.Text("Txn: N", weight=ft.FontWeight.BOLD, size=20)
|
self.txn_display = ft.Text("Txn: N", weight=ft.FontWeight.BOLD, size=20)
|
||||||
self.cust_display = ft.Text("Cust: N", weight=ft.FontWeight.BOLD, size=20)
|
self.cust_display = ft.Text("Cust: N", weight=ft.FontWeight.BOLD, size=20)
|
||||||
|
|
||||||
|
@ -56,51 +61,52 @@ class WuttaHeader(WuttaControl):
|
||||||
terminal_style.bgcolor = 'red'
|
terminal_style.bgcolor = 'red'
|
||||||
terminal_style.color = 'white'
|
terminal_style.color = 'white'
|
||||||
|
|
||||||
return ft.Stack(
|
self.controls = [
|
||||||
controls=[
|
ft.Container(
|
||||||
ft.Container(
|
content=ft.Row(
|
||||||
content=ft.Row(
|
|
||||||
[
|
|
||||||
ft.Container(
|
|
||||||
content=self.training_mode,
|
|
||||||
bgcolor='yellow',
|
|
||||||
),
|
|
||||||
],
|
|
||||||
alignment=ft.MainAxisAlignment.CENTER,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
ft.Row(
|
|
||||||
[
|
[
|
||||||
ft.Row(
|
ft.Container(
|
||||||
[
|
content=self.training_mode,
|
||||||
self.txn_display,
|
bgcolor='yellow',
|
||||||
ft.VerticalDivider(),
|
|
||||||
self.cust_display,
|
|
||||||
ft.VerticalDivider(),
|
|
||||||
WuttaTimestamp(self.config, weight=ft.FontWeight.BOLD, size=20),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
ft.Row(
|
|
||||||
[
|
|
||||||
self.user_display,
|
|
||||||
ft.VerticalDivider(),
|
|
||||||
self.logout_button,
|
|
||||||
self.logout_divider,
|
|
||||||
ft.Text(
|
|
||||||
spans=[
|
|
||||||
ft.TextSpan(style=terminal_style, text=f"Term: {self.terminal_id or '??'}"),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
ft.VerticalDivider(),
|
|
||||||
self.title_button,
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
alignment=ft.MainAxisAlignment.SPACE_BETWEEN,
|
alignment=ft.MainAxisAlignment.CENTER,
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
self.txn_display,
|
||||||
|
ft.VerticalDivider(),
|
||||||
|
self.cust_display,
|
||||||
|
ft.VerticalDivider(),
|
||||||
|
WuttaTimestamp(self.config, weight=ft.FontWeight.BOLD, size=20),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
self.user_display,
|
||||||
|
ft.VerticalDivider(),
|
||||||
|
self.logout_button,
|
||||||
|
self.logout_divider,
|
||||||
|
ft.Text(
|
||||||
|
spans=[
|
||||||
|
ft.TextSpan(style=terminal_style, text=f"Term: {self.terminal_id or '??'}"),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
ft.VerticalDivider(),
|
||||||
|
self.title_button,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
alignment=ft.MainAxisAlignment.SPACE_BETWEEN,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
],
|
def reset(self, e=None):
|
||||||
)
|
if self.on_reset:
|
||||||
|
self.on_reset(e=e)
|
||||||
|
|
||||||
def did_mount(self):
|
def did_mount(self):
|
||||||
self.informed_refresh()
|
self.informed_refresh()
|
||||||
|
@ -188,11 +194,11 @@ WuttaPOS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
has_perm = auth.has_permission(session, user, 'pos.test_error')
|
has_perm = auth.has_permission(session, user, 'pos.test_error')
|
||||||
session.close()
|
session.close()
|
||||||
if has_perm:
|
if has_perm:
|
||||||
test_error = self.make_button("TEST ERROR", font_size=24,
|
test_error = make_button("TEST ERROR", font_size=24,
|
||||||
height=60,
|
height=60,
|
||||||
width=60 * 3,
|
width=60 * 3,
|
||||||
bgcolor='red',
|
bgcolor='red',
|
||||||
on_click=self.test_error_click)
|
on_click=self.test_error_click)
|
||||||
buttons.append(test_error)
|
buttons.append(test_error)
|
||||||
|
|
||||||
feedback = WuttaFeedback(self.config, page=self.page,
|
feedback = WuttaFeedback(self.config, page=self.page,
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
################################################################################
|
################################################################################
|
||||||
#
|
#
|
||||||
# WuttaPOS -- Pythonic Point of Sale System
|
# WuttaPOS -- Pythonic Point of Sale System
|
||||||
# Copyright © 2023 Lance Edgar
|
# Copyright © 2023-2024 Lance Edgar
|
||||||
#
|
#
|
||||||
# This file is part of WuttaPOS.
|
# This file is part of WuttaPOS.
|
||||||
#
|
#
|
||||||
|
@ -26,20 +26,23 @@ WuttaPOS - keyboard control
|
||||||
|
|
||||||
import flet as ft
|
import flet as ft
|
||||||
|
|
||||||
from .base import WuttaControl
|
|
||||||
|
|
||||||
|
class WuttaKeyboard(ft.Container):
|
||||||
class WuttaKeyboard(WuttaControl):
|
|
||||||
|
|
||||||
default_font_size = 20
|
default_font_size = 20
|
||||||
default_button_size = 80
|
default_button_size = 80
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, config, page=None, *args, **kwargs):
|
||||||
|
self.on_reset = kwargs.pop('on_reset', None)
|
||||||
self.on_keypress = kwargs.pop('on_keypress', None)
|
self.on_keypress = kwargs.pop('on_keypress', None)
|
||||||
self.on_long_backspace = kwargs.pop('on_long_backspace', None)
|
self.on_long_backspace = kwargs.pop('on_long_backspace', None)
|
||||||
|
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
self.config = config
|
||||||
|
self.app = config.get_app()
|
||||||
|
self.enum = self.app.enum
|
||||||
|
|
||||||
self.caps_lock = False
|
self.caps_lock = False
|
||||||
self.caps_map = dict([(k, k.upper())
|
self.caps_map = dict([(k, k.upper())
|
||||||
for k in 'abcdefghijklmnopqrstuvwxyz'])
|
for k in 'abcdefghijklmnopqrstuvwxyz'])
|
||||||
|
@ -69,6 +72,63 @@ class WuttaKeyboard(WuttaControl):
|
||||||
'/': '?',
|
'/': '?',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.keys = {}
|
||||||
|
|
||||||
|
def make_key(key, data=None,
|
||||||
|
on_click=self.simple_keypress,
|
||||||
|
on_long_press=None,
|
||||||
|
width=self.default_button_size,
|
||||||
|
bgcolor=None):
|
||||||
|
button = ft.Container(content=ft.Text(key, size=self.default_font_size,
|
||||||
|
weight=ft.FontWeight.BOLD),
|
||||||
|
data=data or key,
|
||||||
|
height=self.default_button_size,
|
||||||
|
width=width,
|
||||||
|
on_click=on_click,
|
||||||
|
on_long_press=on_long_press,
|
||||||
|
alignment=ft.alignment.center,
|
||||||
|
border=ft.border.all(1, 'black'),
|
||||||
|
border_radius=ft.border_radius.all(5),
|
||||||
|
bgcolor=bgcolor,
|
||||||
|
)
|
||||||
|
self.keys[key] = button
|
||||||
|
return button
|
||||||
|
|
||||||
|
def caps_click(e):
|
||||||
|
self.update_caps_lock(not self.caps_lock)
|
||||||
|
|
||||||
|
self.caps_key = make_key('CAPS', on_click=caps_click)
|
||||||
|
if self.caps_lock:
|
||||||
|
self.caps_key.bgcolor = 'blue'
|
||||||
|
|
||||||
|
def shift_click(e):
|
||||||
|
self.update_shift(not self.shift)
|
||||||
|
|
||||||
|
self.shift_key = make_key('SHIFT', on_click=shift_click)
|
||||||
|
if self.shift:
|
||||||
|
self.shift_key.bgcolor = 'blue'
|
||||||
|
|
||||||
|
rows = [
|
||||||
|
[make_key(k) for k in "`1234567890-="] + [make_key('⌫', bgcolor='yellow',
|
||||||
|
on_long_press=self.long_backspace)],
|
||||||
|
[make_key(k) for k in "qwertyuiop[]\\"],
|
||||||
|
[self.caps_key] + [make_key(k) for k in "asdfghjkl;'"] + [make_key('⏎', bgcolor='blue')],
|
||||||
|
[self.shift_key] + [make_key(k) for k in "zxcvbnm,./"],
|
||||||
|
[make_key('SPACE', width=self.default_button_size * 5)],
|
||||||
|
]
|
||||||
|
|
||||||
|
rows = [ft.Row(controls, alignment=ft.MainAxisAlignment.CENTER)
|
||||||
|
for controls in rows]
|
||||||
|
|
||||||
|
self.content = ft.Column(rows)
|
||||||
|
|
||||||
|
def informed_refresh(self, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def reset(self, e=None):
|
||||||
|
if self.on_reset:
|
||||||
|
self.on_reset(e=e)
|
||||||
|
|
||||||
def update_caps_lock(self, caps_lock):
|
def update_caps_lock(self, caps_lock):
|
||||||
self.caps_lock = caps_lock
|
self.caps_lock = caps_lock
|
||||||
|
|
||||||
|
@ -131,58 +191,3 @@ class WuttaKeyboard(WuttaControl):
|
||||||
def long_backspace(self, e):
|
def long_backspace(self, e):
|
||||||
if self.on_long_backspace:
|
if self.on_long_backspace:
|
||||||
self.on_long_backspace()
|
self.on_long_backspace()
|
||||||
|
|
||||||
def build(self):
|
|
||||||
self.keys = {}
|
|
||||||
|
|
||||||
def make_key(key, data=None,
|
|
||||||
on_click=self.simple_keypress,
|
|
||||||
on_long_press=None,
|
|
||||||
width=self.default_button_size,
|
|
||||||
bgcolor=None):
|
|
||||||
button = ft.Container(content=ft.Text(key, size=self.default_font_size,
|
|
||||||
weight=ft.FontWeight.BOLD),
|
|
||||||
data=data or key,
|
|
||||||
height=self.default_button_size,
|
|
||||||
width=width,
|
|
||||||
on_click=on_click,
|
|
||||||
on_long_press=on_long_press,
|
|
||||||
alignment=ft.alignment.center,
|
|
||||||
border=ft.border.all(1, 'black'),
|
|
||||||
border_radius=ft.border_radius.all(5),
|
|
||||||
bgcolor=bgcolor,
|
|
||||||
)
|
|
||||||
self.keys[key] = button
|
|
||||||
return button
|
|
||||||
|
|
||||||
def caps_click(e):
|
|
||||||
self.update_caps_lock(not self.caps_lock)
|
|
||||||
|
|
||||||
self.caps_key = make_key('CAPS', on_click=caps_click)
|
|
||||||
if self.caps_lock:
|
|
||||||
self.caps_key.bgcolor = 'blue'
|
|
||||||
|
|
||||||
def shift_click(e):
|
|
||||||
self.update_shift(not self.shift)
|
|
||||||
|
|
||||||
self.shift_key = make_key('SHIFT', on_click=shift_click)
|
|
||||||
if self.shift:
|
|
||||||
self.shift_key.bgcolor = 'blue'
|
|
||||||
|
|
||||||
rows = [
|
|
||||||
[make_key(k) for k in "`1234567890-="] + [make_key('⌫', bgcolor='yellow',
|
|
||||||
on_long_press=self.long_backspace)],
|
|
||||||
[make_key(k) for k in "qwertyuiop[]\\"],
|
|
||||||
[self.caps_key] + [make_key(k) for k in "asdfghjkl;'"] + [make_key('⏎', bgcolor='blue')],
|
|
||||||
[self.shift_key] + [make_key(k) for k in "zxcvbnm,./"],
|
|
||||||
[make_key('SPACE', width=self.default_button_size * 5)],
|
|
||||||
]
|
|
||||||
|
|
||||||
rows = [ft.Row(controls, alignment=ft.MainAxisAlignment.CENTER)
|
|
||||||
for controls in rows]
|
|
||||||
|
|
||||||
return ft.Container(
|
|
||||||
content=ft.Column(
|
|
||||||
rows,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
################################################################################
|
################################################################################
|
||||||
#
|
#
|
||||||
# WuttaPOS -- Pythonic Point of Sale System
|
# WuttaPOS -- Pythonic Point of Sale System
|
||||||
# Copyright © 2023 Lance Edgar
|
# Copyright © 2023-2024 Lance Edgar
|
||||||
#
|
#
|
||||||
# This file is part of WuttaPOS.
|
# This file is part of WuttaPOS.
|
||||||
#
|
#
|
||||||
|
@ -26,14 +26,15 @@ WuttaPOS - login form control
|
||||||
|
|
||||||
import flet as ft
|
import flet as ft
|
||||||
|
|
||||||
from .base import WuttaControl
|
|
||||||
from .keyboard import WuttaKeyboard
|
from .keyboard import WuttaKeyboard
|
||||||
from .tenkey import WuttaTenkeyMenu
|
from .tenkey import WuttaTenkeyMenu
|
||||||
|
from wuttapos.util import make_button
|
||||||
|
|
||||||
|
|
||||||
class WuttaLoginForm(WuttaControl):
|
class WuttaLoginForm(ft.Column):
|
||||||
|
|
||||||
def __init__(self, config, *args, **kwargs):
|
def __init__(self, config, page=None, *args, **kwargs):
|
||||||
|
self.on_reset = kwargs.pop('on_reset', None)
|
||||||
|
|
||||||
# permission to be checked for login to succeed
|
# permission to be checked for login to succeed
|
||||||
self.perm_required = kwargs.pop('perm_required', 'pos.ring_sales')
|
self.perm_required = kwargs.pop('perm_required', 'pos.ring_sales')
|
||||||
|
@ -57,17 +58,21 @@ class WuttaLoginForm(WuttaControl):
|
||||||
self.on_authz_failure = kwargs.pop('on_authz_failure', None)
|
self.on_authz_failure = kwargs.pop('on_authz_failure', None)
|
||||||
self.on_login_success = kwargs.pop('on_login_success', None)
|
self.on_login_success = kwargs.pop('on_login_success', None)
|
||||||
|
|
||||||
super().__init__(config, *args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
self.config = config
|
||||||
|
self.app = config.get_app()
|
||||||
|
self.enum = self.app.enum
|
||||||
|
|
||||||
# track which login input has focus
|
# track which login input has focus
|
||||||
self.focused = None
|
self.focused = None
|
||||||
|
|
||||||
def build(self):
|
|
||||||
|
|
||||||
login_form = self.build_login_form()
|
login_form = self.build_login_form()
|
||||||
|
self.expand = True
|
||||||
|
self.alignment = ft.MainAxisAlignment.CENTER
|
||||||
|
|
||||||
if self.use_tenkey:
|
if self.use_tenkey:
|
||||||
controls = [
|
self.controls = [
|
||||||
ft.Row(
|
ft.Row(
|
||||||
[
|
[
|
||||||
login_form,
|
login_form,
|
||||||
|
@ -81,7 +86,7 @@ class WuttaLoginForm(WuttaControl):
|
||||||
]
|
]
|
||||||
|
|
||||||
else: # full keyboard
|
else: # full keyboard
|
||||||
controls = [
|
self.controls = [
|
||||||
login_form,
|
login_form,
|
||||||
ft.Row(),
|
ft.Row(),
|
||||||
ft.Row(),
|
ft.Row(),
|
||||||
|
@ -90,9 +95,12 @@ class WuttaLoginForm(WuttaControl):
|
||||||
on_long_backspace=self.keyboard_long_backspace),
|
on_long_backspace=self.keyboard_long_backspace),
|
||||||
]
|
]
|
||||||
|
|
||||||
return ft.Column(controls=controls,
|
def informed_refresh(self, **kwargs):
|
||||||
expand=True,
|
pass
|
||||||
alignment=ft.MainAxisAlignment.CENTER)
|
|
||||||
|
def reset(self, e=None):
|
||||||
|
if self.on_reset:
|
||||||
|
self.on_reset(e=e)
|
||||||
|
|
||||||
def build_login_form(self):
|
def build_login_form(self):
|
||||||
form_fields = []
|
form_fields = []
|
||||||
|
@ -113,16 +121,16 @@ class WuttaLoginForm(WuttaControl):
|
||||||
|
|
||||||
form_fields.append(self.password)
|
form_fields.append(self.password)
|
||||||
|
|
||||||
login_button = self.make_button("Login",
|
login_button = make_button("Login",
|
||||||
height=60,
|
height=60,
|
||||||
width=60 * 2.5,
|
width=60 * 2.5,
|
||||||
bgcolor='blue',
|
bgcolor='blue',
|
||||||
on_click=self.attempt_login)
|
on_click=self.attempt_login)
|
||||||
|
|
||||||
reset_button = self.make_button("Clear",
|
reset_button = make_button("Clear",
|
||||||
height=60,
|
height=60,
|
||||||
width=60 * 2.5,
|
width=60 * 2.5,
|
||||||
on_click=self.clear_login)
|
on_click=self.clear_login)
|
||||||
|
|
||||||
if self.use_tenkey:
|
if self.use_tenkey:
|
||||||
form_fields.extend([
|
form_fields.extend([
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
################################################################################
|
################################################################################
|
||||||
#
|
#
|
||||||
# WuttaPOS -- Pythonic Point of Sale System
|
# WuttaPOS -- Pythonic Point of Sale System
|
||||||
# Copyright © 2023 Lance Edgar
|
# Copyright © 2023-2024 Lance Edgar
|
||||||
#
|
#
|
||||||
# This file is part of WuttaPOS.
|
# This file is part of WuttaPOS.
|
||||||
#
|
#
|
||||||
|
@ -26,11 +26,11 @@ WuttaPOS - base lookup control
|
||||||
|
|
||||||
import flet as ft
|
import flet as ft
|
||||||
|
|
||||||
from .base import WuttaControl
|
|
||||||
from .keyboard import WuttaKeyboard
|
from .keyboard import WuttaKeyboard
|
||||||
|
from wuttapos.util import make_button
|
||||||
|
|
||||||
|
|
||||||
class WuttaLookup(WuttaControl):
|
class WuttaLookup(ft.Container):
|
||||||
|
|
||||||
default_font_size = 40
|
default_font_size = 40
|
||||||
font_size = default_font_size * 0.8
|
font_size = default_font_size * 0.8
|
||||||
|
@ -39,7 +39,8 @@ class WuttaLookup(WuttaControl):
|
||||||
|
|
||||||
long_scroll_delta = 500
|
long_scroll_delta = 500
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, config, page=None, *args, **kwargs):
|
||||||
|
self.on_reset = kwargs.pop('on_reset', None)
|
||||||
self.show_search = kwargs.pop('show_search', True)
|
self.show_search = kwargs.pop('show_search', True)
|
||||||
self.initial_search = kwargs.pop('initial_search', None)
|
self.initial_search = kwargs.pop('initial_search', None)
|
||||||
self.allow_empty_query = kwargs.pop('allow_empty_query', False)
|
self.allow_empty_query = kwargs.pop('allow_empty_query', False)
|
||||||
|
@ -48,12 +49,14 @@ class WuttaLookup(WuttaControl):
|
||||||
|
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
self.config = config
|
||||||
|
self.app = config.get_app()
|
||||||
|
self.enum = self.app.enum
|
||||||
|
|
||||||
# track current selection
|
# track current selection
|
||||||
self.selected_uuid = None
|
self.selected_uuid = None
|
||||||
self.selected_datarow = None
|
self.selected_datarow = None
|
||||||
|
|
||||||
def build(self):
|
|
||||||
|
|
||||||
self.search_results = ft.DataTable(
|
self.search_results = ft.DataTable(
|
||||||
columns=[ft.DataColumn(self.make_cell_text(text))
|
columns=[ft.DataColumn(self.make_cell_text(text))
|
||||||
for text in self.get_results_columns()],
|
for text in self.get_results_columns()],
|
||||||
|
@ -64,27 +67,27 @@ class WuttaLookup(WuttaControl):
|
||||||
weight=ft.FontWeight.BOLD,
|
weight=ft.FontWeight.BOLD,
|
||||||
visible=False)
|
visible=False)
|
||||||
|
|
||||||
self.select_button = self.make_button("Select",
|
self.select_button = make_button("Select",
|
||||||
font_size=self.font_size * 0.8,
|
font_size=self.font_size * 0.8,
|
||||||
height=self.default_button_height_dlg * 0.8,
|
height=self.default_button_height_dlg * 0.8,
|
||||||
width=self.default_button_height_dlg * 1.3,
|
width=self.default_button_height_dlg * 1.3,
|
||||||
on_click=self.select_click,
|
on_click=self.select_click,
|
||||||
disabled=True,
|
disabled=True,
|
||||||
bgcolor=self.disabled_bgcolor)
|
bgcolor=self.disabled_bgcolor)
|
||||||
|
|
||||||
self.up_button = self.make_button("↑",
|
self.up_button = make_button("↑",
|
||||||
font_size=self.font_size,
|
font_size=self.font_size,
|
||||||
height=self.default_button_height_dlg,
|
height=self.default_button_height_dlg,
|
||||||
width=self.default_button_height_dlg,
|
width=self.default_button_height_dlg,
|
||||||
on_click=self.up_click,
|
on_click=self.up_click,
|
||||||
on_long_press=self.up_longpress)
|
on_long_press=self.up_longpress)
|
||||||
|
|
||||||
self.down_button = self.make_button("↓",
|
self.down_button = make_button("↓",
|
||||||
font_size=self.font_size,
|
font_size=self.font_size,
|
||||||
height=self.default_button_height_dlg,
|
height=self.default_button_height_dlg,
|
||||||
width=self.default_button_height_dlg,
|
width=self.default_button_height_dlg,
|
||||||
on_click=self.down_click,
|
on_click=self.down_click,
|
||||||
on_long_press=self.down_longpress)
|
on_long_press=self.down_longpress)
|
||||||
|
|
||||||
self.search_results_wrapper = ft.Column(
|
self.search_results_wrapper = ft.Column(
|
||||||
[
|
[
|
||||||
|
@ -110,18 +113,18 @@ class WuttaLookup(WuttaControl):
|
||||||
[
|
[
|
||||||
ft.Text("SEARCH FOR:"),
|
ft.Text("SEARCH FOR:"),
|
||||||
self.searchbox,
|
self.searchbox,
|
||||||
self.make_button("Lookup",
|
make_button("Lookup",
|
||||||
font_size=self.font_size * 0.8,
|
font_size=self.font_size * 0.8,
|
||||||
height=self.default_button_height_dlg * 0.8,
|
height=self.default_button_height_dlg * 0.8,
|
||||||
width=self.default_button_height_dlg * 1.3,
|
width=self.default_button_height_dlg * 1.3,
|
||||||
on_click=self.lookup,
|
on_click=self.lookup,
|
||||||
bgcolor='blue'),
|
bgcolor='blue'),
|
||||||
self.make_button("Reset",
|
make_button("Reset",
|
||||||
font_size=self.font_size * 0.8,
|
font_size=self.font_size * 0.8,
|
||||||
height=self.default_button_height_dlg * 0.8,
|
height=self.default_button_height_dlg * 0.8,
|
||||||
width=self.default_button_height_dlg * 1.3,
|
width=self.default_button_height_dlg * 1.3,
|
||||||
on_click=self.reset,
|
on_click=self.reset,
|
||||||
bgcolor='yellow'),
|
bgcolor='yellow'),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
ft.Divider(),
|
ft.Divider(),
|
||||||
|
@ -150,11 +153,11 @@ class WuttaLookup(WuttaControl):
|
||||||
ft.Row(),
|
ft.Row(),
|
||||||
ft.Row(),
|
ft.Row(),
|
||||||
ft.Row(),
|
ft.Row(),
|
||||||
self.make_button("Cancel",
|
make_button("Cancel",
|
||||||
font_size=self.font_size * 0.8,
|
font_size=self.font_size * 0.8,
|
||||||
height=self.default_button_height_dlg * 0.8,
|
height=self.default_button_height_dlg * 0.8,
|
||||||
width=self.default_button_height_dlg * 1.3,
|
width=self.default_button_height_dlg * 1.3,
|
||||||
on_click=self.cancel),
|
on_click=self.cancel),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -162,10 +165,15 @@ class WuttaLookup(WuttaControl):
|
||||||
),
|
),
|
||||||
])
|
])
|
||||||
|
|
||||||
return ft.Container(
|
self.content = ft.Column(controls=controls)
|
||||||
content=ft.Column(controls=controls),
|
self.height = None if self.show_search else 600
|
||||||
height=None if self.show_search else 600,
|
|
||||||
)
|
def informed_refresh(self, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def reset(self, e=None):
|
||||||
|
if self.on_reset:
|
||||||
|
self.on_reset(e=e)
|
||||||
|
|
||||||
def get_results_columns(self):
|
def get_results_columns(self):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
################################################################################
|
################################################################################
|
||||||
#
|
#
|
||||||
# WuttaPOS -- Pythonic Point of Sale System
|
# WuttaPOS -- Pythonic Point of Sale System
|
||||||
# Copyright © 2023 Lance Edgar
|
# Copyright © 2023-2024 Lance Edgar
|
||||||
#
|
#
|
||||||
# This file is part of WuttaPOS.
|
# This file is part of WuttaPOS.
|
||||||
#
|
#
|
||||||
|
@ -26,15 +26,16 @@ WuttaPOS - ten-key control
|
||||||
|
|
||||||
import flet as ft
|
import flet as ft
|
||||||
|
|
||||||
from .base import WuttaControl
|
from wuttapos.util import make_button
|
||||||
|
|
||||||
|
|
||||||
class WuttaTenkeyMenu(WuttaControl):
|
class WuttaTenkeyMenu(ft.Container):
|
||||||
|
|
||||||
default_font_size = 40
|
default_font_size = 40
|
||||||
default_button_size = 100
|
default_button_size = 100
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, config, page=None, *args, **kwargs):
|
||||||
|
self.on_reset = kwargs.pop('on_reset', None)
|
||||||
self.simple = kwargs.pop('simple', False)
|
self.simple = kwargs.pop('simple', False)
|
||||||
self.on_char = kwargs.pop('on_char', None)
|
self.on_char = kwargs.pop('on_char', None)
|
||||||
self.on_enter = kwargs.pop('on_enter', None)
|
self.on_enter = kwargs.pop('on_enter', None)
|
||||||
|
@ -42,9 +43,12 @@ class WuttaTenkeyMenu(WuttaControl):
|
||||||
self.on_up_longpress = kwargs.pop('on_up_longpress', None)
|
self.on_up_longpress = kwargs.pop('on_up_longpress', None)
|
||||||
self.on_down_click = kwargs.pop('on_down_click', None)
|
self.on_down_click = kwargs.pop('on_down_click', None)
|
||||||
self.on_down_longpress = kwargs.pop('on_down_longpress', None)
|
self.on_down_longpress = kwargs.pop('on_down_longpress', None)
|
||||||
|
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
def build(self):
|
self.config = config
|
||||||
|
self.app = config.get_app()
|
||||||
|
self.enum = self.app.enum
|
||||||
|
|
||||||
row1 = [
|
row1 = [
|
||||||
self.make_tenkey_button("1"),
|
self.make_tenkey_button("1"),
|
||||||
|
@ -90,30 +94,35 @@ class WuttaTenkeyMenu(WuttaControl):
|
||||||
self.make_tenkey_button("ENTER", width=self.default_button_size * 2),
|
self.make_tenkey_button("ENTER", width=self.default_button_size * 2),
|
||||||
])
|
])
|
||||||
|
|
||||||
return ft.Container(
|
self.content = ft.Column(
|
||||||
content=ft.Column(
|
[
|
||||||
[
|
ft.Row(
|
||||||
ft.Row(
|
controls=row1,
|
||||||
controls=row1,
|
spacing=0,
|
||||||
spacing=0,
|
),
|
||||||
),
|
ft.Row(
|
||||||
ft.Row(
|
controls=row2,
|
||||||
controls=row2,
|
spacing=0,
|
||||||
spacing=0,
|
),
|
||||||
),
|
ft.Row(
|
||||||
ft.Row(
|
controls=row3,
|
||||||
controls=row3,
|
spacing=0,
|
||||||
spacing=0,
|
),
|
||||||
),
|
ft.Row(
|
||||||
ft.Row(
|
controls=row4,
|
||||||
controls=row4,
|
spacing=0,
|
||||||
spacing=0,
|
),
|
||||||
),
|
],
|
||||||
],
|
spacing=0,
|
||||||
spacing=0,
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def informed_refresh(self, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def reset(self, e=None):
|
||||||
|
if self.on_reset:
|
||||||
|
self.on_reset(e=e)
|
||||||
|
|
||||||
def make_tenkey_button(
|
def make_tenkey_button(
|
||||||
self,
|
self,
|
||||||
text,
|
text,
|
||||||
|
@ -132,10 +141,10 @@ class WuttaTenkeyMenu(WuttaControl):
|
||||||
if not on_click:
|
if not on_click:
|
||||||
on_click = self.tenkey_click
|
on_click = self.tenkey_click
|
||||||
|
|
||||||
return self.make_button(text, font_size=font_size, bgcolor='green',
|
return make_button(text, font_size=font_size, bgcolor='green',
|
||||||
height=height, width=width,
|
height=height, width=width,
|
||||||
on_click=on_click,
|
on_click=on_click,
|
||||||
on_long_press=on_long_press)
|
on_long_press=on_long_press)
|
||||||
|
|
||||||
def tenkey_click(self, e):
|
def tenkey_click(self, e):
|
||||||
value = e.control.content.value
|
value = e.control.content.value
|
||||||
|
|
|
@ -24,43 +24,35 @@
|
||||||
WuttaPOS - timestamp control
|
WuttaPOS - timestamp control
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import threading
|
import asyncio
|
||||||
import time
|
|
||||||
|
|
||||||
import flet as ft
|
import flet as ft
|
||||||
|
|
||||||
from .base import WuttaControl
|
|
||||||
|
|
||||||
|
class WuttaTimestamp(ft.Text):
|
||||||
|
|
||||||
class WuttaTimestamp(WuttaControl):
|
def __init__(self, config, page=None, *args, **kwargs):
|
||||||
|
self.on_reset = kwargs.pop('on_reset', None)
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
self.weight = kwargs.pop('weight', None)
|
|
||||||
self.size = kwargs.pop('size', None)
|
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
def build(self):
|
self.config = config
|
||||||
text = self.render_time(self.app.localtime())
|
self.app = self.config.get_app()
|
||||||
self.display = ft.Text(text, weight=self.weight, size=self.size)
|
|
||||||
|
|
||||||
# nb. daemonized thread should be stopped when app exits
|
self.value = self.render_time(self.app.localtime())
|
||||||
# cf. https://docs.python.org/3/library/threading.html#thread-objects
|
|
||||||
thread = threading.Thread(target=self.update_display, daemon=True)
|
|
||||||
thread.start()
|
|
||||||
|
|
||||||
return self.display
|
def did_mount(self):
|
||||||
|
self.running = True
|
||||||
|
self.page.run_task(self.update_display)
|
||||||
|
|
||||||
|
def will_unmount(self):
|
||||||
|
self.running = False
|
||||||
|
|
||||||
def render_time(self, value):
|
def render_time(self, value):
|
||||||
return value.strftime('%a %d %b %Y %I:%M:%S %p')
|
return value.strftime('%a %d %b %Y %I:%M:%S %p')
|
||||||
|
|
||||||
def update_display(self):
|
async def update_display(self):
|
||||||
while True:
|
while self.running:
|
||||||
if self.page:
|
self.value = self.render_time(self.app.localtime())
|
||||||
self.display.value = self.render_time(self.app.localtime())
|
self.update()
|
||||||
try:
|
await asyncio.sleep(0.5)
|
||||||
self.update()
|
|
||||||
except RuntimeError as error:
|
|
||||||
if str(error) == 'Event loop is closed':
|
|
||||||
break
|
|
||||||
raise
|
|
||||||
time.sleep(0.5)
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
################################################################################
|
################################################################################
|
||||||
#
|
#
|
||||||
# WuttaPOS -- Pythonic Point of Sale System
|
# WuttaPOS -- Pythonic Point of Sale System
|
||||||
# Copyright © 2023 Lance Edgar
|
# Copyright © 2024 Lance Edgar
|
||||||
#
|
#
|
||||||
# This file is part of WuttaPOS.
|
# This file is part of WuttaPOS.
|
||||||
#
|
#
|
||||||
|
@ -26,21 +26,24 @@ WuttaPOS - txn item control
|
||||||
|
|
||||||
import flet as ft
|
import flet as ft
|
||||||
|
|
||||||
from .base import WuttaControl
|
|
||||||
|
|
||||||
|
class WuttaTxnItem(ft.Row):
|
||||||
class WuttaTxnItem(WuttaControl):
|
|
||||||
"""
|
"""
|
||||||
Control for displaying a transaction line item within main POS
|
Control for displaying a transaction line item within main POS
|
||||||
items list.
|
items list.
|
||||||
"""
|
"""
|
||||||
font_size = 24
|
font_size = 24
|
||||||
|
|
||||||
def __init__(self, config, row, *args, **kwargs):
|
def __init__(self, config, row, page=None, *args, **kwargs):
|
||||||
super().__init__(config, *args, **kwargs)
|
self.on_reset = kwargs.pop('on_reset', None)
|
||||||
self.row = row
|
|
||||||
|
|
||||||
def build(self):
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
self.config = config
|
||||||
|
self.app = config.get_app()
|
||||||
|
self.enum = self.app.enum
|
||||||
|
|
||||||
|
self.row = row
|
||||||
|
|
||||||
self.major_style = ft.TextStyle(size=self.font_size,
|
self.major_style = ft.TextStyle(size=self.font_size,
|
||||||
weight=ft.FontWeight.BOLD)
|
weight=ft.FontWeight.BOLD)
|
||||||
|
@ -50,11 +53,11 @@ class WuttaTxnItem(WuttaControl):
|
||||||
|
|
||||||
if self.row.row_type in (self.enum.POS_ROW_TYPE_SELL,
|
if self.row.row_type in (self.enum.POS_ROW_TYPE_SELL,
|
||||||
self.enum.POS_ROW_TYPE_OPEN_RING):
|
self.enum.POS_ROW_TYPE_OPEN_RING):
|
||||||
return self.build_item_sell()
|
self.build_item_sell()
|
||||||
|
|
||||||
elif self.row.row_type in (self.enum.POS_ROW_TYPE_TENDER,
|
elif self.row.row_type in (self.enum.POS_ROW_TYPE_TENDER,
|
||||||
self.enum.POS_ROW_TYPE_CHANGE_BACK):
|
self.enum.POS_ROW_TYPE_CHANGE_BACK):
|
||||||
return self.build_item_tender()
|
self.build_item_tender()
|
||||||
|
|
||||||
def build_item_sell(self):
|
def build_item_sell(self):
|
||||||
|
|
||||||
|
@ -72,49 +75,52 @@ class WuttaTxnItem(WuttaControl):
|
||||||
# set initial text display values
|
# set initial text display values
|
||||||
self.refresh(update=False)
|
self.refresh(update=False)
|
||||||
|
|
||||||
return ft.Row(
|
self.controls = [
|
||||||
[
|
ft.Text(
|
||||||
ft.Text(
|
spans=[
|
||||||
spans=[
|
ft.TextSpan(f"{self.row.description}",
|
||||||
ft.TextSpan(f"{self.row.description}",
|
style=self.major_style),
|
||||||
style=self.major_style),
|
ft.TextSpan("× ", style=self.minor_style),
|
||||||
ft.TextSpan("× ", style=self.minor_style),
|
self.quantity,
|
||||||
self.quantity,
|
ft.TextSpan(" @ ", style=self.minor_style),
|
||||||
ft.TextSpan(" @ ", style=self.minor_style),
|
self.txn_price,
|
||||||
self.txn_price,
|
],
|
||||||
],
|
),
|
||||||
),
|
ft.Text(
|
||||||
ft.Text(
|
spans=[
|
||||||
spans=[
|
self.fs_flag,
|
||||||
self.fs_flag,
|
self.tax_flag,
|
||||||
self.tax_flag,
|
self.sales_total,
|
||||||
self.sales_total,
|
],
|
||||||
],
|
),
|
||||||
),
|
|
||||||
|
|
||||||
],
|
]
|
||||||
alignment=ft.MainAxisAlignment.SPACE_BETWEEN,
|
self.alignment = ft.MainAxisAlignment.SPACE_BETWEEN
|
||||||
)
|
|
||||||
|
|
||||||
def build_item_tender(self):
|
def build_item_tender(self):
|
||||||
return ft.Row(
|
self.controls = [
|
||||||
[
|
ft.Text(
|
||||||
ft.Text(
|
spans=[
|
||||||
spans=[
|
ft.TextSpan(f"{self.row.description}",
|
||||||
ft.TextSpan(f"{self.row.description}",
|
style=self.major_style),
|
||||||
style=self.major_style),
|
],
|
||||||
],
|
),
|
||||||
),
|
ft.Text(
|
||||||
ft.Text(
|
spans=[
|
||||||
spans=[
|
ft.TextSpan(self.app.render_currency(self.row.tender_total),
|
||||||
ft.TextSpan(self.app.render_currency(self.row.tender_total),
|
style=self.major_style),
|
||||||
style=self.major_style),
|
],
|
||||||
],
|
),
|
||||||
),
|
|
||||||
|
|
||||||
],
|
]
|
||||||
alignment=ft.MainAxisAlignment.SPACE_BETWEEN,
|
self.alignment = ft.MainAxisAlignment.SPACE_BETWEEN
|
||||||
)
|
|
||||||
|
def informed_refresh(self, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def reset(self, e=None):
|
||||||
|
if self.on_reset:
|
||||||
|
self.on_reset(e=e)
|
||||||
|
|
||||||
def refresh(self, update=True):
|
def refresh(self, update=True):
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue