Merge pull request #29 from joshlay/async_improvements
Async improvements
This commit is contained in:
commit
30f1f1f247
4 changed files with 86 additions and 71 deletions
|
@ -1,6 +1,6 @@
|
||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "amdgpu-stats"
|
name = "amdgpu-stats"
|
||||||
version = "0.1.15"
|
version = "0.1.16"
|
||||||
description = "A module/TUI for AMD GPU statistics"
|
description = "A module/TUI for AMD GPU statistics"
|
||||||
authors = ["Josh Lay <pypi@jlay.io>"]
|
authors = ["Josh Lay <pypi@jlay.io>"]
|
||||||
repository = "https://github.com/joshlay/amdgpu_stats"
|
repository = "https://github.com/joshlay/amdgpu_stats"
|
||||||
|
@ -10,7 +10,7 @@ documentation = "https://amdgpu-stats.readthedocs.io/en/latest/"
|
||||||
|
|
||||||
[tool.poetry.dependencies]
|
[tool.poetry.dependencies]
|
||||||
python = "^3.9"
|
python = "^3.9"
|
||||||
textual = ">=0.16.0"
|
textual = ">=0.18.0"
|
||||||
humanfriendly = ">=10.0"
|
humanfriendly = ">=10.0"
|
||||||
|
|
||||||
[tool.poetry.scripts]
|
[tool.poetry.scripts]
|
||||||
|
|
BIN
screens/main.png
BIN
screens/main.png
Binary file not shown.
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
|
@ -18,6 +18,7 @@ from datetime import datetime
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from rich.text import Text
|
from rich.text import Text
|
||||||
|
from textual import work
|
||||||
from textual.binding import Binding
|
from textual.binding import Binding
|
||||||
from textual.app import App, ComposeResult
|
from textual.app import App, ComposeResult
|
||||||
from textual.containers import Container
|
from textual.containers import Container
|
||||||
|
@ -109,7 +110,6 @@ class GPUStatsWidget(Static):
|
||||||
|
|
||||||
def __init__(self, *args, cards=None, **kwargs):
|
def __init__(self, *args, cards=None, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
self.cards = cards
|
self.cards = cards
|
||||||
self.text_log = TextLog(highlight=True,
|
self.text_log = TextLog(highlight=True,
|
||||||
markup=True,
|
markup=True,
|
||||||
|
@ -121,12 +121,16 @@ class GPUStatsWidget(Static):
|
||||||
classes='stat_table')
|
classes='stat_table')
|
||||||
self.tabbed_container = TabbedContent()
|
self.tabbed_container = TabbedContent()
|
||||||
|
|
||||||
async def on_mount(self) -> None:
|
def on_mount(self) -> None:
|
||||||
'''Fires when stats widget 'mounted', behaves like on first showing'''
|
'''Fires when stats widget 'mounted', behaves like on first showing'''
|
||||||
|
self.update_log("[bold green]App started, logging begin!")
|
||||||
|
self.update_log(f"[bold]Discovered AMD GPUs: [/]{list(AMDGPU_CARDS)}")
|
||||||
# construct the table columns
|
# construct the table columns
|
||||||
columns = list(self.get_column_data_mapping(None).keys())
|
columns = list(self.get_column_data_mapping(None).keys())
|
||||||
|
self.update_log('[bold]Stats table columns:')
|
||||||
for column in columns:
|
for column in columns:
|
||||||
self.stats_table.add_column(label=column, key=column)
|
self.stats_table.add_column(label=column, key=column)
|
||||||
|
self.update_log(f' - "{column}"')
|
||||||
# do a one-off stat collection, populate table before the interval
|
# do a one-off stat collection, populate table before the interval
|
||||||
self.get_stats()
|
self.get_stats()
|
||||||
# stand up the stat-collecting interval, twice per second
|
# stand up the stat-collecting interval, twice per second
|
||||||
|
@ -134,13 +138,9 @@ class GPUStatsWidget(Static):
|
||||||
|
|
||||||
def compose(self) -> ComposeResult:
|
def compose(self) -> ComposeResult:
|
||||||
"""Create child widgets."""
|
"""Create child widgets."""
|
||||||
self.update_log("[bold green]App started, logging begin!")
|
|
||||||
self.update_log(f"[bold]Discovered AMD GPUs: [/]{list(AMDGPU_CARDS)}")
|
|
||||||
# Add the TabbedContent widget
|
|
||||||
with self.tabbed_container:
|
with self.tabbed_container:
|
||||||
with TabPane("Stats", id="tab_stats"):
|
with TabPane("Stats", id="tab_stats"):
|
||||||
yield self.stats_table
|
yield self.stats_table
|
||||||
self.update_log('[bold]App: [/]created stats table')
|
|
||||||
with TabPane("Logs", id="tab_logs"):
|
with TabPane("Logs", id="tab_logs"):
|
||||||
yield self.text_log
|
yield self.text_log
|
||||||
|
|
||||||
|
@ -148,13 +148,10 @@ class GPUStatsWidget(Static):
|
||||||
"""Update the TextLog widget with a new message."""
|
"""Update the TextLog widget with a new message."""
|
||||||
self.text_log.write(message)
|
self.text_log.write(message)
|
||||||
|
|
||||||
def get_stats(self):
|
@work(exclusive=True)
|
||||||
|
async def get_stats(self):
|
||||||
'''Function to fetch stats / update the table for each AMD GPU found'''
|
'''Function to fetch stats / update the table for each AMD GPU found'''
|
||||||
for card in self.cards:
|
for card in self.cards:
|
||||||
# annoyingly, must retain the styling used w/ the cols above
|
|
||||||
# otherwise stats won't update
|
|
||||||
# noticed when fiddling 'style' below between new/update 'Text'
|
|
||||||
# should store their IDs on creation and map those instead
|
|
||||||
self.data = self.get_column_data_mapping(card)
|
self.data = self.get_column_data_mapping(card)
|
||||||
# handle the table data appopriately
|
# handle the table data appopriately
|
||||||
# if needs populated anew or updated
|
# if needs populated anew or updated
|
||||||
|
@ -166,7 +163,7 @@ class GPUStatsWidget(Static):
|
||||||
]
|
]
|
||||||
self.stats_table.add_row(*styled_row, key=card)
|
self.stats_table.add_row(*styled_row, key=card)
|
||||||
hwmon_dir = AMDGPU_CARDS[card]
|
hwmon_dir = AMDGPU_CARDS[card]
|
||||||
self.update_log(f"[bold]Table: [/]added row for '{card}', info dir: '{hwmon_dir}'")
|
self.update_log(f"[bold]Stats table: [/]added row for '{card}', info dir: '{hwmon_dir}'")
|
||||||
else:
|
else:
|
||||||
# Update existing rows, retaining styling/justification
|
# Update existing rows, retaining styling/justification
|
||||||
for column, value in self.data.items():
|
for column, value in self.data.items():
|
||||||
|
@ -186,6 +183,7 @@ class app(App): # pylint: disable=invalid-name
|
||||||
|
|
||||||
# set the title - same as the class, but with spaces
|
# set the title - same as the class, but with spaces
|
||||||
TITLE = 'AMD GPU Stats'
|
TITLE = 'AMD GPU Stats'
|
||||||
|
SUB_TITLE = ''
|
||||||
|
|
||||||
# setup keybinds
|
# setup keybinds
|
||||||
BINDINGS = [
|
BINDINGS = [
|
||||||
|
@ -205,7 +203,8 @@ class app(App): # pylint: disable=invalid-name
|
||||||
yield Container(self.stats_widget)
|
yield Container(self.stats_widget)
|
||||||
yield Footer()
|
yield Footer()
|
||||||
|
|
||||||
def action_custom_dark(self) -> None:
|
@work(exclusive=True)
|
||||||
|
async def action_custom_dark(self) -> None:
|
||||||
"""An action to toggle dark mode.
|
"""An action to toggle dark mode.
|
||||||
|
|
||||||
Wraps 'action_toggle_dark' with our logging"""
|
Wraps 'action_toggle_dark' with our logging"""
|
||||||
|
@ -225,13 +224,28 @@ class app(App): # pylint: disable=invalid-name
|
||||||
self.screen.mount(Notification(message))
|
self.screen.mount(Notification(message))
|
||||||
self.update_log(message)
|
self.update_log(message)
|
||||||
|
|
||||||
def action_custom_tab(self) -> None:
|
|
||||||
"""Toggle between the 'Stats' and 'Logs' tabs"""
|
|
||||||
if self.stats_widget.tabbed_container.active == "tab_stats":
|
|
||||||
self.stats_widget.tabbed_container.active = 'tab_logs'
|
|
||||||
else:
|
|
||||||
self.stats_widget.tabbed_container.active = 'tab_stats'
|
|
||||||
|
|
||||||
def update_log(self, message: str) -> None:
|
def update_log(self, message: str) -> None:
|
||||||
"""Update the TextLog widget with a new message."""
|
"""Update the TextLog widget with a new message."""
|
||||||
self.stats_widget.text_log.write(message)
|
self.stats_widget.text_log.write(message)
|
||||||
|
|
||||||
|
def action_custom_tab(self) -> None:
|
||||||
|
"""Toggle between the 'Stats' and 'Logs' tabs"""
|
||||||
|
if self.stats_widget.tabbed_container.active == "tab_stats":
|
||||||
|
new_tab = 'tab_logs'
|
||||||
|
else:
|
||||||
|
new_tab = 'tab_stats'
|
||||||
|
self.stats_widget.tabbed_container.active = new_tab
|
||||||
|
# craft a 'tab activated' (changed) event
|
||||||
|
# used to set the subtitle via event handling
|
||||||
|
event = TabbedContent.TabActivated(tabbed_content=self.stats_widget.tabbed_container,
|
||||||
|
tab=new_tab)
|
||||||
|
self.on_tabbed_content_tab_activated(event)
|
||||||
|
|
||||||
|
def on_tabbed_content_tab_activated(self, event: TabbedContent.TabActivated):
|
||||||
|
"""Listens to 'TabActivated' event, sets subtitle"""
|
||||||
|
# if self.tabbed_container.active == "tab_logs":
|
||||||
|
active_title = event.tabbed_content.active.title().replace('Tab_', '')
|
||||||
|
if event.tabbed_content.active == "tab_logs":
|
||||||
|
self.sub_title = active_title # pylint: disable=attribute-defined-outside-init
|
||||||
|
elif event.tabbed_content.active == "tab_stats":
|
||||||
|
self.sub_title = f'{active_title}: {list(AMDGPU_CARDS)}' # pylint: disable=attribute-defined-outside-init
|
||||||
|
|
Reference in a new issue