mirror of
https://github.com/arsenetar/dupeguru.git
synced 2025-05-06 17:09:49 +00:00
Additional type hints in hscommon
This commit is contained in:
parent
7865e4aeac
commit
d5eeab4a17
@ -36,11 +36,11 @@ class GUIObject:
|
|||||||
``multibind`` flag to ``True`` and the safeguard will be disabled.
|
``multibind`` flag to ``True`` and the safeguard will be disabled.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, multibind=False):
|
def __init__(self, multibind: bool = False) -> None:
|
||||||
self._view = None
|
self._view = None
|
||||||
self._multibind = multibind
|
self._multibind = multibind
|
||||||
|
|
||||||
def _view_updated(self):
|
def _view_updated(self) -> None:
|
||||||
"""(Virtual) Called after :attr:`view` has been set.
|
"""(Virtual) Called after :attr:`view` has been set.
|
||||||
|
|
||||||
Doing nothing by default, this method is called after :attr:`view` has been set (it isn't
|
Doing nothing by default, this method is called after :attr:`view` has been set (it isn't
|
||||||
@ -48,7 +48,7 @@ class GUIObject:
|
|||||||
(which is often the whole of the initialization code).
|
(which is often the whole of the initialization code).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def has_view(self):
|
def has_view(self) -> bool:
|
||||||
return (self._view is not None) and (not isinstance(self._view, NoopGUI))
|
return (self._view is not None) and (not isinstance(self._view, NoopGUI))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -67,7 +67,7 @@ class GUIObject:
|
|||||||
return self._view
|
return self._view
|
||||||
|
|
||||||
@view.setter
|
@view.setter
|
||||||
def view(self, value):
|
def view(self, value) -> None:
|
||||||
if self._view is None and value is None:
|
if self._view is None and value is None:
|
||||||
# Initial view assignment
|
# Initial view assignment
|
||||||
return
|
return
|
||||||
|
@ -7,8 +7,10 @@
|
|||||||
# http://www.gnu.org/licenses/gpl-3.0.html
|
# http://www.gnu.org/licenses/gpl-3.0.html
|
||||||
|
|
||||||
import copy
|
import copy
|
||||||
|
from typing import Any, List, Tuple, Union
|
||||||
|
|
||||||
from hscommon.gui.base import GUIObject
|
from hscommon.gui.base import GUIObject
|
||||||
|
from hscommon.gui.table import GUITable
|
||||||
|
|
||||||
|
|
||||||
class Column:
|
class Column:
|
||||||
@ -17,7 +19,7 @@ class Column:
|
|||||||
These attributes are then used to correctly configure the column on the "view" side.
|
These attributes are then used to correctly configure the column on the "view" side.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, name, display="", visible=True, optional=False):
|
def __init__(self, name: str, display: str = "", visible: bool = True, optional: bool = False) -> None:
|
||||||
#: "programmatical" (not for display) name. Used as a reference in a couple of place, such
|
#: "programmatical" (not for display) name. Used as a reference in a couple of place, such
|
||||||
#: as :meth:`Columns.column_by_name`.
|
#: as :meth:`Columns.column_by_name`.
|
||||||
self.name = name
|
self.name = name
|
||||||
@ -52,14 +54,14 @@ class ColumnsView:
|
|||||||
callbacks.
|
callbacks.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def restore_columns(self):
|
def restore_columns(self) -> None:
|
||||||
"""Update all columns according to the model.
|
"""Update all columns according to the model.
|
||||||
|
|
||||||
When this is called, our view has to update the columns title, order and visibility of all
|
When this is called, our view has to update the columns title, order and visibility of all
|
||||||
columns.
|
columns.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def set_column_visible(self, colname, visible):
|
def set_column_visible(self, colname: str, visible: bool) -> None:
|
||||||
"""Update visibility of column ``colname``.
|
"""Update visibility of column ``colname``.
|
||||||
|
|
||||||
Called when the user toggles the visibility of a column, we must update the column
|
Called when the user toggles the visibility of a column, we must update the column
|
||||||
@ -73,13 +75,13 @@ class PrefAccessInterface:
|
|||||||
*Not actually used in the code. For documentation purposes only.*
|
*Not actually used in the code. For documentation purposes only.*
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def get_default(self, key, fallback_value):
|
def get_default(self, key: str, fallback_value: Union[Any, None]) -> Any:
|
||||||
"""Retrieve the value for ``key`` in the currently running app's preference store.
|
"""Retrieve the value for ``key`` in the currently running app's preference store.
|
||||||
|
|
||||||
If the key doesn't exist, return ``fallback_value``.
|
If the key doesn't exist, return ``fallback_value``.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def set_default(self, key, value):
|
def set_default(self, key: str, value: Any) -> None:
|
||||||
"""Set the value ``value`` for ``key`` in the currently running app's preference store."""
|
"""Set the value ``value`` for ``key`` in the currently running app's preference store."""
|
||||||
|
|
||||||
|
|
||||||
@ -104,65 +106,65 @@ class Columns(GUIObject):
|
|||||||
have that same prefix.
|
have that same prefix.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, table, prefaccess=None, savename=None):
|
def __init__(self, table: GUITable, prefaccess=None, savename: Union[str, None] = None):
|
||||||
GUIObject.__init__(self)
|
GUIObject.__init__(self)
|
||||||
self.table = table
|
self.table = table
|
||||||
self.prefaccess = prefaccess
|
self.prefaccess = prefaccess
|
||||||
self.savename = savename
|
self.savename = savename
|
||||||
# We use copy here for test isolation. If we don't, changing a column affects all tests.
|
# We use copy here for test isolation. If we don't, changing a column affects all tests.
|
||||||
self.column_list = list(map(copy.copy, table.COLUMNS))
|
self.column_list: List[Column] = list(map(copy.copy, table.COLUMNS))
|
||||||
for i, column in enumerate(self.column_list):
|
for i, column in enumerate(self.column_list):
|
||||||
column.logical_index = i
|
column.logical_index = i
|
||||||
column.ordered_index = i
|
column.ordered_index = i
|
||||||
self.coldata = {col.name: col for col in self.column_list}
|
self.coldata = {col.name: col for col in self.column_list}
|
||||||
|
|
||||||
# --- Private
|
# --- Private
|
||||||
def _get_colname_attr(self, colname, attrname, default):
|
def _get_colname_attr(self, colname: str, attrname: str, default: Any) -> Any:
|
||||||
try:
|
try:
|
||||||
return getattr(self.coldata[colname], attrname)
|
return getattr(self.coldata[colname], attrname)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
return default
|
return default
|
||||||
|
|
||||||
def _set_colname_attr(self, colname, attrname, value):
|
def _set_colname_attr(self, colname: str, attrname: str, value: Any) -> None:
|
||||||
try:
|
try:
|
||||||
col = self.coldata[colname]
|
col = self.coldata[colname]
|
||||||
setattr(col, attrname, value)
|
setattr(col, attrname, value)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def _optional_columns(self):
|
def _optional_columns(self) -> List[Column]:
|
||||||
return [c for c in self.column_list if c.optional]
|
return [c for c in self.column_list if c.optional]
|
||||||
|
|
||||||
# --- Override
|
# --- Override
|
||||||
def _view_updated(self):
|
def _view_updated(self) -> None:
|
||||||
self.restore_columns()
|
self.restore_columns()
|
||||||
|
|
||||||
# --- Public
|
# --- Public
|
||||||
def column_by_index(self, index):
|
def column_by_index(self, index: int):
|
||||||
"""Return the :class:`Column` having the :attr:`~Column.logical_index` ``index``."""
|
"""Return the :class:`Column` having the :attr:`~Column.logical_index` ``index``."""
|
||||||
return self.column_list[index]
|
return self.column_list[index]
|
||||||
|
|
||||||
def column_by_name(self, name):
|
def column_by_name(self, name: str):
|
||||||
"""Return the :class:`Column` having the :attr:`~Column.name` ``name``."""
|
"""Return the :class:`Column` having the :attr:`~Column.name` ``name``."""
|
||||||
return self.coldata[name]
|
return self.coldata[name]
|
||||||
|
|
||||||
def columns_count(self):
|
def columns_count(self) -> int:
|
||||||
"""Returns the number of columns in our set."""
|
"""Returns the number of columns in our set."""
|
||||||
return len(self.column_list)
|
return len(self.column_list)
|
||||||
|
|
||||||
def column_display(self, colname):
|
def column_display(self, colname: str) -> str:
|
||||||
"""Returns display name for column named ``colname``, or ``''`` if there's none."""
|
"""Returns display name for column named ``colname``, or ``''`` if there's none."""
|
||||||
return self._get_colname_attr(colname, "display", "")
|
return self._get_colname_attr(colname, "display", "")
|
||||||
|
|
||||||
def column_is_visible(self, colname):
|
def column_is_visible(self, colname: str) -> bool:
|
||||||
"""Returns visibility for column named ``colname``, or ``True`` if there's none."""
|
"""Returns visibility for column named ``colname``, or ``True`` if there's none."""
|
||||||
return self._get_colname_attr(colname, "visible", True)
|
return self._get_colname_attr(colname, "visible", True)
|
||||||
|
|
||||||
def column_width(self, colname):
|
def column_width(self, colname: str) -> int:
|
||||||
"""Returns width for column named ``colname``, or ``0`` if there's none."""
|
"""Returns width for column named ``colname``, or ``0`` if there's none."""
|
||||||
return self._get_colname_attr(colname, "width", 0)
|
return self._get_colname_attr(colname, "width", 0)
|
||||||
|
|
||||||
def columns_to_right(self, colname):
|
def columns_to_right(self, colname: str) -> List[str]:
|
||||||
"""Returns the list of all columns to the right of ``colname``.
|
"""Returns the list of all columns to the right of ``colname``.
|
||||||
|
|
||||||
"right" meaning "having a higher :attr:`Column.ordered_index`" in our left-to-right
|
"right" meaning "having a higher :attr:`Column.ordered_index`" in our left-to-right
|
||||||
@ -172,7 +174,7 @@ class Columns(GUIObject):
|
|||||||
index = column.ordered_index
|
index = column.ordered_index
|
||||||
return [col.name for col in self.column_list if (col.visible and col.ordered_index > index)]
|
return [col.name for col in self.column_list if (col.visible and col.ordered_index > index)]
|
||||||
|
|
||||||
def menu_items(self):
|
def menu_items(self) -> List[Tuple[str, bool]]:
|
||||||
"""Returns a list of items convenient for quick visibility menu generation.
|
"""Returns a list of items convenient for quick visibility menu generation.
|
||||||
|
|
||||||
Returns a list of ``(display_name, is_marked)`` items for each optional column in the
|
Returns a list of ``(display_name, is_marked)`` items for each optional column in the
|
||||||
@ -184,7 +186,7 @@ class Columns(GUIObject):
|
|||||||
"""
|
"""
|
||||||
return [(c.display, c.visible) for c in self._optional_columns()]
|
return [(c.display, c.visible) for c in self._optional_columns()]
|
||||||
|
|
||||||
def move_column(self, colname, index):
|
def move_column(self, colname: str, index: int) -> None:
|
||||||
"""Moves column ``colname`` to ``index``.
|
"""Moves column ``colname`` to ``index``.
|
||||||
|
|
||||||
The column will be placed just in front of the column currently having that index, or to the
|
The column will be placed just in front of the column currently having that index, or to the
|
||||||
@ -195,7 +197,7 @@ class Columns(GUIObject):
|
|||||||
colnames.insert(index, colname)
|
colnames.insert(index, colname)
|
||||||
self.set_column_order(colnames)
|
self.set_column_order(colnames)
|
||||||
|
|
||||||
def reset_to_defaults(self):
|
def reset_to_defaults(self) -> None:
|
||||||
"""Reset all columns' width and visibility to their default values."""
|
"""Reset all columns' width and visibility to their default values."""
|
||||||
self.set_column_order([col.name for col in self.column_list])
|
self.set_column_order([col.name for col in self.column_list])
|
||||||
for col in self._optional_columns():
|
for col in self._optional_columns():
|
||||||
@ -203,11 +205,11 @@ class Columns(GUIObject):
|
|||||||
col.width = col.default_width
|
col.width = col.default_width
|
||||||
self.view.restore_columns()
|
self.view.restore_columns()
|
||||||
|
|
||||||
def resize_column(self, colname, newwidth):
|
def resize_column(self, colname: str, newwidth: int) -> None:
|
||||||
"""Set column ``colname``'s width to ``newwidth``."""
|
"""Set column ``colname``'s width to ``newwidth``."""
|
||||||
self._set_colname_attr(colname, "width", newwidth)
|
self._set_colname_attr(colname, "width", newwidth)
|
||||||
|
|
||||||
def restore_columns(self):
|
def restore_columns(self) -> None:
|
||||||
"""Restore's column persistent attributes from the last :meth:`save_columns`."""
|
"""Restore's column persistent attributes from the last :meth:`save_columns`."""
|
||||||
if not (self.prefaccess and self.savename and self.coldata):
|
if not (self.prefaccess and self.savename and self.coldata):
|
||||||
if (not self.savename) and (self.coldata):
|
if (not self.savename) and (self.coldata):
|
||||||
@ -226,7 +228,7 @@ class Columns(GUIObject):
|
|||||||
col.visible = coldata["visible"]
|
col.visible = coldata["visible"]
|
||||||
self.view.restore_columns()
|
self.view.restore_columns()
|
||||||
|
|
||||||
def save_columns(self):
|
def save_columns(self) -> None:
|
||||||
"""Save column attributes in persistent storage for restoration in :meth:`restore_columns`."""
|
"""Save column attributes in persistent storage for restoration in :meth:`restore_columns`."""
|
||||||
if not (self.prefaccess and self.savename and self.coldata):
|
if not (self.prefaccess and self.savename and self.coldata):
|
||||||
return
|
return
|
||||||
@ -237,7 +239,8 @@ class Columns(GUIObject):
|
|||||||
coldata["visible"] = col.visible
|
coldata["visible"] = col.visible
|
||||||
self.prefaccess.set_default(pref_name, coldata)
|
self.prefaccess.set_default(pref_name, coldata)
|
||||||
|
|
||||||
def set_column_order(self, colnames):
|
# TODO annotate colnames
|
||||||
|
def set_column_order(self, colnames) -> None:
|
||||||
"""Change the columns order so it matches the order in ``colnames``.
|
"""Change the columns order so it matches the order in ``colnames``.
|
||||||
|
|
||||||
:param colnames: A list of column names in the desired order.
|
:param colnames: A list of column names in the desired order.
|
||||||
@ -247,17 +250,17 @@ class Columns(GUIObject):
|
|||||||
col = self.coldata[colname]
|
col = self.coldata[colname]
|
||||||
col.ordered_index = i
|
col.ordered_index = i
|
||||||
|
|
||||||
def set_column_visible(self, colname, visible):
|
def set_column_visible(self, colname: str, visible: bool) -> None:
|
||||||
"""Set the visibility of column ``colname``."""
|
"""Set the visibility of column ``colname``."""
|
||||||
self.table.save_edits() # the table on the GUI side will stop editing when the columns change
|
self.table.save_edits() # the table on the GUI side will stop editing when the columns change
|
||||||
self._set_colname_attr(colname, "visible", visible)
|
self._set_colname_attr(colname, "visible", visible)
|
||||||
self.view.set_column_visible(colname, visible)
|
self.view.set_column_visible(colname, visible)
|
||||||
|
|
||||||
def set_default_width(self, colname, width):
|
def set_default_width(self, colname: str, width: int) -> None:
|
||||||
"""Set the default width or column ``colname``."""
|
"""Set the default width or column ``colname``."""
|
||||||
self._set_colname_attr(colname, "default_width", width)
|
self._set_colname_attr(colname, "default_width", width)
|
||||||
|
|
||||||
def toggle_menu_item(self, index):
|
def toggle_menu_item(self, index: int) -> bool:
|
||||||
"""Toggles the visibility of an optional column.
|
"""Toggles the visibility of an optional column.
|
||||||
|
|
||||||
You know, that optional column menu you've generated in :meth:`menu_items`? Well, ``index``
|
You know, that optional column menu you've generated in :meth:`menu_items`? Well, ``index``
|
||||||
@ -271,11 +274,11 @@ class Columns(GUIObject):
|
|||||||
|
|
||||||
# --- Properties
|
# --- Properties
|
||||||
@property
|
@property
|
||||||
def ordered_columns(self):
|
def ordered_columns(self) -> List[Column]:
|
||||||
"""List of :class:`Column` in visible order."""
|
"""List of :class:`Column` in visible order."""
|
||||||
return [col for col in sorted(self.column_list, key=lambda col: col.ordered_index)]
|
return [col for col in sorted(self.column_list, key=lambda col: col.ordered_index)]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def colnames(self):
|
def colnames(self) -> List[str]:
|
||||||
"""List of column names in visible order."""
|
"""List of column names in visible order."""
|
||||||
return [col.name for col in self.ordered_columns]
|
return [col.name for col in self.ordered_columns]
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
# which should be included with this package. The terms are also available at
|
# which should be included with this package. The terms are also available at
|
||||||
# http://www.gnu.org/licenses/gpl-3.0.html
|
# http://www.gnu.org/licenses/gpl-3.0.html
|
||||||
|
|
||||||
|
from typing import Callable, Tuple, Union
|
||||||
from hscommon.jobprogress.performer import ThreadedJobPerformer
|
from hscommon.jobprogress.performer import ThreadedJobPerformer
|
||||||
from hscommon.gui.base import GUIObject
|
from hscommon.gui.base import GUIObject
|
||||||
from hscommon.gui.text_field import TextField
|
from hscommon.gui.text_field import TextField
|
||||||
@ -20,13 +21,13 @@ class ProgressWindowView:
|
|||||||
It's also expected to call :meth:`ProgressWindow.cancel` when the cancel button is clicked.
|
It's also expected to call :meth:`ProgressWindow.cancel` when the cancel button is clicked.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def show(self):
|
def show(self) -> None:
|
||||||
"""Show the dialog."""
|
"""Show the dialog."""
|
||||||
|
|
||||||
def close(self):
|
def close(self) -> None:
|
||||||
"""Close the dialog."""
|
"""Close the dialog."""
|
||||||
|
|
||||||
def set_progress(self, progress):
|
def set_progress(self, progress: int) -> None:
|
||||||
"""Set the progress of the progress bar to ``progress``.
|
"""Set the progress of the progress bar to ``progress``.
|
||||||
|
|
||||||
Not all jobs are equally responsive on their job progress report and it is recommended that
|
Not all jobs are equally responsive on their job progress report and it is recommended that
|
||||||
@ -60,7 +61,11 @@ class ProgressWindow(GUIObject, ThreadedJobPerformer):
|
|||||||
called as if the job terminated normally.
|
called as if the job terminated normally.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, finish_func, error_func=None):
|
def __init__(
|
||||||
|
self,
|
||||||
|
finish_func: Callable[[Union[str, None]], None],
|
||||||
|
error_func: Callable[[Union[str, None], Exception], bool] = None,
|
||||||
|
) -> None:
|
||||||
# finish_func(jobid) is the function that is called when a job is completed.
|
# finish_func(jobid) is the function that is called when a job is completed.
|
||||||
GUIObject.__init__(self)
|
GUIObject.__init__(self)
|
||||||
ThreadedJobPerformer.__init__(self)
|
ThreadedJobPerformer.__init__(self)
|
||||||
@ -71,9 +76,9 @@ class ProgressWindow(GUIObject, ThreadedJobPerformer):
|
|||||||
#: :class:`.TextField`. It contains the job textual update that the function might yield
|
#: :class:`.TextField`. It contains the job textual update that the function might yield
|
||||||
#: during its course.
|
#: during its course.
|
||||||
self.progressdesc_textfield = TextField()
|
self.progressdesc_textfield = TextField()
|
||||||
self.jobid = None
|
self.jobid: Union[str, None] = None
|
||||||
|
|
||||||
def cancel(self):
|
def cancel(self) -> None:
|
||||||
"""Call for a user-initiated job cancellation."""
|
"""Call for a user-initiated job cancellation."""
|
||||||
# The UI is sometimes a bit buggy and calls cancel() on self.view.close(). We just want to
|
# The UI is sometimes a bit buggy and calls cancel() on self.view.close(). We just want to
|
||||||
# make sure that this doesn't lead us to think that the user acually cancelled the task, so
|
# make sure that this doesn't lead us to think that the user acually cancelled the task, so
|
||||||
@ -81,7 +86,7 @@ class ProgressWindow(GUIObject, ThreadedJobPerformer):
|
|||||||
if self._job_running:
|
if self._job_running:
|
||||||
self.job_cancelled = True
|
self.job_cancelled = True
|
||||||
|
|
||||||
def pulse(self):
|
def pulse(self) -> None:
|
||||||
"""Update progress reports in the GUI.
|
"""Update progress reports in the GUI.
|
||||||
|
|
||||||
Call this regularly from the GUI main run loop. The values might change before
|
Call this regularly from the GUI main run loop. The values might change before
|
||||||
@ -111,7 +116,7 @@ class ProgressWindow(GUIObject, ThreadedJobPerformer):
|
|||||||
self.progressdesc_textfield.text = last_desc
|
self.progressdesc_textfield.text = last_desc
|
||||||
self.view.set_progress(last_progress)
|
self.view.set_progress(last_progress)
|
||||||
|
|
||||||
def run(self, jobid, title, target, args=()):
|
def run(self, jobid: str, title: str, target: Callable, args: Tuple = ()):
|
||||||
"""Starts a threaded job.
|
"""Starts a threaded job.
|
||||||
|
|
||||||
The ``target`` function will be sent, as its first argument, a :class:`.Job` instance which
|
The ``target`` function will be sent, as its first argument, a :class:`.Job` instance which
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
from collections.abc import MutableSequence
|
from collections.abc import MutableSequence
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
|
from typing import Any, List, Tuple, Union
|
||||||
|
|
||||||
from hscommon.gui.base import GUIObject
|
from hscommon.gui.base import GUIObject
|
||||||
from hscommon.gui.selectable_list import Selectable
|
from hscommon.gui.selectable_list import Selectable
|
||||||
@ -27,12 +28,16 @@ class Table(MutableSequence, Selectable):
|
|||||||
Subclasses :class:`.Selectable`.
|
Subclasses :class:`.Selectable`.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
# Should be List[Column], but have circular import...
|
||||||
Selectable.__init__(self)
|
COLUMNS: List = []
|
||||||
self._rows = []
|
|
||||||
self._header = None
|
|
||||||
self._footer = None
|
|
||||||
|
|
||||||
|
def __init__(self) -> None:
|
||||||
|
Selectable.__init__(self)
|
||||||
|
self._rows: List["Row"] = []
|
||||||
|
self._header: Union["Row", None] = None
|
||||||
|
self._footer: Union["Row", None] = None
|
||||||
|
|
||||||
|
# TODO type hint for key
|
||||||
def __delitem__(self, key):
|
def __delitem__(self, key):
|
||||||
self._rows.__delitem__(key)
|
self._rows.__delitem__(key)
|
||||||
if self._header is not None and ((not self) or (self[0] is not self._header)):
|
if self._header is not None and ((not self) or (self[0] is not self._header)):
|
||||||
@ -41,16 +46,18 @@ class Table(MutableSequence, Selectable):
|
|||||||
self._footer = None
|
self._footer = None
|
||||||
self._check_selection_range()
|
self._check_selection_range()
|
||||||
|
|
||||||
def __getitem__(self, key):
|
# TODO type hint for key
|
||||||
|
def __getitem__(self, key) -> Any:
|
||||||
return self._rows.__getitem__(key)
|
return self._rows.__getitem__(key)
|
||||||
|
|
||||||
def __len__(self):
|
def __len__(self) -> int:
|
||||||
return len(self._rows)
|
return len(self._rows)
|
||||||
|
|
||||||
def __setitem__(self, key, value):
|
# TODO type hint for key
|
||||||
|
def __setitem__(self, key, value: Any) -> None:
|
||||||
self._rows.__setitem__(key, value)
|
self._rows.__setitem__(key, value)
|
||||||
|
|
||||||
def append(self, item):
|
def append(self, item: "Row") -> None:
|
||||||
"""Appends ``item`` at the end of the table.
|
"""Appends ``item`` at the end of the table.
|
||||||
|
|
||||||
If there's a footer, the item is inserted before it.
|
If there's a footer, the item is inserted before it.
|
||||||
@ -60,7 +67,7 @@ class Table(MutableSequence, Selectable):
|
|||||||
else:
|
else:
|
||||||
self._rows.append(item)
|
self._rows.append(item)
|
||||||
|
|
||||||
def insert(self, index, item):
|
def insert(self, index: int, item: "Row") -> None:
|
||||||
"""Inserts ``item`` at ``index`` in the table.
|
"""Inserts ``item`` at ``index`` in the table.
|
||||||
|
|
||||||
If there's a header, will make sure we don't insert before it, and if there's a footer, will
|
If there's a header, will make sure we don't insert before it, and if there's a footer, will
|
||||||
@ -72,7 +79,7 @@ class Table(MutableSequence, Selectable):
|
|||||||
index = len(self) - 1
|
index = len(self) - 1
|
||||||
self._rows.insert(index, item)
|
self._rows.insert(index, item)
|
||||||
|
|
||||||
def remove(self, row):
|
def remove(self, row: "Row") -> None:
|
||||||
"""Removes ``row`` from table.
|
"""Removes ``row`` from table.
|
||||||
|
|
||||||
If ``row`` is a header or footer, that header or footer will be set to ``None``.
|
If ``row`` is a header or footer, that header or footer will be set to ``None``.
|
||||||
@ -84,7 +91,7 @@ class Table(MutableSequence, Selectable):
|
|||||||
self._rows.remove(row)
|
self._rows.remove(row)
|
||||||
self._check_selection_range()
|
self._check_selection_range()
|
||||||
|
|
||||||
def sort_by(self, column_name, desc=False):
|
def sort_by(self, column_name: str, desc: bool = False) -> None:
|
||||||
"""Sort table by ``column_name``.
|
"""Sort table by ``column_name``.
|
||||||
|
|
||||||
Sort key for each row is computed from :meth:`Row.sort_key_for_column`.
|
Sort key for each row is computed from :meth:`Row.sort_key_for_column`.
|
||||||
@ -105,7 +112,7 @@ class Table(MutableSequence, Selectable):
|
|||||||
|
|
||||||
# --- Properties
|
# --- Properties
|
||||||
@property
|
@property
|
||||||
def footer(self):
|
def footer(self) -> Union["Row", None]:
|
||||||
"""If set, a row that always stay at the bottom of the table.
|
"""If set, a row that always stay at the bottom of the table.
|
||||||
|
|
||||||
:class:`Row`. *get/set*.
|
:class:`Row`. *get/set*.
|
||||||
@ -128,7 +135,7 @@ class Table(MutableSequence, Selectable):
|
|||||||
return self._footer
|
return self._footer
|
||||||
|
|
||||||
@footer.setter
|
@footer.setter
|
||||||
def footer(self, value):
|
def footer(self, value: Union["Row", None]) -> None:
|
||||||
if self._footer is not None:
|
if self._footer is not None:
|
||||||
self._rows.pop()
|
self._rows.pop()
|
||||||
if value is not None:
|
if value is not None:
|
||||||
@ -136,7 +143,7 @@ class Table(MutableSequence, Selectable):
|
|||||||
self._footer = value
|
self._footer = value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def header(self):
|
def header(self) -> Union["Row", None]:
|
||||||
"""If set, a row that always stay at the bottom of the table.
|
"""If set, a row that always stay at the bottom of the table.
|
||||||
|
|
||||||
See :attr:`footer` for details.
|
See :attr:`footer` for details.
|
||||||
@ -144,7 +151,7 @@ class Table(MutableSequence, Selectable):
|
|||||||
return self._header
|
return self._header
|
||||||
|
|
||||||
@header.setter
|
@header.setter
|
||||||
def header(self, value):
|
def header(self, value: Union["Row", None]) -> None:
|
||||||
if self._header is not None:
|
if self._header is not None:
|
||||||
self._rows.pop(0)
|
self._rows.pop(0)
|
||||||
if value is not None:
|
if value is not None:
|
||||||
@ -152,7 +159,7 @@ class Table(MutableSequence, Selectable):
|
|||||||
self._header = value
|
self._header = value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def row_count(self):
|
def row_count(self) -> int:
|
||||||
"""Number or rows in the table (without counting header and footer).
|
"""Number or rows in the table (without counting header and footer).
|
||||||
|
|
||||||
*int*. *read-only*.
|
*int*. *read-only*.
|
||||||
@ -165,7 +172,7 @@ class Table(MutableSequence, Selectable):
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def rows(self):
|
def rows(self) -> List["Row"]:
|
||||||
"""List of rows in the table, excluding header and footer.
|
"""List of rows in the table, excluding header and footer.
|
||||||
|
|
||||||
List of :class:`Row`. *read-only*.
|
List of :class:`Row`. *read-only*.
|
||||||
@ -179,7 +186,7 @@ class Table(MutableSequence, Selectable):
|
|||||||
return self[start:end]
|
return self[start:end]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def selected_row(self):
|
def selected_row(self) -> "Row":
|
||||||
"""Selected row according to :attr:`Selectable.selected_index`.
|
"""Selected row according to :attr:`Selectable.selected_index`.
|
||||||
|
|
||||||
:class:`Row`. *get/set*.
|
:class:`Row`. *get/set*.
|
||||||
@ -190,14 +197,14 @@ class Table(MutableSequence, Selectable):
|
|||||||
return self[self.selected_index] if self.selected_index is not None else None
|
return self[self.selected_index] if self.selected_index is not None else None
|
||||||
|
|
||||||
@selected_row.setter
|
@selected_row.setter
|
||||||
def selected_row(self, value):
|
def selected_row(self, value: int) -> None:
|
||||||
try:
|
try:
|
||||||
self.selected_index = self.index(value)
|
self.selected_index = self.index(value)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def selected_rows(self):
|
def selected_rows(self) -> List["Row"]:
|
||||||
"""List of selected rows based on :attr:`.selected_indexes`.
|
"""List of selected rows based on :attr:`.selected_indexes`.
|
||||||
|
|
||||||
List of :class:`Row`. *read-only*.
|
List of :class:`Row`. *read-only*.
|
||||||
@ -219,20 +226,20 @@ class GUITableView:
|
|||||||
Whenever the user changes the selection, we expect the view to call :meth:`Table.select`.
|
Whenever the user changes the selection, we expect the view to call :meth:`Table.select`.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def refresh(self):
|
def refresh(self) -> None:
|
||||||
"""Refreshes the contents of the table widget.
|
"""Refreshes the contents of the table widget.
|
||||||
|
|
||||||
Ensures that the contents of the table widget is synced with the model. This includes
|
Ensures that the contents of the table widget is synced with the model. This includes
|
||||||
selection.
|
selection.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def start_editing(self):
|
def start_editing(self) -> None:
|
||||||
"""Start editing the currently selected row.
|
"""Start editing the currently selected row.
|
||||||
|
|
||||||
Begin whatever inline editing support that the view supports.
|
Begin whatever inline editing support that the view supports.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def stop_editing(self):
|
def stop_editing(self) -> None:
|
||||||
"""Stop editing if there's an inline editing in effect.
|
"""Stop editing if there's an inline editing in effect.
|
||||||
|
|
||||||
There's no "aborting" implied in this call, so it's appropriate to send whatever the user
|
There's no "aborting" implied in this call, so it's appropriate to send whatever the user
|
||||||
@ -260,33 +267,33 @@ class GUITable(Table, GUIObject):
|
|||||||
:class:`GUITableView`.
|
:class:`GUITableView`.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self) -> None:
|
||||||
GUIObject.__init__(self)
|
GUIObject.__init__(self)
|
||||||
Table.__init__(self)
|
Table.__init__(self)
|
||||||
#: The row being currently edited by the user. ``None`` if no edit is taking place.
|
#: The row being currently edited by the user. ``None`` if no edit is taking place.
|
||||||
self.edited = None
|
self.edited: Union["Row", None] = None
|
||||||
self._sort_descriptor = None
|
self._sort_descriptor: Union[SortDescriptor, None] = None
|
||||||
|
|
||||||
# --- Virtual
|
# --- Virtual
|
||||||
def _do_add(self):
|
def _do_add(self) -> Tuple["Row", int]:
|
||||||
"""(Virtual) Creates a new row, adds it in the table.
|
"""(Virtual) Creates a new row, adds it in the table.
|
||||||
|
|
||||||
Returns ``(row, insert_index)``.
|
Returns ``(row, insert_index)``.
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
def _do_delete(self):
|
def _do_delete(self) -> None:
|
||||||
"""(Virtual) Delete the selected rows."""
|
"""(Virtual) Delete the selected rows."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def _fill(self):
|
def _fill(self) -> None:
|
||||||
"""(Virtual/Required) Fills the table with all the rows that this table is supposed to have.
|
"""(Virtual/Required) Fills the table with all the rows that this table is supposed to have.
|
||||||
|
|
||||||
Called by :meth:`refresh`. Does nothing by default.
|
Called by :meth:`refresh`. Does nothing by default.
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def _is_edited_new(self):
|
def _is_edited_new(self) -> bool:
|
||||||
"""(Virtual) Returns whether the currently edited row should be considered "new".
|
"""(Virtual) Returns whether the currently edited row should be considered "new".
|
||||||
|
|
||||||
This is used in :meth:`cancel_edits` to know whether the cancellation of the edit means a
|
This is used in :meth:`cancel_edits` to know whether the cancellation of the edit means a
|
||||||
@ -315,7 +322,7 @@ class GUITable(Table, GUIObject):
|
|||||||
self.select([len(self) - 1])
|
self.select([len(self) - 1])
|
||||||
|
|
||||||
# --- Public
|
# --- Public
|
||||||
def add(self):
|
def add(self) -> None:
|
||||||
"""Add a new row in edit mode.
|
"""Add a new row in edit mode.
|
||||||
|
|
||||||
Requires :meth:`do_add` to be implemented. The newly added row will be selected and in edit
|
Requires :meth:`do_add` to be implemented. The newly added row will be selected and in edit
|
||||||
@ -334,7 +341,7 @@ class GUITable(Table, GUIObject):
|
|||||||
self.edited = row
|
self.edited = row
|
||||||
self.view.start_editing()
|
self.view.start_editing()
|
||||||
|
|
||||||
def can_edit_cell(self, column_name, row_index):
|
def can_edit_cell(self, column_name: str, row_index: int) -> bool:
|
||||||
"""Returns whether the cell at ``row_index`` and ``column_name`` can be edited.
|
"""Returns whether the cell at ``row_index`` and ``column_name`` can be edited.
|
||||||
|
|
||||||
A row is, by default, editable as soon as it has an attr with the same name as `column`.
|
A row is, by default, editable as soon as it has an attr with the same name as `column`.
|
||||||
@ -346,7 +353,7 @@ class GUITable(Table, GUIObject):
|
|||||||
row = self[row_index]
|
row = self[row_index]
|
||||||
return row.can_edit_cell(column_name)
|
return row.can_edit_cell(column_name)
|
||||||
|
|
||||||
def cancel_edits(self):
|
def cancel_edits(self) -> None:
|
||||||
"""Cancels the current edit operation.
|
"""Cancels the current edit operation.
|
||||||
|
|
||||||
If there's an :attr:`edited` row, it will be re-initialized (with :meth:`Row.load`).
|
If there's an :attr:`edited` row, it will be re-initialized (with :meth:`Row.load`).
|
||||||
@ -364,7 +371,7 @@ class GUITable(Table, GUIObject):
|
|||||||
self.edited = None
|
self.edited = None
|
||||||
self.view.refresh()
|
self.view.refresh()
|
||||||
|
|
||||||
def delete(self):
|
def delete(self) -> None:
|
||||||
"""Delete the currently selected rows.
|
"""Delete the currently selected rows.
|
||||||
|
|
||||||
Requires :meth:`_do_delete` for this to have any effect on the model. Cancels editing if
|
Requires :meth:`_do_delete` for this to have any effect on the model. Cancels editing if
|
||||||
@ -377,7 +384,7 @@ class GUITable(Table, GUIObject):
|
|||||||
if self:
|
if self:
|
||||||
self._do_delete()
|
self._do_delete()
|
||||||
|
|
||||||
def refresh(self, refresh_view=True):
|
def refresh(self, refresh_view: bool = True) -> None:
|
||||||
"""Empty the table and re-create its rows.
|
"""Empty the table and re-create its rows.
|
||||||
|
|
||||||
:meth:`_fill` is called after we emptied the table to create our rows. Previous sort order
|
:meth:`_fill` is called after we emptied the table to create our rows. Previous sort order
|
||||||
@ -399,7 +406,7 @@ class GUITable(Table, GUIObject):
|
|||||||
if refresh_view:
|
if refresh_view:
|
||||||
self.view.refresh()
|
self.view.refresh()
|
||||||
|
|
||||||
def save_edits(self):
|
def save_edits(self) -> None:
|
||||||
"""Commit user edits to the model.
|
"""Commit user edits to the model.
|
||||||
|
|
||||||
This is done by calling :meth:`Row.save`.
|
This is done by calling :meth:`Row.save`.
|
||||||
@ -410,7 +417,7 @@ class GUITable(Table, GUIObject):
|
|||||||
self.edited = None
|
self.edited = None
|
||||||
row.save()
|
row.save()
|
||||||
|
|
||||||
def sort_by(self, column_name, desc=False):
|
def sort_by(self, column_name: str, desc: bool = False) -> None:
|
||||||
"""Sort table by ``column_name``.
|
"""Sort table by ``column_name``.
|
||||||
|
|
||||||
Overrides :meth:`Table.sort_by`. After having performed sorting, calls
|
Overrides :meth:`Table.sort_by`. After having performed sorting, calls
|
||||||
@ -450,18 +457,18 @@ class Row:
|
|||||||
Of course, this is only default behavior. This can be overriden.
|
Of course, this is only default behavior. This can be overriden.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, table):
|
def __init__(self, table: GUITable) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.table = table
|
self.table = table
|
||||||
|
|
||||||
def _edit(self):
|
def _edit(self) -> None:
|
||||||
if self.table.edited is self:
|
if self.table.edited is self:
|
||||||
return
|
return
|
||||||
assert self.table.edited is None
|
assert self.table.edited is None
|
||||||
self.table.edited = self
|
self.table.edited = self
|
||||||
|
|
||||||
# --- Virtual
|
# --- Virtual
|
||||||
def can_edit(self):
|
def can_edit(self) -> bool:
|
||||||
"""(Virtual) Whether the whole row can be edited.
|
"""(Virtual) Whether the whole row can be edited.
|
||||||
|
|
||||||
By default, always returns ``True``. This is for the *whole* row. For individual cells, it's
|
By default, always returns ``True``. This is for the *whole* row. For individual cells, it's
|
||||||
@ -469,7 +476,7 @@ class Row:
|
|||||||
"""
|
"""
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def load(self):
|
def load(self) -> None:
|
||||||
"""(Virtual/Required) Loads up values from the model to be presented in the table.
|
"""(Virtual/Required) Loads up values from the model to be presented in the table.
|
||||||
|
|
||||||
Usually, our model instances contain values that are not quite ready for display. If you
|
Usually, our model instances contain values that are not quite ready for display. If you
|
||||||
@ -478,7 +485,7 @@ class Row:
|
|||||||
"""
|
"""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
def save(self):
|
def save(self) -> None:
|
||||||
"""(Virtual/Required) Saves user edits into your model.
|
"""(Virtual/Required) Saves user edits into your model.
|
||||||
|
|
||||||
If your table is editable, this is called when the user commits his changes. Usually, these
|
If your table is editable, this is called when the user commits his changes. Usually, these
|
||||||
@ -487,7 +494,7 @@ class Row:
|
|||||||
"""
|
"""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
def sort_key_for_column(self, column_name):
|
def sort_key_for_column(self, column_name: str) -> Any:
|
||||||
"""(Virtual) Return the value that is to be used to sort by column ``column_name``.
|
"""(Virtual) Return the value that is to be used to sort by column ``column_name``.
|
||||||
|
|
||||||
By default, looks for an attribute with the same name as ``column_name``, but with an
|
By default, looks for an attribute with the same name as ``column_name``, but with an
|
||||||
@ -500,7 +507,7 @@ class Row:
|
|||||||
return getattr(self, column_name)
|
return getattr(self, column_name)
|
||||||
|
|
||||||
# --- Public
|
# --- Public
|
||||||
def can_edit_cell(self, column_name):
|
def can_edit_cell(self, column_name: str) -> bool:
|
||||||
"""Returns whether cell for column ``column_name`` can be edited.
|
"""Returns whether cell for column ``column_name`` can be edited.
|
||||||
|
|
||||||
By the default, the check is done in many steps:
|
By the default, the check is done in many steps:
|
||||||
@ -530,7 +537,7 @@ class Row:
|
|||||||
return False
|
return False
|
||||||
return bool(getattr(prop, "fset", None))
|
return bool(getattr(prop, "fset", None))
|
||||||
|
|
||||||
def get_cell_value(self, attrname):
|
def get_cell_value(self, attrname: str) -> Any:
|
||||||
"""Get cell value for ``attrname``.
|
"""Get cell value for ``attrname``.
|
||||||
|
|
||||||
By default, does a simple ``getattr()``, but it is used to allow subclasses to have
|
By default, does a simple ``getattr()``, but it is used to allow subclasses to have
|
||||||
@ -540,7 +547,7 @@ class Row:
|
|||||||
attrname = "from_"
|
attrname = "from_"
|
||||||
return getattr(self, attrname)
|
return getattr(self, attrname)
|
||||||
|
|
||||||
def set_cell_value(self, attrname, value):
|
def set_cell_value(self, attrname: str, value: Any) -> None:
|
||||||
"""Set cell value to ``value`` for ``attrname``.
|
"""Set cell value to ``value`` for ``attrname``.
|
||||||
|
|
||||||
By default, does a simple ``setattr()``, but it is used to allow subclasses to have
|
By default, does a simple ``setattr()``, but it is used to allow subclasses to have
|
||||||
|
@ -7,6 +7,9 @@
|
|||||||
# http://www.gnu.org/licenses/gpl-3.0.html
|
# http://www.gnu.org/licenses/gpl-3.0.html
|
||||||
|
|
||||||
|
|
||||||
|
from typing import Any, Callable, Generator, Iterator, List, Union
|
||||||
|
|
||||||
|
|
||||||
class JobCancelled(Exception):
|
class JobCancelled(Exception):
|
||||||
"The user has cancelled the job"
|
"The user has cancelled the job"
|
||||||
|
|
||||||
@ -36,7 +39,7 @@ class Job:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
# ---Magic functions
|
# ---Magic functions
|
||||||
def __init__(self, job_proportions, callback):
|
def __init__(self, job_proportions: Union[List[int], int], callback: Callable) -> None:
|
||||||
"""Initialize the Job with 'jobcount' jobs. Start every job with
|
"""Initialize the Job with 'jobcount' jobs. Start every job with
|
||||||
start_job(). Every time the job progress is updated, 'callback' is called
|
start_job(). Every time the job progress is updated, 'callback' is called
|
||||||
'callback' takes a 'progress' int param, and a optional 'desc'
|
'callback' takes a 'progress' int param, and a optional 'desc'
|
||||||
@ -55,12 +58,12 @@ class Job:
|
|||||||
self._currmax = 1
|
self._currmax = 1
|
||||||
|
|
||||||
# ---Private
|
# ---Private
|
||||||
def _subjob_callback(self, progress, desc=""):
|
def _subjob_callback(self, progress: int, desc: str = "") -> bool:
|
||||||
"""This is the callback passed to children jobs."""
|
"""This is the callback passed to children jobs."""
|
||||||
self.set_progress(progress, desc)
|
self.set_progress(progress, desc)
|
||||||
return True # if JobCancelled has to be raised, it will be at the highest level
|
return True # if JobCancelled has to be raised, it will be at the highest level
|
||||||
|
|
||||||
def _do_update(self, desc):
|
def _do_update(self, desc: str) -> None:
|
||||||
"""Calls the callback function with a % progress as a parameter.
|
"""Calls the callback function with a % progress as a parameter.
|
||||||
|
|
||||||
The parameter is a int in the 0-100 range.
|
The parameter is a int in the 0-100 range.
|
||||||
@ -78,13 +81,16 @@ class Job:
|
|||||||
raise JobCancelled()
|
raise JobCancelled()
|
||||||
|
|
||||||
# ---Public
|
# ---Public
|
||||||
def add_progress(self, progress=1, desc=""):
|
def add_progress(self, progress: int = 1, desc: str = "") -> None:
|
||||||
self.set_progress(self._progress + progress, desc)
|
self.set_progress(self._progress + progress, desc)
|
||||||
|
|
||||||
def check_if_cancelled(self):
|
def check_if_cancelled(self) -> None:
|
||||||
self._do_update("")
|
self._do_update("")
|
||||||
|
|
||||||
def iter_with_progress(self, iterable, desc_format=None, every=1, count=None):
|
# TODO type hint iterable
|
||||||
|
def iter_with_progress(
|
||||||
|
self, iterable, desc_format: Union[str, None] = None, every: int = 1, count: Union[int, None] = None
|
||||||
|
) -> Generator[Any, None, None]:
|
||||||
"""Iterate through ``iterable`` while automatically adding progress.
|
"""Iterate through ``iterable`` while automatically adding progress.
|
||||||
|
|
||||||
WARNING: We need our iterable's length. If ``iterable`` is not a sequence (that is,
|
WARNING: We need our iterable's length. If ``iterable`` is not a sequence (that is,
|
||||||
@ -107,7 +113,7 @@ class Job:
|
|||||||
desc = desc_format % (count, count)
|
desc = desc_format % (count, count)
|
||||||
self.set_progress(100, desc)
|
self.set_progress(100, desc)
|
||||||
|
|
||||||
def start_job(self, max_progress=100, desc=""):
|
def start_job(self, max_progress: int = 100, desc: str = "") -> None:
|
||||||
"""Begin work on the next job. You must not call start_job more than
|
"""Begin work on the next job. You must not call start_job more than
|
||||||
'jobcount' (in __init__) times.
|
'jobcount' (in __init__) times.
|
||||||
'max' is the job units you are to perform.
|
'max' is the job units you are to perform.
|
||||||
@ -122,7 +128,7 @@ class Job:
|
|||||||
self._currmax = max(1, max_progress)
|
self._currmax = max(1, max_progress)
|
||||||
self._do_update(desc)
|
self._do_update(desc)
|
||||||
|
|
||||||
def start_subjob(self, job_proportions, desc=""):
|
def start_subjob(self, job_proportions: Union[List[int], int], desc: str = "") -> "Job":
|
||||||
"""Starts a sub job. Use this when you want to split a job into
|
"""Starts a sub job. Use this when you want to split a job into
|
||||||
multiple smaller jobs. Pretty handy when starting a process where you
|
multiple smaller jobs. Pretty handy when starting a process where you
|
||||||
know how many subjobs you will have, but don't know the work unit count
|
know how many subjobs you will have, but don't know the work unit count
|
||||||
@ -132,7 +138,7 @@ class Job:
|
|||||||
self.start_job(100, desc)
|
self.start_job(100, desc)
|
||||||
return Job(job_proportions, self._subjob_callback)
|
return Job(job_proportions, self._subjob_callback)
|
||||||
|
|
||||||
def set_progress(self, progress, desc=""):
|
def set_progress(self, progress: int, desc: str = "") -> None:
|
||||||
"""Sets the progress of the current job to 'progress', and call the
|
"""Sets the progress of the current job to 'progress', and call the
|
||||||
callback
|
callback
|
||||||
"""
|
"""
|
||||||
@ -143,29 +149,29 @@ class Job:
|
|||||||
|
|
||||||
|
|
||||||
class NullJob:
|
class NullJob:
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs) -> None:
|
||||||
# Null job does nothing
|
# Null job does nothing
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def add_progress(self, *args, **kwargs):
|
def add_progress(self, *args, **kwargs) -> None:
|
||||||
# Null job does nothing
|
# Null job does nothing
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def check_if_cancelled(self):
|
def check_if_cancelled(self) -> None:
|
||||||
# Null job does nothing
|
# Null job does nothing
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def iter_with_progress(self, sequence, *args, **kwargs):
|
def iter_with_progress(self, sequence, *args, **kwargs) -> Iterator:
|
||||||
return iter(sequence)
|
return iter(sequence)
|
||||||
|
|
||||||
def start_job(self, *args, **kwargs):
|
def start_job(self, *args, **kwargs) -> None:
|
||||||
# Null job does nothing
|
# Null job does nothing
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def start_subjob(self, *args, **kwargs):
|
def start_subjob(self, *args, **kwargs) -> "NullJob":
|
||||||
return NullJob()
|
return NullJob()
|
||||||
|
|
||||||
def set_progress(self, *args, **kwargs):
|
def set_progress(self, *args, **kwargs) -> None:
|
||||||
# Null job does nothing
|
# Null job does nothing
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
import sys
|
import sys
|
||||||
|
from typing import Callable, Tuple, Union
|
||||||
|
|
||||||
from hscommon.jobprogress.job import Job, JobInProgressError, JobCancelled
|
from hscommon.jobprogress.job import Job, JobInProgressError, JobCancelled
|
||||||
|
|
||||||
@ -28,15 +29,15 @@ class ThreadedJobPerformer:
|
|||||||
last_error = None
|
last_error = None
|
||||||
|
|
||||||
# --- Protected
|
# --- Protected
|
||||||
def create_job(self):
|
def create_job(self) -> Job:
|
||||||
if self._job_running:
|
if self._job_running:
|
||||||
raise JobInProgressError()
|
raise JobInProgressError()
|
||||||
self.last_progress = -1
|
self.last_progress: Union[int, None] = -1
|
||||||
self.last_desc = ""
|
self.last_desc = ""
|
||||||
self.job_cancelled = False
|
self.job_cancelled = False
|
||||||
return Job(1, self._update_progress)
|
return Job(1, self._update_progress)
|
||||||
|
|
||||||
def _async_run(self, *args):
|
def _async_run(self, *args) -> None:
|
||||||
target = args[0]
|
target = args[0]
|
||||||
args = tuple(args[1:])
|
args = tuple(args[1:])
|
||||||
self._job_running = True
|
self._job_running = True
|
||||||
@ -52,7 +53,7 @@ class ThreadedJobPerformer:
|
|||||||
self._job_running = False
|
self._job_running = False
|
||||||
self.last_progress = None
|
self.last_progress = None
|
||||||
|
|
||||||
def reraise_if_error(self):
|
def reraise_if_error(self) -> None:
|
||||||
"""Reraises the error that happened in the thread if any.
|
"""Reraises the error that happened in the thread if any.
|
||||||
|
|
||||||
Call this after the caller of run_threaded detected that self._job_running returned to False
|
Call this after the caller of run_threaded detected that self._job_running returned to False
|
||||||
@ -60,13 +61,13 @@ class ThreadedJobPerformer:
|
|||||||
if self.last_error is not None:
|
if self.last_error is not None:
|
||||||
raise self.last_error.with_traceback(self.last_traceback)
|
raise self.last_error.with_traceback(self.last_traceback)
|
||||||
|
|
||||||
def _update_progress(self, newprogress, newdesc=""):
|
def _update_progress(self, newprogress: int, newdesc: str = "") -> bool:
|
||||||
self.last_progress = newprogress
|
self.last_progress = newprogress
|
||||||
if newdesc:
|
if newdesc:
|
||||||
self.last_desc = newdesc
|
self.last_desc = newdesc
|
||||||
return not self.job_cancelled
|
return not self.job_cancelled
|
||||||
|
|
||||||
def run_threaded(self, target, args=()):
|
def run_threaded(self, target: Callable, args: Tuple = ()) -> None:
|
||||||
if self._job_running:
|
if self._job_running:
|
||||||
raise JobInProgressError()
|
raise JobInProgressError()
|
||||||
args = (target,) + args
|
args = (target,) + args
|
||||||
|
Loading…
x
Reference in New Issue
Block a user