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

Basic row filtering #362

Merged
merged 13 commits into from
Feb 11, 2025
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
101 changes: 14 additions & 87 deletions damnit/gui/main_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,11 @@
from .plot import (ImagePlotWindow, PlottingControls, ScatterPlotWindow,
Xarray1DPlotWindow)
from .process import ProcessingDialog
from .standalone_comments import TimeComment
from .table import DamnitTableModel, TableView, prettify_notation
from .theme import Theme, ThemeManager, set_lexer_theme
from .user_variables import AddUserVariableDialog
from .web_viewer import PlotlyPlot, UrlSchemeHandler
from .widgets import CollapsibleWidget
from .zulip_messenger import ZulipMessenger

log = logging.getLogger(__name__)
Expand Down Expand Up @@ -595,18 +595,6 @@ def _updates_thread_launcher(self) -> None:
self.update_agent.message.connect(self.handle_update)
QtCore.QTimer.singleShot(0, self._updates_thread.start)

def _set_comment_date(self):
self.comment_time.setText(
time.strftime("%H:%M %d/%m/%Y", time.localtime(time.time()))
)

def _comment_button_clicked(self):
ts = datetime.strptime(self.comment_time.text(), "%H:%M %d/%m/%Y").timestamp()
text = self.comment.text()
comment_id = self.db.add_standalone_comment(ts, text)
self.table.insert_comment_row(comment_id, text, ts)
self.comment.clear()

def get_run_file(self, proposal, run, log=True):
file_name = self.extracted_data_template.format(proposal, run)

Expand Down Expand Up @@ -743,7 +731,6 @@ def show_run_logs(self, proposal, run):
def _create_table_model(self, db, col_settings):
table = DamnitTableModel(db, col_settings, self)
table.value_changed.connect(self.save_value)
table.time_comment_changed.connect(self.save_time_comment)
table.run_visibility_changed.connect(lambda row, state: self.plot.update())
table.rowsInserted.connect(self.on_rows_inserted)
return table
Expand All @@ -756,82 +743,29 @@ def _create_view(self) -> None:
vertical_layout.addWidget(toolbar)

# the table
self.table_view = TableView()
self.table_view = TableView(self)
self.table_view.doubleClicked.connect(self._inspect_data_proxy_idx)
self.table_view.settings_changed.connect(self.save_settings)
self.table_view.zulip_action.triggered.connect(self.export_selection_to_zulip)
self.table_view.process_action.triggered.connect(self.process_runs)
self.table_view.log_view_requested.connect(self.show_run_logs)

# Add table view's toolbar widgets
for widget in self.table_view.get_toolbar_widgets():
toolbar.addWidget(widget)

vertical_layout.addWidget(self.table_view)

# add all other widgets on a collapsible layout
collapsible = CollapsibleWidget()
vertical_layout.addWidget(collapsible)

comment_horizontal_layout = QtWidgets.QHBoxLayout()
self.comment = QtWidgets.QLineEdit(self)
self.comment.setText("Time can be edited in the field on the right.")

self.comment_time = QtWidgets.QLineEdit(self)
self.comment_time.setStyleSheet("width: 25px;")

comment_button = QtWidgets.QPushButton("Additional comment")
comment_button.setEnabled(True)
comment_button.clicked.connect(self._comment_button_clicked)

comment_horizontal_layout.addWidget(comment_button)
comment_horizontal_layout.addWidget(self.comment, stretch=3)
comment_horizontal_layout.addWidget(QtWidgets.QLabel("at"))
comment_horizontal_layout.addWidget(self.comment_time, stretch=1)

collapsible.add_layout(comment_horizontal_layout)

comment_timer = QtCore.QTimer()
self._set_comment_date()
comment_timer.setInterval(30000)
comment_timer.timeout.connect(self._set_comment_date)
comment_timer.start()

