Skip to content

Commit

Permalink
gui: support multiple log docks
Browse files Browse the repository at this point in the history
sbourdeauducq committed Nov 11, 2015
1 parent fa89e16 commit 3cbd7c4
Showing 2 changed files with 99 additions and 17 deletions.
38 changes: 24 additions & 14 deletions artiq/frontend/artiq_gui.py
Original file line number Diff line number Diff line change
@@ -53,14 +53,17 @@ def restore_state(self, state):


def main():
# initialize application
args = get_argparser().parse_args()
init_logger(args)

app = QtGui.QApplication([])
loop = QEventLoop(app)
asyncio.set_event_loop(loop)
atexit.register(loop.close)
smgr = state.StateManager(args.db_file)

# create connections to master
rpc_clients = dict()
for target in "schedule", "repository", "dataset_db":
client = AsyncioClient()
@@ -80,8 +83,7 @@ def main():
atexit_register_coroutine(subscriber.close)
sub_clients[notifier_name] = subscriber

smgr = state.StateManager(args.db_file)

# initialize main window
win = MainWindow(app, args.server)
area = dockarea.DockArea()
smgr.register(area)
@@ -91,6 +93,7 @@ def main():
status_bar.showMessage("Connected to {}".format(args.server))
win.setStatusBar(status_bar)

# create UI components
d_explorer = explorer.ExplorerDock(win, status_bar,
sub_clients["explist"],
sub_clients["schedule"],
@@ -106,30 +109,37 @@ def main():
loop.run_until_complete(d_ttl_dds.start(args.server, args.port_notify))
atexit_register_coroutine(d_ttl_dds.stop)

if os.name != "nt":
area.addDock(d_ttl_dds.dds_dock, "top")
area.addDock(d_ttl_dds.ttl_dock, "above", d_ttl_dds.dds_dock)
area.addDock(d_datasets, "above", d_ttl_dds.ttl_dock)
else:
area.addDock(d_datasets, "top")
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)
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":
area.addDock(d_ttl_dds.dds_dock, "top")
area.addDock(d_ttl_dds.ttl_dock, "above", d_ttl_dds.dds_dock)
area.addDock(d_datasets, "above", d_ttl_dds.ttl_dock)
else:
area.addDock(d_datasets, "top")
area.addDock(d_explorer, "above", d_datasets)
area.addDock(d_console, "bottom")
area.addDock(d_log, "above", d_console)
area.addDock(d_schedule, "above", d_log)
area.addDock(d_schedule, "above", d_console)

# load/initialize state
smgr.load()
smgr.start()
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()
loop.run_until_complete(win.exit_request.wait())

78 changes: 75 additions & 3 deletions artiq/gui/log.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import asyncio
import logging
import time
from functools import partial

from quamash import QtGui, QtCore
from pyqtgraph import dockarea, LayoutWidget
@@ -151,8 +152,8 @@ def set_freetext(self, freetext):


class LogDock(dockarea.Dock):
def __init__(self, log_sub):
dockarea.Dock.__init__(self, "Log", size=(1000, 300))
def __init__(self, manager, name, log_sub):
dockarea.Dock.__init__(self, name, label="Log", size=(1000, 300))

grid = LayoutWidget()
self.addWidget(grid)
@@ -180,6 +181,13 @@ def __init__(self, log_sub):
self.log.setTextElideMode(QtCore.Qt.ElideNone)
grid.addWidget(self.log, 1, 0, colspan=4)
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)

@@ -228,7 +236,10 @@ def set_model(self, model):
self.table_model_filter.rowsRemoved.connect(self.rows_removed)

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):
try:
@@ -237,3 +248,64 @@ def restore_state(self, state):
pass
else:
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

0 comments on commit 3cbd7c4

Please sign in to comment.