1- from PyQt6 .QtWidgets import (QDockWidget , QTabWidget , QTextEdit , QTabBar , QToolBar , QVBoxLayout , QWidget ,
1+ from PyQt6 .QtWidgets import (QDockWidget , QTabWidget , QTextEdit , QTabBar , QVBoxLayout , QWidget ,
22 QHBoxLayout , QPushButton , QLabel )
3- from PyQt6 .QtCore import Qt , pyqtSignal , QUrl , QFileSystemWatcher
4- from PyQt6 .QtGui import QTextCursor
3+ from PyQt6 .QtCore import Qt , pyqtSignal , QUrl , QFileSystemWatcher , QTimer
54from PyQt6 .QtWebEngineWidgets import QWebEngineView
65from PyQt6 .QtWebEngineCore import QWebEnginePage , QWebEngineHistory
6+ from PyQt6 .QtWidgets import QStyle
7+ from PyQt6 .QtWebEngineCore import QWebEngineProfile , QWebEngineScript
8+
79
810class OutputDock (QDockWidget ):
911 clear_requested = pyqtSignal ()
@@ -15,19 +17,29 @@ def __init__(self, parent=None):
1517 title_bar = QWidget ()
1618 title_bar_layout = QHBoxLayout (title_bar )
1719 title_bar_layout .setContentsMargins (5 , 0 , 5 , 0 )
18- title_bar_layout .setSpacing (5 )
20+ title_bar_layout .setSpacing (1 )
1921
20- title_label = QLabel ("Output:" )
21- title_bar_layout .addWidget (title_label )
2222 title_bar_layout .addStretch ()
2323
2424 self .is_maximized = False
2525 self .restore_height = 0
2626
27- self .back_button = QPushButton ("Back" )
28- self .forward_button = QPushButton ("Forward" )
29- self .open_log_button = QPushButton ("Open Log" )
30- self .maximize_button = QPushButton ("Maximize" )
27+ style = self .style ()
28+ # back_icon = style.standardIcon(QStyle.StandardPixmap.SP_ArrowBack)
29+ # forward_icon = style.standardIcon(QStyle.StandardPixmap.SP_ArrowForward)
30+ open_log_icon = style .standardIcon (QStyle .StandardPixmap .SP_DialogOpenButton )
31+ self .maximize_icon = style .standardIcon (QStyle .StandardPixmap .SP_TitleBarMaxButton )
32+ self .restore_icon = style .standardIcon (QStyle .StandardPixmap .SP_TitleBarNormalButton )
33+
34+ self .back_button = QPushButton ("◀" )
35+ self .forward_button = QPushButton ("▶" )
36+ self .maximize_button = QPushButton (icon = self .maximize_icon )
37+ self .open_log_button = QPushButton (icon = open_log_icon )
38+
39+ self .back_button .setObjectName ("DockTitleBarButton" )
40+ self .forward_button .setObjectName ("DockTitleBarButton" )
41+ self .maximize_button .setObjectName ("DockTitleBarButton" )
42+ self .open_log_button .setObjectName ("DockTitleBarButton" )
3143
3244 self .back_button .hide ()
3345 self .forward_button .hide ()
@@ -37,6 +49,16 @@ def __init__(self, parent=None):
3749 title_bar_layout .addWidget (self .open_log_button )
3850 title_bar_layout .addWidget (self .maximize_button )
3951
52+ self .back_button .setFlat (True )
53+ self .forward_button .setFlat (True )
54+ self .open_log_button .setFlat (True )
55+ self .maximize_button .setFlat (True )
56+
57+ self .back_button .setToolTip ("Go Back (Alt+Left)" )
58+ self .forward_button .setToolTip ("Go Forward (Alt+Right)" )
59+ self .open_log_button .setToolTip ("Open Log File" )
60+ self .maximize_button .setToolTip ("Maximize/Restore Dock (Ctrl+M)" )
61+
4062 self .setTitleBarWidget (title_bar )
4163 self .maximize_button .clicked .connect (self .toggle_maximize )
4264
@@ -58,19 +80,22 @@ def __init__(self, parent=None):
5880 self .tabs .tabBar ().setTabButton (0 , QTabBar .ButtonPosition .RightSide , None )
5981 self .tabs .currentChanged .connect (self .on_tab_changed )
6082
83+ self .log_polling_timer = QTimer (self )
84+ self .log_polling_timer .timeout .connect (self ._poll_active_log )
85+
86+ self ._warmup_web_engine ()
87+
6188 def get_console (self ):
6289 return self .console
6390
6491 def open_log_tab (self , filepath , title ):
6592 report_view = QWebEngineView ()
93+ report_view .loadFinished .connect (self ._scroll_log )
6694 report_view .setUrl (QUrl .fromLocalFile (filepath ))
6795
6896 index = self .tabs .addTab (report_view , title )
6997 self .tabs .setCurrentIndex (index )
7098
71- watcher = QFileSystemWatcher ([filepath ], self )
72- watcher .fileChanged .connect (report_view .reload )
73-
7499 def close_tab (self , index ):
75100 if index == 0 :
76101 return
@@ -123,7 +148,8 @@ def toggle_maximize(self):
123148 # self.setFixedHeight(self.restore_height)
124149 main_window .centralWidget ().show ()
125150 self .is_maximized = False
126- self .maximize_button .setText ("Maximize" )
151+ # self.maximize_button.setText("Maximize")
152+ self .maximize_button .setIcon (self .maximize_icon )
127153 else :
128154 if self .restore_height == 0 :
129155 self .restore_height = self .height ()
@@ -134,7 +160,8 @@ def toggle_maximize(self):
134160 # self.setFixedHeight(maximized_height)
135161 main_window .centralWidget ().hide ()
136162 self .is_maximized = True
137- self .maximize_button .setText ("Restore" )
163+ self .maximize_button .setIcon (self .restore_icon )
164+ # self.maximize_button.setText("Restore")
138165
139166 def _update_back_button_status (self ):
140167 current_widget = self .property ("current_log_widget" )
@@ -150,3 +177,38 @@ def _update_forward_button_status(self):
150177
151178 def is_dock_maximized (self ):
152179 return self .is_maximized
180+
181+ def _warmup_web_engine (self ):
182+ temp_view = QWebEngineView ()
183+ temp_view .deleteLater ()
184+
185+ def _scroll_log (self , ok ):
186+ if not ok :
187+ return
188+
189+ view = self .sender ()
190+ if not view :
191+ return
192+
193+ def do_scroll ():
194+ try :
195+ js_code = "window.scrollTo(0, document.body.scrollHeight);"
196+ view .page ().runJavaScript (js_code )
197+ except RuntimeError as e :
198+ if "has been deleted" in str (e ):
199+ pass
200+ else :
201+ raise
202+
203+ QTimer .singleShot (50 , do_scroll )
204+
205+ def start_log_polling (self , interval_ms = 1000 ):
206+ self .log_polling_timer .start (interval_ms )
207+
208+ def stop_log_polling (self ):
209+ self .log_polling_timer .stop ()
210+
211+ def _poll_active_log (self ):
212+ current_widget = self .tabs .currentWidget ()
213+ if isinstance (current_widget , QWebEngineView ):
214+ current_widget .reload ()
0 commit comments