From e4601fd8795399bbe9612b9eb82fa859a65411e2 Mon Sep 17 00:00:00 2001 From: Salad Dais Date: Thu, 29 Jul 2021 00:45:31 +0000 Subject: [PATCH] Support multiple Message Log windows Closes #19 --- hippolyzer/apps/model.py | 4 +- hippolyzer/apps/proxy_gui.py | 91 ++++++++++++++++++----------- hippolyzer/apps/proxy_mainwindow.ui | 6 ++ 3 files changed, 65 insertions(+), 36 deletions(-) diff --git a/hippolyzer/apps/model.py b/hippolyzer/apps/model.py index 25278dc..d3d2872 100644 --- a/hippolyzer/apps/model.py +++ b/hippolyzer/apps/model.py @@ -19,9 +19,9 @@ class MessageLogHeader(enum.IntEnum): class MessageLogModel(QtCore.QAbstractTableModel, FilteringMessageLogger): - def __init__(self, parent=None): + def __init__(self, parent=None, maxlen=2000): QtCore.QAbstractTableModel.__init__(self, parent) - FilteringMessageLogger.__init__(self) + FilteringMessageLogger.__init__(self, maxlen=maxlen) def _begin_insert(self, insert_idx: int): self.beginInsertRows(QtCore.QModelIndex(), insert_idx, insert_idx) diff --git a/hippolyzer/apps/proxy_gui.py b/hippolyzer/apps/proxy_gui.py index e4bbbd6..b438ad3 100644 --- a/hippolyzer/apps/proxy_gui.py +++ b/hippolyzer/apps/proxy_gui.py @@ -68,12 +68,11 @@ class GUISessionManager(SessionManager, QtCore.QObject): regionAdded = QtCore.Signal(ProxiedRegion) regionRemoved = QtCore.Signal(ProxiedRegion) - def __init__(self, settings, model): + def __init__(self, settings): SessionManager.__init__(self, settings) QtCore.QObject.__init__(self) self.all_regions = [] self.message_logger = WrappingMessageLogger() - self.message_logger.loggers.append(model) def checkRegions(self): new_regions = itertools.chain(*[s.regions for s in self.sessions]) @@ -157,6 +156,22 @@ class GUIInteractionManager(BaseInteractionManager, QtCore.QObject): return (await fut) == QtWidgets.QMessageBox.Ok +class GUIProxySettings(ProxySettings): + """Persistent settings backed by QSettings""" + def __init__(self, settings: QtCore.QSettings): + super().__init__() + self._settings_obj = settings + + def get_setting(self, name: str) -> Any: + val: Any = self._settings_obj.value(name, defaultValue=dataclasses.MISSING) + if val is dataclasses.MISSING: + return val + return json.loads(val) + + def set_setting(self, name: str, val: Any): + self._settings_obj.setValue(name, json.dumps(val)) + + def nonFatalExceptions(f): @functools.wraps(f) def _wrapper(self, *args, **kwargs): @@ -198,7 +213,7 @@ def buildReplacements(session: Session, region: ProxiedRegion): } -class ProxyGUI(QtWidgets.QMainWindow): +class MessageLogWindow(QtWidgets.QMainWindow): DEFAULT_IGNORE = "StartPingCheck CompletePingCheck PacketAck SimulatorViewerTimeMessage SimStats " \ "AgentUpdate AgentAnimation AvatarAnimation ViewerEffect CoarseLocationUpdate LayerData " \ "CameraConstraint ObjectUpdateCached RequestMultipleObjects ObjectUpdate ObjectUpdateCompressed " \ @@ -212,23 +227,36 @@ class ProxyGUI(QtWidgets.QMainWindow): textRequest: QtWidgets.QTextEdit - def __init__(self): - super().__init__() + def __init__( + self, settings: GUIProxySettings, session_manager: GUISessionManager, + log_live_messages: bool, parent: Optional[QtWidgets.QWidget] = None, + ): + super().__init__(parent=parent) loadUi(MAIN_WINDOW_UI_PATH, self) + if parent: + self.setWindowTitle("Message Log") + self.menuBar.setEnabled(False) # type: ignore + self.menuBar.hide() # type: ignore + self._selectedEntry: Optional[AbstractMessageLogEntry] = None - self.settings = GUIProxySettings(QtCore.QSettings("SaladDais", "hippolyzer")) - self.model = MessageLogModel(parent=self.tableView) + self.settings = settings + self.sessionManager = session_manager + if log_live_messages: + self.model = MessageLogModel(parent=self.tableView) + session_manager.message_logger.loggers.append(self.model) + else: + self.model = MessageLogModel(parent=self.tableView, maxlen=None) self.tableView.setModel(self.model) self.model.rowsAboutToBeInserted.connect(self.beforeInsert) self.model.rowsInserted.connect(self.afterInsert) self.tableView.selectionModel().selectionChanged.connect(self._messageSelected) self.checkBeautify.clicked.connect(self._showSelectedMessage) self.checkPause.clicked.connect(self._setPaused) - self._setFilter(self.DEFAULT_FILTER) + self.setFilter(self.DEFAULT_FILTER) self.btnClearLog.clicked.connect(self.model.clear) - self.lineEditFilter.editingFinished.connect(self._setFilter) + self.lineEditFilter.editingFinished.connect(self.setFilter) self.btnMessageBuilder.clicked.connect(self._sendToMessageBuilder) self.btnCopyRepr.clicked.connect(self._copyRepr) self.actionInstallHTTPSCerts.triggered.connect(self._installHTTPSCerts) @@ -242,15 +270,12 @@ class ProxyGUI(QtWidgets.QMainWindow): self.actionProxyRemotelyAccessible.triggered.connect(self._setProxyRemotelyAccessible) self.actionUseViewerObjectCache.triggered.connect(self._setUseViewerObjectCache) self.actionRequestMissingObjects.triggered.connect(self._setRequestMissingObjects) + self.actionOpenNewMessageLogWindow.triggered.connect(self._openNewMessageLogWindow) self._filterMenu = QtWidgets.QMenu() self._populateFilterMenu() self.toolButtonFilter.setMenu(self._filterMenu) - self.sessionManager = GUISessionManager(self.settings, self.model) - self.interactionManager = GUIInteractionManager(self) - AddonManager.UI = self.interactionManager - self._shouldScrollOnInsert = True self.tableView.horizontalHeader().resizeSection(MessageLogHeader.Host, 80) self.tableView.horizontalHeader().resizeSection(MessageLogHeader.Method, 60) @@ -259,10 +284,16 @@ class ProxyGUI(QtWidgets.QMainWindow): self.textResponse.hide() + def closeEvent(self, event) -> None: + loggers = self.sessionManager.message_logger.loggers + if self.model in loggers: + loggers.remove(self.model) + super().closeEvent(event) + def _populateFilterMenu(self): def _addFilterAction(text, filter_str): filter_action = QtWidgets.QAction(text, self) - filter_action.triggered.connect(lambda: self._setFilter(filter_str)) + filter_action.triggered.connect(lambda: self.setFilter(filter_str)) self._filterMenu.addAction(filter_action) self._filterMenu.clear() @@ -281,7 +312,7 @@ class ProxyGUI(QtWidgets.QMainWindow): dialog.exec_() @nonFatalExceptions - def _setFilter(self, filter_str=None): + def setFilter(self, filter_str=None): if filter_str is None: filter_str = self.lineEditFilter.text() else: @@ -369,6 +400,11 @@ class ProxyGUI(QtWidgets.QMainWindow): win = MessageBuilderWindow(self, self.sessionManager) win.show() + def _openNewMessageLogWindow(self): + win = MessageLogWindow(self.settings, self.sessionManager, log_live_messages=True, parent=self) + win.setFilter(self.lineEditFilter.text()) + win.show() + def _installHTTPSCerts(self): msg = QtWidgets.QMessageBox() msg.setText("This will install the proxy's HTTPS certificate in the config dir" @@ -736,7 +772,7 @@ class MessageBuilderWindow(QtWidgets.QMainWindow): class AddonDialog(QtWidgets.QDialog): listAddons: QtWidgets.QListWidget - def __init__(self, parent: ProxyGUI): + def __init__(self, parent: MessageLogWindow): super().__init__() loadUi(ADDON_DIALOG_UI_PATH, self) @@ -787,7 +823,7 @@ class AddonDialog(QtWidgets.QDialog): class FilterDialog(QtWidgets.QDialog): listFilters: QtWidgets.QListWidget - def __init__(self, parent: ProxyGUI): + def __init__(self, parent: MessageLogWindow): super().__init__() loadUi(FILTER_DIALOG_UI_PATH, self) @@ -825,29 +861,16 @@ class FilterDialog(QtWidgets.QDialog): self.listFilters.takeItem(idx) -class GUIProxySettings(ProxySettings): - """Persistent settings backed by QSettings""" - def __init__(self, settings: QtCore.QSettings): - super().__init__() - self._settings_obj = settings - - def get_setting(self, name: str) -> Any: - val: Any = self._settings_obj.value(name, defaultValue=dataclasses.MISSING) - if val is dataclasses.MISSING: - return val - return json.loads(val) - - def set_setting(self, name: str, val: Any): - self._settings_obj.setValue(name, json.dumps(val)) - - def gui_main(): multiprocessing.set_start_method('spawn') QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_ShareOpenGLContexts) app = QtWidgets.QApplication(sys.argv) loop = QEventLoop(app) asyncio.set_event_loop(loop) - window = ProxyGUI() + settings = GUIProxySettings(QtCore.QSettings("SaladDais", "hippolyzer")) + session_manager = GUISessionManager(settings) + window = MessageLogWindow(settings, session_manager, log_live_messages=True) + AddonManager.UI = GUIInteractionManager(window) timer = QtCore.QTimer(app) timer.timeout.connect(window.sessionManager.checkRegions) timer.start(100) diff --git a/hippolyzer/apps/proxy_mainwindow.ui b/hippolyzer/apps/proxy_mainwindow.ui index ae0708d..0377dcf 100644 --- a/hippolyzer/apps/proxy_mainwindow.ui +++ b/hippolyzer/apps/proxy_mainwindow.ui @@ -256,6 +256,7 @@ true + @@ -323,6 +324,11 @@ Force the proxy to request objects that it doesn't know about due to cache misses + + + Open New Message Log Window + +