# plotting control
# Initialize plot controls
self.plot = PlottingControls(self)
plotting_group = QtWidgets.QGroupBox("Plotting controls")
plot_vertical_layout = QtWidgets.QVBoxLayout()
plot_horizontal_layout = QtWidgets.QHBoxLayout()
plot_parameters_horizontal_layout = QtWidgets.QHBoxLayout()

plot_horizontal_layout.addWidget(self.plot._button_plot)
self.plot._button_plot_runs.setMinimumWidth(200)
plot_horizontal_layout.addStretch()
self.plot_dialog_button = QtWidgets.QPushButton("Plot")
self.plot_dialog_button.clicked.connect(self.plot.show_dialog)
self.comment_button = QtWidgets.QPushButton("Time comment")
self.comment_button.clicked.connect(lambda: TimeComment(self).show())

plot_horizontal_layout.addWidget(QtWidgets.QLabel("Y:"))
plot_horizontal_layout.addWidget(self.plot._combo_box_y_axis)
plot_horizontal_layout.addWidget(self.plot.vs_button)
plot_horizontal_layout.addWidget(QtWidgets.QLabel("X:"))
plot_horizontal_layout.addWidget(self.plot._combo_box_x_axis)

plot_vertical_layout.addLayout(plot_horizontal_layout)

plot_parameters_horizontal_layout.addWidget(self.plot._button_plot_runs)
self.plot._button_plot.setMinimumWidth(200)
plot_parameters_horizontal_layout.addStretch()

plot_parameters_horizontal_layout.addWidget(
self.plot._toggle_probability_density
)

plot_vertical_layout.addLayout(plot_parameters_horizontal_layout)

plotting_group.setLayout(plot_vertical_layout)
toolbar.addWidget(self.plot_dialog_button)
toolbar.addWidget(self.comment_button)
for widget in self.table_view.get_toolbar_widgets():
toolbar.addWidget(widget)

collapsible.add_widget(plotting_group)
vertical_layout.addWidget(self.table_view)
vertical_layout.setContentsMargins(0, 7, 0, 0)

vertical_layout.setSpacing(0)
vertical_layout.setContentsMargins(0, 0, 0, 0)
self._view_widget.setLayout(vertical_layout)

def configure_editor(self):
Expand Down Expand Up @@ -983,14 +917,6 @@ def save_value(self, prop, run, name, value):
if self._connect_to_kafka:
self.update_agent.run_values_updated(prop, run, name, value)

def save_time_comment(self, comment_id, value):
if self.db is None:
log.warning("No SQLite database in use, comment not saved")
return

log.debug("Saving time-based comment ID %d", comment_id)
self.db.change_standalone_comment(comment_id, value)

def check_zulip_messenger(self):
if not isinstance(self.zulip_messenger, ZulipMessenger):
self.zulip_messenger = ZulipMessenger(self)
Expand Down Expand Up @@ -1128,6 +1054,7 @@ def styleHint(self, hint, option=None, widget=None, returnData=None):
else:
return super().styleHint(hint, option, widget, returnData)


