Skip to content
Snippets Groups Projects
Commit 420ccdea authored by Allen, Bruce (CIV)'s avatar Allen, Bruce (CIV)
Browse files

add graph list table

parent bdb318c1
No related branches found
No related tags found
No related merge requests found
......@@ -15,8 +15,7 @@ class GraphListFilterManager(QObject):
def __init__(self, gui_manager):
super().__init__()
self.graph_list_proxy_model = gui_manager.graph_list_view \
.graph_list_proxy_model
self.graph_list_proxy_model = gui_manager.graph_list_proxy_model
self.filter_event_dialog_wrapper = FilterEventDialogWrapper(
gui_manager.w, gui_manager.graphs_manager,
......
......@@ -5,7 +5,6 @@ from PySide6.QtCore import Qt
from PySide6.QtCore import QModelIndex
from PySide6.QtCore import QItemSelectionModel
from PySide6.QtWidgets import QAbstractItemView, QTableView, QHeaderView
from graph_list_table_model import COLUMN_COUNT
from graph_list_sort_filter_proxy_model import GraphListSortFilterProxyModel
from preferences import preferences
from graph_item import GraphItem
......
......@@ -20,7 +20,8 @@ class GraphListSortManager(QObject):
def __init__(self, gui_manager):
super().__init__()
self.graph_list_proxy_model = \
gui_manager.graph_list_view.graph_list_proxy_model
gui_manager.graph_list_proxy_model
self.graph_list_table_dialog = gui_manager.graph_list_table_dialog
# connect to reset selections on graphs loaded event
gui_manager.graphs_manager.signal_graphs_loaded.connect(
......@@ -63,32 +64,44 @@ class GraphListSortManager(QObject):
self.sort_menu, "&Marked", MARK_COLUMN))
# size
self.sort_menu_size = self.sort_menu.addMenu("&Size")
self.sort_menu_size.addAction(self._make_ascending_sort_action(
self.sort_menu_size, "Ascending", SIZE_COLUMN))
self.sort_menu_size.addAction(self._make_descending_sort_action(
self.sort_menu_size, "Descending", SIZE_COLUMN))
sort_menu_size = self.sort_menu.addMenu("&Size")
sort_menu_size.addAction(self._make_ascending_sort_action(
sort_menu_size, "Ascending", SIZE_COLUMN))
sort_menu_size.addAction(self._make_descending_sort_action(
sort_menu_size, "Descending", SIZE_COLUMN))
# probability
self.sort_menu_size = self.sort_menu.addMenu("&Probability")
self.sort_menu_size.addAction(self._make_ascending_sort_action(
self.sort_menu_size, "Ascending", PROBABILITY_COLUMN))
self.sort_menu_size.addAction(self._make_descending_sort_action(
self.sort_menu_size, "Descending", PROBABILITY_COLUMN))
sort_menu_probability = self.sort_menu.addMenu("&Probability")
sort_menu_probability.addAction(self._make_ascending_sort_action(
sort_menu_probability, "Ascending", PROBABILITY_COLUMN))
sort_menu_probability.addAction(self._make_descending_sort_action(
sort_menu_probability, "Descending", PROBABILITY_COLUMN))
# trace index
self.sort_menu_size = self.sort_menu.addMenu("&Trace index")
self.sort_menu_size.addAction(self._make_ascending_sort_action(
self.sort_menu_size, "Ascending", TRACE_INDEX_COLUMN))
self.sort_menu_size.addAction(self._make_descending_sort_action(
self.sort_menu_size, "Descending", TRACE_INDEX_COLUMN))
# button
sort_menu_trace_index = self.sort_menu.addMenu("&Trace index")
sort_menu_trace_index.addAction(self._make_ascending_sort_action(
sort_menu_trace_index, "Ascending", TRACE_INDEX_COLUMN))
sort_menu_trace_index.addAction(self._make_descending_sort_action(
sort_menu_trace_index, "Descending", TRACE_INDEX_COLUMN))
# sort by attribute
action_sort_by_attribute = QAction("Sort by &attribute...",
self.sort_menu)
action_sort_by_attribute.setToolTip("Sort traces by attribute value")
action_sort_by_attribute.triggered.connect(self._sort_by_attribute)
self.sort_menu.addSeparator()
self.sort_menu.addAction(action_sort_by_attribute)
# sort menu button
self.sort_menu_button = mp_menu_button(self.sort_menu,
QIcon(":/icons/sort_ascending"),
"Sort",
"Sort the events list")
@Slot()
def _sort_by_attribute(self):
self.graph_list_table_dialog.show()
@Slot()
def _reset_selections(self):
self.sort_group.actions()[0].setChecked(True)
......
......@@ -21,7 +21,7 @@ class GraphListTableDialog(QDialog):
self.setFixedSize(self.width(), self.height())
self.setWindowFlags(self.windowFlags() | Qt.Tool)
self.setAttribute(Qt.WA_MacAlwaysShowToolWindow)
self.setWindowTitle("Sort traces by attribute")
self.setWindowTitle("Sort Traces by Attribute")
# layout
self.layout = QVBoxLayout(self)
......@@ -37,6 +37,7 @@ class GraphListTableDialog(QDialog):
QAbstractItemView.SelectionMode.SingleSelection)
self.table.setAlternatingRowColors(True)
self.table.horizontalHeader().setStretchLastSection(True)
self.table.horizontalHeader().setDefaultAlignment(Qt.AlignLeft)
self.table.hideColumn(GRAPH_COLUMN)
self.layout.addWidget(self.table)
......@@ -54,9 +55,6 @@ class GraphListTableDialog(QDialog):
# slot
graph_list_selection_model.selectionChanged.connect(self._select_row)
#zz
# self.show()
@Slot(QItemSelection, QItemSelection)
def _select_row(self, selected, deselected):
# get a lsit of the same row index for each column else an empty list
......
......@@ -3,6 +3,7 @@ from PySide6.QtCore import Slot # for signal/slot support
from PySide6.QtCore import Qt, QAbstractTableModel, QModelIndex
from graph_item import GraphItem
from settings import settings
from say_info import say_column_names, say_column_map, say_info
from mp_logger import log_to_statusbar
# graph list model column constants
......@@ -11,9 +12,8 @@ TRACE_INDEX_COLUMN = 1 # values start at 1
MARK_COLUMN = 2 # "M" or ""
PROBABILITY_COLUMN = 3 # float 0.0 to 1.0
SIZE_COLUMN = 4 # int, see implementation
COLUMN_COUNT = 5 # number of columns
_HEADERS = ["Graph", "index", "Mark", "Type 1 P", "Size"]
_HARDCODED_HEADERS = ["Graph", "index", "Mark", "Type 1 P", "Size"]
class GraphListTableModel(QAbstractTableModel):
"""Provides the graph list model for graph list accessors, consisting of
......@@ -35,20 +35,25 @@ class GraphListTableModel(QAbstractTableModel):
"""
def __init__(self):
self.headers = list()
super().__init__()
graphs = list()
self.graphs = list()
self.headers = list()
self.say_map = say_column_map(self.graphs, len(_HARDCODED_HEADERS))
def set_graph_list(self, graphs):
self.beginResetModel() # because self.graphs change
self.graphs = graphs
self.headers = _HARDCODED_HEADERS.copy()
self.headers.extend(say_column_names(graphs))
self.endResetModel()
def headerData(self, section, orientation,
role=Qt.ItemDataRole.DisplayRole):
if role == Qt.ItemDataRole.DisplayRole \
and orientation == Qt.Orientation.Horizontal:
return _HEADERS[section]
return self.headers[section]
else:
return None # QVariant()
......@@ -57,7 +62,7 @@ class GraphListTableModel(QAbstractTableModel):
return len(self.graphs)
def columnCount(self, parent=QModelIndex()):
return COLUMN_COUNT
return len(self.headers)
def data(self, model_index, role=Qt.ItemDataRole.DisplayRole):
......@@ -66,28 +71,37 @@ class GraphListTableModel(QAbstractTableModel):
row = model_index.row()
column = model_index.column()
gry_graph = self.graphs[row].gry_graph
# provide values for hardcoded columns
if column == GRAPH_COLUMN:
# graph_list_view_delegate renders this
return None # QVariant()
if column == TRACE_INDEX_COLUMN:
return row
if row == 0:
# these columns are for traces only
return "NA"
if column == MARK_COLUMN:
if "trace" in gry_graph:
return gry_graph["trace"]["mark"]
else:
return "U"
return gry_graph["trace"]["mark"]
if column == PROBABILITY_COLUMN:
if "trace" in gry_graph:
return gry_graph["trace"]["probability"]
else:
return 0
return gry_graph["trace"]["probability"]
if column == SIZE_COLUMN:
if "trace" in gry_graph:
gry_trace = gry_graph["trace"]
return len(gry_trace["nodes"]) + len(gry_trace["edges"])
else:
return 0
raise RuntimeError("bad %d"%column)
gry_trace = gry_graph["trace"]
return len(gry_trace["nodes"]) + len(gry_trace["edges"])
if column >= len(self.headers):
raise RuntimeError("bad %d"%column)
# provide values for columns dynamically created from SAY nodes
header = self.headers[column]
says = say_info(gry_graph)
if header in says:
# good, says matches a header so return its numeric value
return says[header]
else:
# return warning when say node text does not match a header
return "Unexpected %s"%header
else:
# ignore this ItemDataRole
return None # QVariant()
......@@ -5,7 +5,6 @@ from PySide6.QtCore import Qt
from PySide6.QtCore import QModelIndex
from PySide6.QtCore import QItemSelectionModel
from PySide6.QtWidgets import QAbstractItemView, QTableView, QHeaderView
from graph_list_table_model import COLUMN_COUNT
from graph_list_sort_filter_proxy_model import GraphListSortFilterProxyModel
from graph_list_view_delegate import GraphListViewDelegate
from preferences import preferences
......@@ -24,15 +23,10 @@ class GraphListView(QTableView):
signal_preferences_changed):
super().__init__(main_splitter)
self.graph_list_proxy_model = graph_list_proxy_model
# the data model
self.setModel(self.graph_list_proxy_model)
self.setModel(graph_list_proxy_model)
# table appearance
for i in range(1, COLUMN_COUNT):
self.setColumnHidden(i, True)
self.setColumnWidth(0, 20) # otherwise minimum is 100
self.horizontalHeader().hide()
self.verticalHeader().hide()
self.setShowGrid(False)
......@@ -48,21 +42,21 @@ class GraphListView(QTableView):
navigation_column_width_manager)
self.setItemDelegate(view_delegate)
# connect model reset
graph_list_proxy_model.modelReset.connect(self._hide_unused_columns)
# connect width changed
navigation_column_width_manager.signal_navigation_column_width_changed\
.connect(self._changed_width)
# connect move scrollbar to top when graph is reloaded
#zz graphs_manager.signal_graphs_loaded.connect(self._reset_scrollbar)
graph_list_proxy_model.sourceModelChanged.connect(self._reset_scrollbar)
# connect scroll mode changed
signal_preferences_changed.connect(self._set_scroll_mode)
@Slot()
def _reset_scrollbar(self):
self.verticalScrollBar().setValue(0)
def _hide_unused_columns(self):
# hide all but the first column so the first column stretches
for i in range(1, self.model().columnCount()):
self.setColumnHidden(i, True)
# width changed
@Slot(int)
......
import re
from collections import defaultdict
"""Extract SAY titles and numbers from SAY trace nodes."""
_pattern = re.compile(r"\s+[0-9.]+\s+")
# Return {say text, [numbers]} where:
# * say_text is say text with index prepended and numbers removed
# * and numbers is a list of zero or more numbers.
# Say nodes are type="T"."""
def say_info(gry_graph):
if not "trace" in gry_graph:
raise RuntimeError("bad")
says = defaultdict(list)
for gry_node in gry_graph["trace"]["nodes"]:
if gry_node["type"] == "T":
label = gry_node["label"]
key = _pattern.sub(" ", label)
numbers = _pattern.findall(label)
for i, number in enumerate(numbers, 1):
try:
says["%i: %s"%(i, key)] = float(number)
except Exception as e:
# multiple decimal places can thwart _pattern so manage
# invalid sortable numbers as text
print("say_info.say_info: invalid number: %s"%number)
says["%i: %s"%(i, key)] = number
return says
# Return a list of "say text" column names.
def say_column_names(graphs):
if len(graphs) >= 2:
says = say_info(graphs[1].gry_graph)
return sorted(says.keys())
else:
return list()
# Return a dict of {key="say text", value=column index}
def say_column_map(graphs, start_index):
column_names = say_column_names(graphs)
column_map = dict()
for i, column_name in enumerate(column_names, start_index):
column_map[column_name] = i
return column_map
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment