Compare commits
No commits in common. "21e16d9872b192843027737e0c75d8d94587970c" and "eee479b02a130937880393412c11c05d82124355" have entirely different histories.
21e16d9872
...
eee479b02a
@ -4,19 +4,20 @@ import logging
|
|||||||
import logging.handlers
|
import logging.handlers
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from PySide6.QtCore import QEvent, Qt
|
from PySide6.QtCore import Qt
|
||||||
from PySide6.QtGui import QFont
|
from PySide6.QtGui import QFont, QKeySequence, QShortcut
|
||||||
from PySide6.QtWidgets import (
|
from PySide6.QtWidgets import (
|
||||||
QApplication,
|
QApplication,
|
||||||
QInputDialog,
|
QInputDialog,
|
||||||
QMainWindow,
|
QMainWindow,
|
||||||
|
QMenuBar,
|
||||||
QMessageBox,
|
QMessageBox,
|
||||||
QTabWidget,
|
QTabWidget,
|
||||||
QToolButton,
|
QToolButton,
|
||||||
)
|
)
|
||||||
|
|
||||||
from ameba_control_panel import config
|
from ameba_control_panel import config
|
||||||
from ameba_control_panel.config import DeviceProfile, Mode
|
from ameba_control_panel.config import DeviceProfile
|
||||||
from ameba_control_panel import theme
|
from ameba_control_panel import theme
|
||||||
from ameba_control_panel.controllers.device_tab_controller import DeviceTabController
|
from ameba_control_panel.controllers.device_tab_controller import DeviceTabController
|
||||||
from ameba_control_panel.services.session_store import SessionStore
|
from ameba_control_panel.services.session_store import SessionStore
|
||||||
@ -38,11 +39,10 @@ class MainWindow(QMainWindow):
|
|||||||
self._settings = Settings()
|
self._settings = Settings()
|
||||||
self._dut_controllers: list[DeviceTabController] = []
|
self._dut_controllers: list[DeviceTabController] = []
|
||||||
self._next_dut_num = 1
|
self._next_dut_num = 1
|
||||||
self._settings_applied = False
|
|
||||||
|
|
||||||
add_btn = QToolButton()
|
add_btn = QToolButton()
|
||||||
add_btn.setText("+")
|
add_btn.setText("+")
|
||||||
add_btn.setToolTip("Add DUT tab (Ctrl+N)")
|
add_btn.setToolTip("Add DUT tab (Ctrl+T)")
|
||||||
add_btn.clicked.connect(self._add_dut_tab_auto)
|
add_btn.clicked.connect(self._add_dut_tab_auto)
|
||||||
self._tabs.setCornerWidget(add_btn, Qt.TopLeftCorner)
|
self._tabs.setCornerWidget(add_btn, Qt.TopLeftCorner)
|
||||||
|
|
||||||
@ -61,28 +61,21 @@ class MainWindow(QMainWindow):
|
|||||||
else:
|
else:
|
||||||
self._add_dut_tab("dut_1", "DUT 1")
|
self._add_dut_tab("dut_1", "DUT 1")
|
||||||
|
|
||||||
def showEvent(self, event) -> None: # noqa: N802
|
|
||||||
super().showEvent(event)
|
|
||||||
if not self._settings_applied:
|
|
||||||
self._settings_applied = True
|
|
||||||
self._apply_settings()
|
|
||||||
|
|
||||||
def _build_menu_bar(self) -> None:
|
def _build_menu_bar(self) -> None:
|
||||||
mb = self.menuBar()
|
mb = self.menuBar()
|
||||||
|
|
||||||
view_menu = mb.addMenu("&View")
|
view_menu = mb.addMenu("&View")
|
||||||
view_menu.addAction("New Tab\tCtrl+N", self._add_dut_tab_auto)
|
view_menu.addAction("New Tab", self._add_dut_tab_auto, QKeySequence("Ctrl+T"))
|
||||||
view_menu.addAction("Close Tab\tCtrl+W", self._close_current_tab)
|
view_menu.addAction("Close Tab", self._close_current_tab, QKeySequence("Ctrl+W"))
|
||||||
view_menu.addSeparator()
|
view_menu.addSeparator()
|
||||||
view_menu.addAction("Clear Log\tCtrl+C", self._clear_current_log)
|
view_menu.addAction("Clear Log", self._clear_current_log, QKeySequence("Ctrl+L"))
|
||||||
view_menu.addAction("Find\tCtrl+S", self._focus_find)
|
view_menu.addAction("Find", self._focus_find, QKeySequence("Ctrl+F"))
|
||||||
|
|
||||||
settings_menu = mb.addMenu("&Settings")
|
settings_menu = mb.addMenu("&Settings")
|
||||||
settings_menu.addAction("Preferences...", self._open_settings)
|
settings_menu.addAction("Preferences...", self._open_settings)
|
||||||
|
|
||||||
help_menu = mb.addMenu("&Help")
|
help_menu = mb.addMenu("&Help")
|
||||||
help_menu.addAction("About", self._show_about)
|
help_menu.addAction("About", self._show_about)
|
||||||
help_menu.addAction("Shortcuts", self._show_shortcuts)
|
|
||||||
|
|
||||||
def _toggle_theme(self) -> None:
|
def _toggle_theme(self) -> None:
|
||||||
if self._current_palette.name == "light":
|
if self._current_palette.name == "light":
|
||||||
@ -94,15 +87,6 @@ class MainWindow(QMainWindow):
|
|||||||
for ctrl in self._dut_controllers:
|
for ctrl in self._dut_controllers:
|
||||||
ctrl.view.log_view.set_colors(p.log_rx, p.log_tx, p.log_info)
|
ctrl.view.log_view.set_colors(p.log_rx, p.log_tx, p.log_info)
|
||||||
|
|
||||||
def _toggle_connect(self) -> None:
|
|
||||||
ctrl = self._current_controller()
|
|
||||||
if ctrl:
|
|
||||||
ctrl.view.connect_button.click()
|
|
||||||
|
|
||||||
def _show_shortcuts(self) -> None:
|
|
||||||
text = "\n".join(f"Ctrl+{lbl}\t{desc}" for lbl, desc in self._shortcut_labels)
|
|
||||||
QMessageBox.information(self, "Shortcuts", text)
|
|
||||||
|
|
||||||
def _show_about(self) -> None:
|
def _show_about(self) -> None:
|
||||||
QMessageBox.about(self, config.APP_NAME,
|
QMessageBox.about(self, config.APP_NAME,
|
||||||
f"{config.APP_NAME}\n"
|
f"{config.APP_NAME}\n"
|
||||||
@ -112,34 +96,14 @@ class MainWindow(QMainWindow):
|
|||||||
f"Licensed under the Apache License, Version 2.0\n"
|
f"Licensed under the Apache License, Version 2.0\n"
|
||||||
f"All rights reserved. For internal use only.")
|
f"All rights reserved. For internal use only.")
|
||||||
|
|
||||||
_KEY_EVENTS = frozenset({QEvent.KeyPress, QEvent.ShortcutOverride})
|
|
||||||
|
|
||||||
def _setup_shortcuts(self) -> None:
|
def _setup_shortcuts(self) -> None:
|
||||||
bindings = (
|
QShortcut(QKeySequence("Ctrl+T"), self, activated=self._add_dut_tab_auto)
|
||||||
("N", "New DUT tab", Qt.Key_N, self._add_dut_tab_auto),
|
QShortcut(QKeySequence("Ctrl+W"), self, activated=self._close_current_tab)
|
||||||
("W", "Close current tab", Qt.Key_W, self._close_current_tab),
|
QShortcut(QKeySequence("Ctrl+F"), self, activated=self._focus_find)
|
||||||
("D", "Connect / Disconnect", Qt.Key_D, self._toggle_connect),
|
QShortcut(QKeySequence("Ctrl+L"), self, activated=self._clear_current_log)
|
||||||
("S", "Find in log", Qt.Key_S, self._focus_find),
|
QShortcut(QKeySequence("Ctrl+Return"), self, activated=self._send_current)
|
||||||
("C", "Clear log", Qt.Key_C, self._clear_current_log),
|
QShortcut(QKeySequence("Ctrl+Shift+F"), self, activated=self._flash_current)
|
||||||
("Enter", "Send command", Qt.Key_Return, self._send_current),
|
QShortcut(QKeySequence("Ctrl+R"), self, activated=self._reset_current)
|
||||||
("F", "Flash firmware", Qt.Key_F, self._flash_current),
|
|
||||||
("R", "Normal mode", Qt.Key_R, self._reset_current),
|
|
||||||
)
|
|
||||||
self._shortcut_labels = tuple((lbl, desc) for lbl, desc, _, _ in bindings)
|
|
||||||
self._key_map = {key: handler for _, _, key, handler in bindings}
|
|
||||||
QApplication.instance().installEventFilter(self)
|
|
||||||
|
|
||||||
def eventFilter(self, obj, event) -> bool:
|
|
||||||
etype = event.type()
|
|
||||||
if etype in self._KEY_EVENTS and event.modifiers() == Qt.ControlModifier:
|
|
||||||
handler = self._key_map.get(event.key())
|
|
||||||
if handler:
|
|
||||||
if etype == QEvent.ShortcutOverride:
|
|
||||||
event.accept()
|
|
||||||
return True
|
|
||||||
handler()
|
|
||||||
return True
|
|
||||||
return super().eventFilter(obj, event)
|
|
||||||
|
|
||||||
def _close_current_tab(self) -> None:
|
def _close_current_tab(self) -> None:
|
||||||
self._close_tab(self._tabs.currentIndex())
|
self._close_tab(self._tabs.currentIndex())
|
||||||
@ -182,7 +146,8 @@ class MainWindow(QMainWindow):
|
|||||||
def _reset_current(self) -> None:
|
def _reset_current(self) -> None:
|
||||||
ctrl = self._current_controller()
|
ctrl = self._current_controller()
|
||||||
if ctrl:
|
if ctrl:
|
||||||
ctrl.flash.run_mode(Mode.NORMAL)
|
from ameba_control_panel.config import Mode
|
||||||
|
ctrl.flash.run_mode(Mode.RESET)
|
||||||
|
|
||||||
def _current_controller(self) -> DeviceTabController | None:
|
def _current_controller(self) -> DeviceTabController | None:
|
||||||
widget = self._tabs.currentWidget()
|
widget = self._tabs.currentWidget()
|
||||||
|
|||||||
@ -36,8 +36,6 @@ class LogManager(QObject):
|
|||||||
self._search_worker: Optional[SearchWorker] = None
|
self._search_worker: Optional[SearchWorker] = None
|
||||||
self._matches: List[int] = []
|
self._matches: List[int] = []
|
||||||
self._match_index = -1
|
self._match_index = -1
|
||||||
self._last_needle = ""
|
|
||||||
self._last_case = False
|
|
||||||
|
|
||||||
self._flush_timer = QTimer(self)
|
self._flush_timer = QTimer(self)
|
||||||
self._flush_timer.setInterval(config.LOG_FLUSH_INTERVAL_MS)
|
self._flush_timer.setInterval(config.LOG_FLUSH_INTERVAL_MS)
|
||||||
@ -89,8 +87,6 @@ class LogManager(QObject):
|
|||||||
visible = [line for line in to_flush if line.direction != Direction.TX]
|
visible = [line for line in to_flush if line.direction != Direction.TX]
|
||||||
if visible:
|
if visible:
|
||||||
self.view.log_view.append_lines(visible)
|
self.view.log_view.append_lines(visible)
|
||||||
if self._matches:
|
|
||||||
self._invalidate_search()
|
|
||||||
# Adaptive: slow down flush when queue is heavy to reduce UI stalls
|
# Adaptive: slow down flush when queue is heavy to reduce UI stalls
|
||||||
pending_count = len(self._pending)
|
pending_count = len(self._pending)
|
||||||
if pending_count > 500:
|
if pending_count > 500:
|
||||||
@ -100,17 +96,12 @@ class LogManager(QObject):
|
|||||||
else:
|
else:
|
||||||
self._flush_timer.setInterval(config.LOG_FLUSH_INTERVAL_MS)
|
self._flush_timer.setInterval(config.LOG_FLUSH_INTERVAL_MS)
|
||||||
|
|
||||||
def _invalidate_search(self) -> None:
|
|
||||||
self._matches.clear()
|
|
||||||
self._match_index = -1
|
|
||||||
self._last_needle = ""
|
|
||||||
self._last_case = False
|
|
||||||
self.view.log_view.set_matches([], -1)
|
|
||||||
|
|
||||||
def clear(self) -> None:
|
def clear(self) -> None:
|
||||||
self.buffer.clear()
|
self.buffer.clear()
|
||||||
self.view.log_view.clear_log()
|
self.view.log_view.clear_log()
|
||||||
self._invalidate_search()
|
self._matches.clear()
|
||||||
|
self._match_index = -1
|
||||||
|
self.view.log_view.set_matches([], -1)
|
||||||
|
|
||||||
def save(self) -> None:
|
def save(self) -> None:
|
||||||
dlg = QFileDialog(self.view, "Save Log", str(Path.home() / "ameba_log.txt"))
|
dlg = QFileDialog(self.view, "Save Log", str(Path.home() / "ameba_log.txt"))
|
||||||
@ -135,21 +126,14 @@ class LogManager(QObject):
|
|||||||
if self._search_worker and self._search_worker.isRunning():
|
if self._search_worker and self._search_worker.isRunning():
|
||||||
return
|
return
|
||||||
needle = self.view.find_input.text()
|
needle = self.view.find_input.text()
|
||||||
case = self.view.case_checkbox.isChecked()
|
self.view.log_view.set_needle(needle, self.view.case_checkbox.isChecked())
|
||||||
# If needle unchanged and we already have matches, just advance
|
|
||||||
if needle == self._last_needle and case == self._last_case and self._matches:
|
|
||||||
self.find_next()
|
|
||||||
return
|
|
||||||
self._last_needle = needle
|
|
||||||
self._last_case = case
|
|
||||||
self.view.log_view.set_needle(needle, case)
|
|
||||||
if not needle:
|
if not needle:
|
||||||
self.view.log_view.set_matches([], -1)
|
self.view.log_view.set_matches([], -1)
|
||||||
self._matches = []
|
self._matches = []
|
||||||
self._match_index = -1
|
self._match_index = -1
|
||||||
return
|
return
|
||||||
lines = [l.as_display() for l in self.view.log_view.displayed_lines()]
|
lines = [l.as_display() for l in self.view.log_view.displayed_lines()]
|
||||||
self._search_worker = SearchWorker(lines, needle, case)
|
self._search_worker = SearchWorker(lines, needle, self.view.case_checkbox.isChecked())
|
||||||
self._search_worker.finished.connect(self._on_search_finished)
|
self._search_worker.finished.connect(self._on_search_finished)
|
||||||
self._search_worker.start()
|
self._search_worker.start()
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user