class TabBarStyle(QtWidgets.QProxyStyle):
"""
Subclass that enables bold tab text for tab 1 (the editor tab).
Expand Down
58 changes: 43 additions & 15 deletions damnit/gui/plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@
import numpy as np
import pandas as pd
import xarray as xr

from matplotlib.backends.backend_qtagg import FigureCanvas
from matplotlib.backends.backend_qtagg import \
NavigationToolbar2QT as NavigationToolbar
from matplotlib.colorbar import Colorbar
from matplotlib.figure import Figure
from mpl_pan_zoom import MouseButton, PanManager, zoom_factory
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import Qt
from PyQt5.QtCore import Qt, QObject
from PyQt5.QtGui import QColor, QIcon, QPainter
from PyQt5.QtWidgets import QMessageBox

Expand Down Expand Up @@ -592,37 +593,64 @@
def __init__(self, main_window) -> None:
self._main_window = main_window

self._button_plot = QtWidgets.QPushButton(main_window)
self.dialog = QtWidgets.QDialog(main_window)
self.dialog.setWindowTitle("Plot Controls")

plot_vertical_layout = QtWidgets.QVBoxLayout()
plot_horizontal_layout = QtWidgets.QHBoxLayout()
plot_parameters_horizontal_layout = QtWidgets.QHBoxLayout()

self._button_plot = QtWidgets.QPushButton(self.dialog)
self._button_plot.setEnabled(True)
self._button_plot.setText("Plot summary for all runs")
self._button_plot.clicked.connect(self._plot_summaries_clicked)

self._button_plot_runs = QtWidgets.QPushButton(
"Plot for selected runs", main_window
)
self._button_plot_runs = QtWidgets.QPushButton("Plot for selected runs", self.dialog)
self._button_plot_runs.clicked.connect(self._plot_run_data_clicked)

self._combo_box_x_axis = SearchableComboBox(self._main_window)
self._combo_box_y_axis = SearchableComboBox(self._main_window)
plot_horizontal_layout.addWidget(self._button_plot)
self._button_plot_runs.setMinimumWidth(200)
plot_horizontal_layout.addStretch()

self._toggle_probability_density = QtWidgets.QPushButton(
"Histogram", main_window
)
self._toggle_probability_density.setCheckable(True)
self._toggle_probability_density.setChecked(False)
self._toggle_probability_density.toggled.connect(
self._combo_box_y_axis.setDisabled
)
self._combo_box_x_axis = SearchableComboBox(self.dialog)
self._combo_box_y_axis = SearchableComboBox(self.dialog)

self.vs_button = QtWidgets.QToolButton()
self.vs_button.setText("vs.")
self.vs_button.setToolTip("Click to swap axes")
self.vs_button.clicked.connect(self.swap_plot_axes)

plot_horizontal_layout.addWidget(QtWidgets.QLabel("Y:"))
plot_horizontal_layout.addWidget(self._combo_box_y_axis)
plot_horizontal_layout.addWidget(self.vs_button)
plot_horizontal_layout.addWidget(QtWidgets.QLabel("X:"))
plot_horizontal_layout.addWidget(self._combo_box_x_axis)

self._combo_box_x_axis.setCurrentText("Run")

plot_vertical_layout.addLayout(plot_horizontal_layout)

plot_parameters_horizontal_layout.addWidget(self._button_plot_runs)
self._button_plot.setMinimumWidth(200)
plot_parameters_horizontal_layout.addStretch()

self._toggle_probability_density = QtWidgets.QPushButton("Histogram", self.dialog)
self._toggle_probability_density.setCheckable(True)
self._toggle_probability_density.setChecked(False)
self._toggle_probability_density.toggled.connect(self._combo_box_y_axis.setDisabled)

plot_parameters_horizontal_layout.addWidget(self._toggle_probability_density)

plot_vertical_layout.addLayout(plot_parameters_horizontal_layout)

self.dialog.setLayout(plot_vertical_layout)
self.dialog.setVisible(False)

self._plot_windows = []

def show_dialog(self):
self.dialog.setVisible(True)

Check warning on line 652 in damnit/gui/plot.py

View check run for this annotation

Codecov / codecov/patch

damnit/gui/plot.py#L652

Added line #L652 was not covered by tests

def update_columns(self):
keys = self.table.column_titles

Expand Down
132 changes: 132 additions & 0 deletions damnit/gui/standalone_comments.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import logging

from PyQt5.QtCore import (QAbstractTableModel, QDateTime, QModelIndex, Qt,
QVariant)
from PyQt5.QtWidgets import (QDateTimeEdit, QDialog, QHBoxLayout, QLineEdit,
QPushButton, QTableView, QVBoxLayout, QHeaderView)

log = logging.getLogger(__name__)


class CommentModel(QAbstractTableModel):
def __init__(self, db, parent=None):
super().__init__(parent)
self.db = db
self._data = []
self._headers = ['#', 'Timestamp', 'Comment']
self._sort_column = 1 # Default sort by timestamp
self._sort_order = Qt.DescendingOrder
self.load_comments()

def load_comments(self):
"""Load comments from the database, sorted by timestamp in descending order"""
self._data = self.db.conn.execute("""
SELECT rowid, timestamp, comment FROM time_comments
ORDER BY timestamp DESC
""").fetchall()

self.layoutChanged.emit()

def data(self, index, role=Qt.DisplayRole):
if role == Qt.DisplayRole:
row = index.row()
col = index.column()

if col == 1:
return QDateTime.fromSecsSinceEpoch(
int(self._data[row][col])
).toString("yyyy-MM-dd HH:mm:ss")
else:
return self._data[row][col]
return QVariant()

def headerData(self, section, orientation, role):
if role == Qt.DisplayRole and orientation == Qt.Horizontal:
return self._headers[section]
return QVariant()

def rowCount(self, parent=QModelIndex()):
return len(self._data)

def columnCount(self, parent=QModelIndex()):
return len(self._headers)

def addComment(self, timestamp, comment):
"""Add a comment to the database"""
if self.db is None:
log.warning("No SQLite database in use, comment not saved")
return

Check warning on line 58 in damnit/gui/standalone_comments.py

View check run for this annotation

Codecov / codecov/patch

damnit/gui/standalone_comments.py#L57-L58

Added lines #L57 - L58 were not covered by tests

cid = self.db.add_standalone_comment(timestamp, comment)
log.debug("Saving time-based id %d", cid)
# Reload comments to reflect the latest state
self.load_comments()

def sort(self, column, order):
"""Sort table by given column number."""
self._sort_column = column
self._sort_order = order

self.layoutAboutToBeChanged.emit()
self._data = sorted(self._data,
key=lambda x: x[column],
reverse=(order == Qt.DescendingOrder))
self.layoutChanged.emit()


class TimeComment(QDialog):

def __init__(self, parent=None):
super().__init__(parent)

layout = QVBoxLayout()

# Table View
self.tableView = QTableView()
self.tableView.setSortingEnabled(True)
self.model = CommentModel(self.parent().db, self)
self.tableView.setModel(self.model)

# Configure column widths
header = self.tableView.horizontalHeader()
for ix in range(self.model.columnCount() - 1):
header.setSectionResizeMode(ix, header.ResizeToContents)
header.setStretchLastSection(True)
# Set word wrap for the comment column
self.tableView.setWordWrap(True)
# Ensure rows resize properly
self.tableView.verticalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)

# Dialog layout
layout.addWidget(self.tableView)

inputLayout = QHBoxLayout()

self.timestampInput = QDateTimeEdit()
self.timestampInput.setDisplayFormat("yyyy-MM-dd HH:mm:ss")
self.timestampInput.setDateTime(QDateTime.currentDateTime())

self.commentInput = QLineEdit()
self.commentInput.setPlaceholderText('Comment:')

publishButton = QPushButton("Publish")
publishButton.clicked.connect(self.publishComment)

inputLayout.addWidget(self.timestampInput)
inputLayout.addWidget(self.commentInput)
inputLayout.addWidget(publishButton)

layout.addLayout(inputLayout)

self.setLayout(layout)
self.setWindowTitle('Standalone comments')
self.resize(600, 400)

def publishComment(self):
timestamp = self.timestampInput.dateTime().toSecsSinceEpoch()
comment = self.commentInput.text()
if comment:
self.model.addComment(timestamp, comment)
self.commentInput.clear()
self.timestampInput.setDateTime(QDateTime.currentDateTime())
self.tableView.resizeRowsToContents()

Check warning on line 132 in damnit/gui/standalone_comments.py

View check run for this annotation

Codecov / codecov/patch

damnit/gui/standalone_comments.py#L126-L132

Added lines #L126 - L132 were not covered by tests
Loading
Loading