Skip to content

Commit

Permalink
Massively improve history table
Browse files Browse the repository at this point in the history
- Table fits the window and follows its size
- Reversed display order by default
- Columns can be rearranged, resized and hidden
- Digits are aligned better
- Selecting a row pre-populates the generator
  • Loading branch information
rdoursenaud committed Jan 5, 2025
1 parent 11ba8ef commit c9f3d82
Show file tree
Hide file tree
Showing 6 changed files with 101 additions and 110 deletions.
1 change: 0 additions & 1 deletion src/midiexplorer/gui/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,6 @@ def init():
log_win_textbox = dpg.get_item_children('log_win', slot=midiexplorer.gui.helpers.constants.slots.Slots.MOST)[2]
dpg.bind_item_font(log_win_textbox, 'mono_font')

dpg.bind_item_font('hist_data_table_headers', 'mono_font')
dpg.bind_item_font('hist_data_table', 'mono_font')

if DEBUG:
Expand Down
8 changes: 0 additions & 8 deletions src/midiexplorer/gui/helpers/probe.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,4 @@ def add(timestamp: Timestamp, source: str, data: mido.Message) -> None:

logger.log_debug(f"Adding data from {source} to probe at {timestamp}: {data!r}")

# FIXME: data.time can also be 0 when using rtmidi time delta. How do we discriminate? Use another property in mido?
delta = None
if data.time and DEBUG:
delta = data.time
logger.log_debug("Timing: Using rtmidi time delta")
else:
logger.log_debug("Timing: Rtmidi time delta not available. Computing timestamp locally.")

