fix(logpanel): lay out the time range dialog properly and prefill the active range

This commit is contained in:
2026-08-04 12:11:00 +02:00
committed by Jan Wyzula
parent 327b2fdd85
commit 270b4829bc
2 changed files with 85 additions and 35 deletions
@@ -43,7 +43,7 @@ from qtpy.QtWidgets import (
QDateTimeEdit,
QDialog,
QDialogButtonBox,
QFormLayout,
QGridLayout,
QHBoxLayout,
QHeaderView,
QLabel,
@@ -982,6 +982,8 @@ class LogPanelToolbar(QWidget):
self._known_services: set[str] = set()
# None means "all services" - the include-list has not been narrowed
self._checked_services: set[str] | None = None
self._active_start: QDateTime | None = None
self._active_end: QDateTime | None = None
self._layout = QHBoxLayout(self)
self._layout.setContentsMargins(0, 0, 0, 0)
@@ -1166,57 +1168,77 @@ class LogPanelToolbar(QWidget):
def _apply_time_preset(self, label: str, seconds: int | None):
if seconds is None:
self.timestamp_update.emit(TimestampUpdate(value=None, update_type="start"))
self.timestamp_update.emit(TimestampUpdate(value=None, update_type="end"))
self.timerange_button.setText("All time")
self._set_time_range(None, None)
return
start = QDateTime.currentDateTime().addSecs(-seconds)
self.timestamp_update.emit(TimestampUpdate(value=start, update_type="start"))
self.timestamp_update.emit(TimestampUpdate(value=None, update_type="end"))
self.timerange_button.setText(f">= {start.toString('HH:mm:ss')}")
self._set_time_range(QDateTime.currentDateTime().addSecs(-seconds), None)
@SafeSlot()
def _open_custom_range_dialog(self):
"""One dialog with both bounds - replaces the former nested calendar dialogs."""
def _set_time_range(self, start: QDateTime | None, end: QDateTime | None):
"""Apply both bounds, remember them for the custom dialog, and label the button."""
self._active_start = start
self._active_end = end
self.timestamp_update.emit(TimestampUpdate(value=start, update_type="start"))
self.timestamp_update.emit(TimestampUpdate(value=end, update_type="end"))
if start and end:
text = f"{start.toString('HH:mm')} - {end.toString('HH:mm')}"
elif start:
text = f">= {start.toString('HH:mm:ss')}"
elif end:
text = f"<= {end.toString('HH:mm:ss')}"
else:
text = "All time"
self.timerange_button.setText(text)
def _build_custom_range_dialog(
self,
) -> tuple[QDialog, dict[str, tuple[QCheckBox, QDateTimeEdit]]]:
"""Build the custom range dialog, prefilled with the currently active bounds."""
dialog = QDialog(self)
dialog.setWindowTitle("Time range")
form = QFormLayout(dialog)
layout = QGridLayout(dialog)
layout.setContentsMargins(16, 16, 16, 12)
layout.setHorizontalSpacing(12)
layout.setVerticalSpacing(10)
bounds: dict[str, tuple[QCheckBox, QDateTimeEdit]] = {}
for bound, default in [
("start", QDateTime.currentDateTime().addSecs(-3600)),
("end", QDateTime.currentDateTime()),
]:
enable = QCheckBox("From" if bound == "start" else "Until", dialog)
edit = QDateTimeEdit(default, dialog)
rows = [
("start", "From", self._active_start, QDateTime.currentDateTime().addSecs(-3600)),
("end", "Until", self._active_end, QDateTime.currentDateTime()),
]
for row, (bound, label, active, default) in enumerate(rows):
enable = QCheckBox(label, dialog)
edit = QDateTimeEdit(active or default, dialog)
edit.setCalendarPopup(True)
edit.setDisplayFormat("yyyy-MM-dd HH:mm:ss")
edit.setEnabled(False)
edit.setMinimumWidth(220)
edit.setEnabled(active is not None)
enable.setChecked(active is not None)
enable.toggled.connect(edit.setEnabled)
form.addRow(enable, edit)
layout.addWidget(enable, row, 0)
layout.addWidget(edit, row, 1)
bounds[bound] = (enable, edit)
buttons = QDialogButtonBox(
QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel, dialog
)
buttons.accepted.connect(dialog.accept)
buttons.rejected.connect(dialog.reject)
form.addRow(buttons)
layout.addWidget(buttons, len(rows), 0, 1, 2)
return dialog, bounds
@SafeSlot()
def _open_custom_range_dialog(self):
"""One dialog with both bounds - replaces the former nested calendar dialogs."""
dialog, bounds = self._build_custom_range_dialog()
if dialog.exec() == QDialog.DialogCode.Accepted:
values = {}
for bound, (enable, edit) in bounds.items():
values[bound] = edit.dateTime() if enable.isChecked() else None
self.timestamp_update.emit(TimestampUpdate(values[bound], bound))
start, end = values["start"], values["end"]
if start and end:
text = f"{start.toString('HH:mm')} - {end.toString('HH:mm')}"
elif start:
text = f">= {start.toString('HH:mm:ss')}"
elif end:
text = f"<= {end.toString('HH:mm:ss')}"
else:
text = "All time"
self.timerange_button.setText(text)
self._apply_custom_range(bounds)
dialog.deleteLater()
def _apply_custom_range(self, bounds: dict[str, tuple[QCheckBox, QDateTimeEdit]]):
start_enable, start_edit = bounds["start"]
end_enable, end_edit = bounds["end"]
self._set_time_range(
start_edit.dateTime() if start_enable.isChecked() else None,
end_edit.dateTime() if end_enable.isChecked() else None,
)
# ---------------------------------------------------------------- search
@SafeSlot(str)
+28
View File
@@ -462,6 +462,34 @@ def test_log_panel_set_level_with_unlisted_level_keeps_filter(qtbot, log_panel:
assert log_panel._toolbar.filter_level_dropdown.currentIndex() == 0
def test_log_panel_custom_range_dialog_prefills_active_bounds(qtbot, log_panel: LogPanel):
toolbar = log_panel._toolbar
toolbar._apply_time_preset("Last 5 min", 300)
qtbot.waitUntil(lambda: log_panel._proxy._ts_start is not None, timeout=200)
dialog, bounds = toolbar._build_custom_range_dialog()
try:
start_enable, start_edit = bounds["start"]
end_enable, end_edit = bounds["end"]
# prefilled from the active preset instead of stale defaults
assert start_enable.isChecked()
assert start_edit.dateTime() == toolbar._active_start
assert not end_enable.isChecked()
# applying with an added end bound routes both bounds to the proxy
end_enable.setChecked(True)
end_edit.setDateTime(toolbar._active_start.addSecs(60))
toolbar._apply_custom_range(bounds)
qtbot.waitUntil(lambda: log_panel._proxy._ts_end is not None, timeout=200)
assert "-" in toolbar.timerange_button.text()
# and 'All time' clears everything again
toolbar._apply_time_preset("All time", None)
qtbot.waitUntil(lambda: log_panel._proxy._ts_start is None, timeout=200)
assert log_panel._proxy._ts_end is None
assert toolbar.timerange_button.text() == "All time"
finally:
dialog.deleteLater()
def test_log_panel_colors_follow_theme(qtbot, log_panel: LogPanel):
info_index = log_panel._model.index(1, 0)
success_index = log_panel._model.index(2, 0)