mirror of https://github.com/m-labs/artiq.git
gui: support multiple log docks
This commit is contained in:
parent
fa89e165b2
commit
3cbd7c4c13
|
@ -53,6 +53,7 @@ class MainWindow(QtGui.QMainWindow):
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
# initialize application
|
||||||
args = get_argparser().parse_args()
|
args = get_argparser().parse_args()
|
||||||
init_logger(args)
|
init_logger(args)
|
||||||
|
|
||||||
|
@ -60,7 +61,9 @@ def main():
|
||||||
loop = QEventLoop(app)
|
loop = QEventLoop(app)
|
||||||
asyncio.set_event_loop(loop)
|
asyncio.set_event_loop(loop)
|
||||||
atexit.register(loop.close)
|
atexit.register(loop.close)
|
||||||
|
smgr = state.StateManager(args.db_file)
|
||||||
|
|
||||||
|
# create connections to master
|
||||||
rpc_clients = dict()
|
rpc_clients = dict()
|
||||||
for target in "schedule", "repository", "dataset_db":
|
for target in "schedule", "repository", "dataset_db":
|
||||||
client = AsyncioClient()
|
client = AsyncioClient()
|
||||||
|
@ -80,8 +83,7 @@ def main():
|
||||||
atexit_register_coroutine(subscriber.close)
|
atexit_register_coroutine(subscriber.close)
|
||||||
sub_clients[notifier_name] = subscriber
|
sub_clients[notifier_name] = subscriber
|
||||||
|
|
||||||
smgr = state.StateManager(args.db_file)
|
# initialize main window
|
||||||
|
|
||||||
win = MainWindow(app, args.server)
|
win = MainWindow(app, args.server)
|
||||||
area = dockarea.DockArea()
|
area = dockarea.DockArea()
|
||||||
smgr.register(area)
|
smgr.register(area)
|
||||||
|
@ -91,6 +93,7 @@ def main():
|
||||||
status_bar.showMessage("Connected to {}".format(args.server))
|
status_bar.showMessage("Connected to {}".format(args.server))
|
||||||
win.setStatusBar(status_bar)
|
win.setStatusBar(status_bar)
|
||||||
|
|
||||||
|
# create UI components
|
||||||
d_explorer = explorer.ExplorerDock(win, status_bar,
|
d_explorer = explorer.ExplorerDock(win, status_bar,
|
||||||
sub_clients["explist"],
|
sub_clients["explist"],
|
||||||
sub_clients["schedule"],
|
sub_clients["schedule"],
|
||||||
|
@ -106,6 +109,16 @@ def main():
|
||||||
loop.run_until_complete(d_ttl_dds.start(args.server, args.port_notify))
|
loop.run_until_complete(d_ttl_dds.start(args.server, args.port_notify))
|
||||||
atexit_register_coroutine(d_ttl_dds.stop)
|
atexit_register_coroutine(d_ttl_dds.stop)
|
||||||
|
|
||||||
|
d_schedule = schedule.ScheduleDock(
|
||||||
|
status_bar, rpc_clients["schedule"], sub_clients["schedule"])
|
||||||
|
|
||||||
|
logmgr = log.LogDockManager(area, sub_clients["log"])
|
||||||
|
smgr.register(logmgr)
|
||||||
|
|
||||||
|
d_console = console.ConsoleDock(sub_clients["datasets"],
|
||||||
|
rpc_clients["dataset_db"])
|
||||||
|
|
||||||
|
# lay out docks
|
||||||
if os.name != "nt":
|
if os.name != "nt":
|
||||||
area.addDock(d_ttl_dds.dds_dock, "top")
|
area.addDock(d_ttl_dds.dds_dock, "top")
|
||||||
area.addDock(d_ttl_dds.ttl_dock, "above", d_ttl_dds.dds_dock)
|
area.addDock(d_ttl_dds.ttl_dock, "above", d_ttl_dds.dds_dock)
|
||||||
|
@ -113,23 +126,20 @@ def main():
|
||||||
else:
|
else:
|
||||||
area.addDock(d_datasets, "top")
|
area.addDock(d_datasets, "top")
|
||||||
area.addDock(d_explorer, "above", d_datasets)
|
area.addDock(d_explorer, "above", d_datasets)
|
||||||
|
|
||||||
d_schedule = schedule.ScheduleDock(
|
|
||||||
status_bar, rpc_clients["schedule"], sub_clients["schedule"])
|
|
||||||
|
|
||||||
d_log = log.LogDock(sub_clients["log"])
|
|
||||||
smgr.register(d_log)
|
|
||||||
|
|
||||||
d_console = console.ConsoleDock(sub_clients["datasets"],
|
|
||||||
rpc_clients["dataset_db"])
|
|
||||||
|
|
||||||
area.addDock(d_console, "bottom")
|
area.addDock(d_console, "bottom")
|
||||||
area.addDock(d_log, "above", d_console)
|
area.addDock(d_schedule, "above", d_console)
|
||||||
area.addDock(d_schedule, "above", d_log)
|
|
||||||
|
|
||||||
|
# load/initialize state
|
||||||
smgr.load()
|
smgr.load()
|
||||||
smgr.start()
|
smgr.start()
|
||||||
atexit_register_coroutine(smgr.stop)
|
atexit_register_coroutine(smgr.stop)
|
||||||
|
|
||||||
|
# create first log dock if not already in state
|
||||||
|
d_log0 = logmgr.first_log_dock()
|
||||||
|
if d_log0 is not None:
|
||||||
|
area.addDock(d_log0, "right", d_explorer)
|
||||||
|
|
||||||
|
# run
|
||||||
win.show()
|
win.show()
|
||||||
loop.run_until_complete(win.exit_request.wait())
|
loop.run_until_complete(win.exit_request.wait())
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import asyncio
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
import time
|
import time
|
||||||
|
from functools import partial
|
||||||
|
|
||||||
from quamash import QtGui, QtCore
|
from quamash import QtGui, QtCore
|
||||||
from pyqtgraph import dockarea, LayoutWidget
|
from pyqtgraph import dockarea, LayoutWidget
|
||||||
|
@ -151,8 +152,8 @@ class _LogFilterProxyModel(QSortFilterProxyModel):
|
||||||
|
|
||||||
|
|
||||||
class LogDock(dockarea.Dock):
|
class LogDock(dockarea.Dock):
|
||||||
def __init__(self, log_sub):
|
def __init__(self, manager, name, log_sub):
|
||||||
dockarea.Dock.__init__(self, "Log", size=(1000, 300))
|
dockarea.Dock.__init__(self, name, label="Log", size=(1000, 300))
|
||||||
|
|
||||||
grid = LayoutWidget()
|
grid = LayoutWidget()
|
||||||
self.addWidget(grid)
|
self.addWidget(grid)
|
||||||
|
@ -180,6 +181,13 @@ class LogDock(dockarea.Dock):
|
||||||
self.log.setTextElideMode(QtCore.Qt.ElideNone)
|
self.log.setTextElideMode(QtCore.Qt.ElideNone)
|
||||||
grid.addWidget(self.log, 1, 0, colspan=4)
|
grid.addWidget(self.log, 1, 0, colspan=4)
|
||||||
self.scroll_at_bottom = False
|
self.scroll_at_bottom = False
|
||||||
|
self.scroll_value = 0
|
||||||
|
|
||||||
|
self.log.setContextMenuPolicy(QtCore.Qt.ActionsContextMenu)
|
||||||
|
newlog_action = QtGui.QAction("Create new log dock", self.log)
|
||||||
|
# note the lambda, the default parameter is overriden otherwise
|
||||||
|
newlog_action.triggered.connect(lambda: manager.create_new_dock())
|
||||||
|
self.log.addAction(newlog_action)
|
||||||
|
|
||||||
log_sub.add_setmodel_callback(self.set_model)
|
log_sub.add_setmodel_callback(self.set_model)
|
||||||
|
|
||||||
|
@ -228,7 +236,10 @@ class LogDock(dockarea.Dock):
|
||||||
self.table_model_filter.rowsRemoved.connect(self.rows_removed)
|
self.table_model_filter.rowsRemoved.connect(self.rows_removed)
|
||||||
|
|
||||||
def save_state(self):
|
def save_state(self):
|
||||||
return {"min_level_idx": self.filter_level.currentIndex()}
|
return {
|
||||||
|
"min_level_idx": self.filter_level.currentIndex(),
|
||||||
|
"freetext_filter": self.filter_freetext.text()
|
||||||
|
}
|
||||||
|
|
||||||
def restore_state(self, state):
|
def restore_state(self, state):
|
||||||
try:
|
try:
|
||||||
|
@ -237,3 +248,64 @@ class LogDock(dockarea.Dock):
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
self.filter_level.setCurrentIndex(idx)
|
self.filter_level.setCurrentIndex(idx)
|
||||||
|
|
||||||
|
try:
|
||||||
|
freetext = state["freetext_filter"]
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
self.filter_freetext.setText(freetext)
|
||||||
|
# Note that editingFinished is not emitted when calling setText,
|
||||||
|
# (unlike currentIndexChanged) so we need to call the callback
|
||||||
|
# manually here, unlike for the combobox.
|
||||||
|
self.filter_freetext_changed()
|
||||||
|
|
||||||
|
|
||||||
|
class LogDockManager:
|
||||||
|
def __init__(self, dock_area, log_sub):
|
||||||
|
self.dock_area = dock_area
|
||||||
|
self.log_sub = log_sub
|
||||||
|
self.docks = dict()
|
||||||
|
|
||||||
|
def create_new_dock(self, add_to_area=True):
|
||||||
|
n = 0
|
||||||
|
name = "log0"
|
||||||
|
while name in self.docks:
|
||||||
|
n += 1
|
||||||
|
name = "log" + str(n)
|
||||||
|
|
||||||
|
dock = LogDock(self, name, self.log_sub)
|
||||||
|
self.docks[name] = dock
|
||||||
|
if add_to_area:
|
||||||
|
self.dock_area.addDock(dock)
|
||||||
|
self.dock_area.floatDock(dock)
|
||||||
|
dock.sigClosed.connect(partial(self.on_dock_closed, name))
|
||||||
|
self.update_closable()
|
||||||
|
return dock
|
||||||
|
|
||||||
|
def on_dock_closed(self, name):
|
||||||
|
del self.docks[name]
|
||||||
|
self.update_closable()
|
||||||
|
|
||||||
|
def update_closable(self):
|
||||||
|
closable = len(self.docks) > 1
|
||||||
|
for dock in self.docks.values():
|
||||||
|
dock.setClosable(closable)
|
||||||
|
|
||||||
|
def save_state(self):
|
||||||
|
return {name: dock.save_state() for name, dock in self.docks.items()}
|
||||||
|
|
||||||
|
def restore_state(self, state):
|
||||||
|
if self.docks:
|
||||||
|
raise NotImplementedError
|
||||||
|
for name, dock_state in state.items():
|
||||||
|
dock = LogDock(self, name, self.log_sub)
|
||||||
|
dock.restore_state(dock_state)
|
||||||
|
self.dock_area.addDock(dock)
|
||||||
|
self.docks[name] = dock
|
||||||
|
|
||||||
|
def first_log_dock(self):
|
||||||
|
if self.docks:
|
||||||
|
return None
|
||||||
|
dock = self.create_new_dock(False)
|
||||||
|
return dock
|
||||||
|
|
Loading…
Reference in New Issue