midiexplorer.gui.windows.mon.data.update_gui_monitor(data)
3 changes: 2 additions & 1 deletion src/midiexplorer/gui/windows/conn.py
Original file line number Diff line number Diff line change
Expand Up @@ -694,8 +694,9 @@ def handle_received_data(timestamp: Timestamp, source: str, dest: str, midi_data
if probe_thru_user_data: # Handle soft-thru
# logger.log(f"Probe thru has user data: {probe_thru_user_data}")
logger.log_debug("Echoing MIDI data to probe thru")
thru_timestamp = Timestamp()
probe_thru_user_data.port.send(midi_data)
hist.data.add(midi_data, "PROBE: Thru", probe_thru_user_data.port.name, timestamp)
hist.data.add(midi_data, "PROBE: Thru", probe_thru_user_data.port.name, thru_timestamp)
add(
timestamp=timestamp,
source=source,
Expand Down
2 changes: 1 addition & 1 deletion src/midiexplorer/gui/windows/gen.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ def decode(sender: int | str, app_data: Any, user_data: Optional[Any]) -> None:

if warning is None:
logger.log_debug(f"Raw message {app_data} decoded to: {decoded!r}.")
dpg.set_value('generator_decoded_message', repr(decoded))
dpg.set_value('generator_decoded_message', decoded)
dpg.enable_item('generator_send_button')
dpg.set_item_user_data('generator_send_button', decoded)
else:
Expand Down
74 changes: 32 additions & 42 deletions src/midiexplorer/gui/windows/hist/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,23 +13,24 @@

from midiexplorer.__config__ import DEBUG
from midiexplorer.gui.helpers.callbacks.debugging import enable as enable_dpg_cb_debugging
from midiexplorer.gui.windows.hist.data import init_details_table_data, clear_hist_data_table
from midiexplorer.gui.windows.hist.data import clear_hist_data_table


def _add_table_columns():
dpg.add_table_column(label="Source")
dpg.add_table_column(label="Destination")
dpg.add_table_column(label="Timestamp (s)")
dpg.add_table_column(label="Delta (ms)")
dpg.add_table_column(label="Source")
dpg.add_table_column(label="Destination")
dpg.add_table_column(label="Raw Message (HEX)")
if DEBUG:
dpg.add_table_column(label="Decoded Message")
dpg.add_table_column(label="Decoded\nMessage")
dpg.add_table_column(label="Status")
dpg.add_table_column(label="Channel")
dpg.add_table_column(label="Data 1")
dpg.add_table_column(label="Data 2")
dpg.add_table_column(label="Select", width_fixed=True, width=0, no_header_width=True, no_header_label=True)


def create() -> None:
"""Creates the history window.
Expand All @@ -48,8 +49,8 @@ def create() -> None:
# History window
# --------------------
with dpg.window(
tag='hist_win',
label="History",
tag='hist_win',
width=900,
height=hist_win_height,
no_close=True,
Expand All @@ -59,48 +60,38 @@ def create() -> None:
# -------------------
# History data table
# -------------------
hist_table_height = 470
if DEBUG:
hist_table_height = 355
dpg.add_child_window(tag='hist_table_container', height=hist_table_height, border=False)

# Separate headers
# FIXME: workaround table scrolling not implemented upstream yet to have static headers
# dpg.add_child_window(tag='hist_det_headers', label="Details headers", height=5, border=False)
with dpg.table(parent='hist_table_container',
tag='hist_data_table_headers',
header_row=True,
freeze_rows=1,
policy=dpg.mvTable_SizingStretchSame):
_add_table_columns()

# Buttons
with dpg.group(parent='hist_win', horizontal=True):
dpg.add_text("Order:")
dpg.add_radio_button(items=("Reversed", "Auto-Scroll"), label="Mode", tag='hist_data_table_mode',
default_value="Reversed", horizontal=True)
dpg.add_checkbox(label="Selection to Generator", tag='hist_data_to_gen', default_value=True)
dpg.add_button(label="Clear", callback=clear_hist_data_table)

# TODO: Allow sorting
# TODO: Show/hide columns
# TODO: timegraph?

# Content details
hist_det_height = 420
if DEBUG:
hist_det_height = 305
dpg.add_child_window(parent='hist_table_container', tag='hist_det', label="Details", height=hist_det_height, border=False)
with dpg.table(parent='hist_det',
tag='hist_data_table',
header_row=False, # FIXME: True when table scrolling will be implemented upstream
freeze_rows=0, # FIXME: 1 when table scrolling will be implemented upstream
row_background=True,
borders_innerV=True,
policy=dpg.mvTable_SizingStretchSame,
# scrollY=True, # FIXME: Scroll the table instead of the window when available upstream
):
with dpg.table(
tag='hist_data_table',
parent='hist_win',
header_row=True,
#clipper= True,
policy=dpg.mvTable_SizingStretchProp,
freeze_rows=1,
# sort_multi=True,
# sort_tristate=True, # TODO: implement
resizable=True,
reorderable=True, # TODO: TableSetupColumn()?
hideable=True,
# sortable=True, # TODO: TableGetSortSpecs()?
context_menu_in_body=True,
row_background=True,
borders_innerV=True,
scrollY=True,
):
_add_table_columns()
init_details_table_data()

# Buttons
# FIXME: separated to not scroll with table child window until table scrolling is supported
dpg.add_child_window(parent='hist_table_container', tag='hist_btns', label="Buttons", border=False)
with dpg.group(parent='hist_btns', horizontal=True):
dpg.add_checkbox(tag='hist_data_table_autoscroll', label="Auto-Scroll", default_value=True)
dpg.add_button(label="Clear", callback=clear_hist_data_table)


def toggle(sender: int | str, app_data: Any, user_data: Optional[Any]) -> None:
Expand All @@ -122,4 +113,3 @@ def toggle(sender: int | str, app_data: Any, user_data: Optional[Any]) -> None:
menu_item = 'menu_tools_history'
if sender != menu_item: # Update menu checkmark when coming from the shortcut handler
dpg.set_value(menu_item, not dpg.get_value(menu_item))

Loading

0 comments on commit c9f3d82

Please sign in to comment.