Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[viewer] provide more task info in task list #102

Merged
merged 5 commits into from
Sep 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/lifeblood/scheduler/task_processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@ async def _awaiter(self, processor_to_run, task_row, node_config, abort_state: T
# splits
if process_result._split_attribs is not None:
split_count = len(process_result._split_attribs)
ui_task_delta.state = TaskState.SPLITTED # note that split_task ALSO adds a task delta event with state update
for attr_dict, split_task_id in zip(process_result._split_attribs, await self.split_task(task_id, split_count, con)):
async with con.execute('SELECT attributes FROM "tasks" WHERE "id" = ?', (split_task_id,)) as cur:
split_task_dict = await cur.fetchone()
Expand Down
174 changes: 101 additions & 73 deletions src/lifeblood_viewer/graphics_items.py

Large diffs are not rendered by default.

38 changes: 38 additions & 0 deletions src/lifeblood_viewer/network_item.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
from PySide2.QtWidgets import QGraphicsItem


class NetworkItem(QGraphicsItem):
def __init__(self, id):
super().__init__()

# cheat cuz Shiboken.Object does not respect mro
mro = self.__class__.mro()
cur_mro_i = mro.index(NetworkItem)
if len(mro) > cur_mro_i + 2:
super(mro[cur_mro_i+2], self).__init__()

self.__id = id

def get_id(self):
return self.__id


class NetworkItemWithUI(NetworkItem):
def item_updated(self, *, redraw: bool = False, ui: bool = False):
"""
should be called when item's state is changed
:param redraw: True if item itself redraw is needed
:param ui: True if item's parameter ui redraw is needed
"""
if redraw:
self.update()
if ui:
self.update() # currently contents and UI are drawn always together, so this will do
# but in future TODO: invalidate only UI layer

def draw_imgui_elements(self, drawing_widget):
"""
this should only be called from active opengl context!
:return:
"""
pass
39 changes: 39 additions & 0 deletions src/lifeblood_viewer/network_item_watchers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
from .network_item import NetworkItem

from typing import FrozenSet, Set


class NetworkItemWatcher:
def item_was_updated(self, item: NetworkItem):
pass


class WatchableNetworkItem:
def __init__(self):
super().__init__()
self.__task_watchers: Set["NetworkItemWatcher"] = set()

def item_watchers(self) -> FrozenSet["NetworkItemWatcher"]:
return frozenset(self.__task_watchers)

def add_item_watcher(self, watcher: "NetworkItemWatcher"):
"""
watcher observes the task, therefore all task's metadata must be updated,
watchers need to be properly notified on update too.
"""
self.__task_watchers.add(watcher)

def remove_item_watcher(self, watcher: "NetworkItemWatcher"):
self.__task_watchers.remove(watcher)

def has_item_watcher(self, watcher: "NetworkItemWatcher") -> bool:
return watcher in self.__task_watchers


class WatchableNetworkItemProxy(WatchableNetworkItem, NetworkItemWatcher):
"""
is NOT being watched itself, but propagates the watch to it's own watchable items
"""
def item_was_updated(self, item: NetworkItem):
for watcher in self.item_watchers():
watcher.item_was_updated(item)
34 changes: 30 additions & 4 deletions src/lifeblood_viewer/nodeeditor_windows/ui_task_list_window.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,36 @@
import imgui
from datetime import timedelta

from lifeblood.enums import TaskState
from lifeblood_viewer.nodeeditor import NodeEditor
from lifeblood_viewer.ui_scene_elements import ImguiViewWindow
from ..graphics_items import Node
from ..graphics_items import Node, Task
from ..network_item_watchers import NetworkItemWatcher
from PySide2.QtCore import QPoint
from PySide2.QtGui import QCursor

from typing import Optional

class TaskListWindow(ImguiViewWindow):

class TaskListWindow(ImguiViewWindow, NetworkItemWatcher):
def __init__(self, editor_widget: NodeEditor):
super().__init__(editor_widget, 'Task List')
self.__displayed_node: Optional[Node] = None
self.__pinned = False

def set_display_node(self, display_node: Node):
def set_display_node(self, display_node: Optional[Node]):
if display_node == self.__displayed_node:
return
if self.__displayed_node:
self.__displayed_node.remove_item_watcher(self)
self.__displayed_node = display_node
if self.__displayed_node:
self.__displayed_node.add_item_watcher(self)
self._update_title()

def on_closed(self):
self.set_display_node(None)

def pin(self, pin: bool = True):
self.__pinned = pin
self._update_title()
Expand All @@ -35,13 +47,15 @@ def draw_window_elements(self):
if self.__displayed_node is not None:
imgui.text(f'node: {self.__displayed_node.node_name()}')
base_name = f'table_{self._imgui_key_name()}'
with imgui.begin_table(f'tasks##{base_name}', 4, imgui.TABLE_SIZING_STRETCH_PROP |
with imgui.begin_table(f'tasks##{base_name}', 6, imgui.TABLE_SIZING_STRETCH_PROP |
imgui.TABLE_BORDERS_INNER_VERTICAL |
imgui.TABLE_ROW_BACKGROUND
) as table:
if table.opened:
imgui.table_setup_column('ID', imgui.TABLE_COLUMN_DEFAULT_SORT)
imgui.table_setup_column('frame(s)')
imgui.table_setup_column('name')
imgui.table_setup_column('total runtime')
imgui.table_setup_column('paused', imgui.TABLE_COLUMN_WIDTH_FIXED, 64)
imgui.table_setup_column('state', imgui.TABLE_COLUMN_WIDTH_FIXED, 128.0)
imgui.table_headers_row()
Expand Down Expand Up @@ -73,9 +87,21 @@ def draw_window_elements(self):

imgui.table_next_column()

if frames := task.attributes().get('frames'):
if len(frames) == 1:
imgui.text(str(frames[0]))
else:
imgui.text(f'{frames[0]}-{frames[-1]}')
else:
imgui.text('')
imgui.table_next_column()

imgui.text(str(task.name()))
imgui.table_next_column()

imgui.text(str(timedelta(seconds=int(task.invocations_total_time(only_last_per_node=True)))))
imgui.table_next_column()

if task.paused():
imgui.text('paused')
imgui.table_next_column()
Expand Down
4 changes: 2 additions & 2 deletions src/lifeblood_viewer/scene_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,7 @@ def _my_do_longop(self, longop: LongOperation):
self.__scene._send_node_parameters_change(node_id, [param], LongOperationData(longop))
node = self.__scene.get_node(node_id)
if node:
node.update_ui()
node.item_updated(ui=True)
yield

def _my_undo_longop(self, longop: LongOperation):
Expand All @@ -362,7 +362,7 @@ def _my_undo_longop(self, longop: LongOperation):
# update node ui, just in case
node = self.__scene.get_node(node_id)
if node:
node.update_ui()
node.node.item_updated(ui=True)

def __str__(self):
return f'Param Changed {self.__param_name} @ {self.__node_sid}'
15 changes: 15 additions & 0 deletions src/lifeblood_viewer/ui_elements_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ class ImguiWindow(ImguiElement):
__unique_nums: Dict[str, int] = {}

def __init__(self, title: str = '', closable: bool = True):
super().__init__()
self.__opened = False
self.__just_opened = False
# wanted to use uuid.uuid4().hex below, but that would bloat imgui's internal db after multiple launches
Expand Down Expand Up @@ -48,6 +49,18 @@ def _close(self):
"""
self.__opened = False

def on_first_opened(self):
"""
override this to react to fist opened event
"""
pass

def on_closed(self):
"""
override this to react to closed event
"""
pass

def _was_just_opened(self) -> bool:
"""
call ONLY from draw_window_elements
Expand Down Expand Up @@ -85,11 +98,13 @@ def draw(self):

(expanded, opened) = imgui.begin(self._imgui_window_name(), closable=self.__closable)
if not opened:
self.on_closed()
imgui.end()
self._close()
return
try:
self.__focused_last_draw = imgui.is_window_focused()
self.on_first_opened()
self.draw_window_elements()
finally:
imgui.end()
Expand Down
Loading