Merge branch 'qtdocks'

This commit is contained in:
Sebastien Bourdeauducq 2016-02-14 23:28:44 +01:00
commit b24146e680
10 changed files with 168 additions and 139 deletions

View File

@ -6,10 +6,8 @@ import atexit
import os import os
import PyQt5 import PyQt5
from quamash import QEventLoop, QtGui, QtCore from quamash import QEventLoop, QtGui, QtCore, QtWidgets
# pyqtgraph will pick up any already imported Qt binding. assert QtGui is PyQt5.QtGui
from pyqtgraph import dockarea
from artiq import __artiq_dir__ as artiq_dir from artiq import __artiq_dir__ as artiq_dir
from artiq.tools import * from artiq.tools import *
@ -49,10 +47,14 @@ class MainWindow(QtGui.QMainWindow):
self.exit_request.set() self.exit_request.set()
def save_state(self): def save_state(self):
return bytes(self.saveGeometry()) return {
"state": bytes(self.saveState()),
"geometry": bytes(self.saveGeometry())
}
def restore_state(self, state): def restore_state(self, state):
self.restoreGeometry(QtCore.QByteArray(state)) self.restoreGeometry(QtCore.QByteArray(state["geometry"]))
self.restoreState(QtCore.QByteArray(state["state"]))
def main(): def main():
@ -87,33 +89,30 @@ def main():
sub_clients[notifier_name] = subscriber sub_clients[notifier_name] = subscriber
# initialize main window # initialize main window
win = MainWindow(args.server) main_window = MainWindow(args.server)
dock_area = dockarea.DockArea() smgr.register(main_window)
smgr.register(dock_area)
smgr.register(win)
win.setCentralWidget(dock_area)
status_bar = QtGui.QStatusBar() status_bar = QtGui.QStatusBar()
status_bar.showMessage("Connected to {}".format(args.server)) status_bar.showMessage("Connected to {}".format(args.server))
win.setStatusBar(status_bar) main_window.setStatusBar(status_bar)
# create UI components # create UI components
expmgr = experiments.ExperimentManager(status_bar, dock_area, expmgr = experiments.ExperimentManager(main_window,
sub_clients["explist"], sub_clients["explist"],
sub_clients["schedule"], sub_clients["schedule"],
rpc_clients["schedule"], rpc_clients["schedule"],
rpc_clients["experiment_db"]) rpc_clients["experiment_db"])
smgr.register(expmgr) smgr.register(expmgr)
d_shortcuts = shortcuts.ShortcutsDock(win, expmgr) d_shortcuts = shortcuts.ShortcutsDock(main_window, expmgr)
smgr.register(d_shortcuts) smgr.register(d_shortcuts)
d_explorer = explorer.ExplorerDock(status_bar, expmgr, d_shortcuts, d_explorer = explorer.Explorer(status_bar, expmgr, d_shortcuts,
sub_clients["explist"], sub_clients["explist"],
rpc_clients["schedule"], rpc_clients["schedule"],
rpc_clients["experiment_db"]) rpc_clients["experiment_db"])
d_datasets = datasets.DatasetsDock(win, sub_clients["datasets"], d_datasets = datasets.DatasetsDock(sub_clients["datasets"],
rpc_clients["dataset_db"]) rpc_clients["dataset_db"])
d_applets = applets.AppletsDock(dock_area, sub_clients["datasets"]) d_applets = applets.AppletsDock(main_window, sub_clients["datasets"])
atexit_register_coroutine(d_applets.stop) atexit_register_coroutine(d_applets.stop)
smgr.register(d_applets) smgr.register(d_applets)
@ -125,21 +124,21 @@ def main():
d_schedule = schedule.ScheduleDock( d_schedule = schedule.ScheduleDock(
status_bar, rpc_clients["schedule"], sub_clients["schedule"]) status_bar, rpc_clients["schedule"], sub_clients["schedule"])
logmgr = log.LogDockManager(dock_area, sub_clients["log"]) logmgr = log.LogDockManager(main_window, sub_clients["log"])
smgr.register(logmgr) smgr.register(logmgr)
# lay out docks # lay out docks
main_window.setCentralWidget(d_explorer)
if os.name != "nt": if os.name != "nt":
dock_area.addDock(d_ttl_dds.dds_dock, "top") main_window.addDockWidget(QtCore.Qt.RightDockWidgetArea, d_ttl_dds.dds_dock)
dock_area.addDock(d_ttl_dds.ttl_dock, "above", d_ttl_dds.dds_dock) main_window.tabifyDockWidget(d_ttl_dds.dds_dock, d_ttl_dds.ttl_dock)
dock_area.addDock(d_applets, "above", d_ttl_dds.ttl_dock) main_window.tabifyDockWidget(d_ttl_dds.ttl_dock, d_applets)
dock_area.addDock(d_datasets, "above", d_applets) main_window.tabifyDockWidget(d_applets, d_datasets)
else: else:
dock_area.addDock(d_applets, "top") main_window.addDockWidget(QtCore.Qt.RightDockWidgetArea, d_applets)
dock_area.addDock(d_datasets, "above", d_applets) main_window.tabifyDockWidget(d_applets, d_datasets)
dock_area.addDock(d_shortcuts, "above", d_datasets) main_window.tabifyDockWidget(d_datasets, d_shortcuts)
dock_area.addDock(d_explorer, "above", d_shortcuts) main_window.addDockWidget(QtCore.Qt.BottomDockWidgetArea, d_schedule)
dock_area.addDock(d_schedule, "bottom")
# load/initialize state # load/initialize state
smgr.load() smgr.load()
@ -149,11 +148,11 @@ def main():
# create first log dock if not already in state # create first log dock if not already in state
d_log0 = logmgr.first_log_dock() d_log0 = logmgr.first_log_dock()
if d_log0 is not None: if d_log0 is not None:
dock_area.addDock(d_log0, "right", d_explorer) main_window.tabifyDockWidget(d_shortcuts, d_log0)
# run # run
win.show() main_window.show()
loop.run_until_complete(win.exit_request.wait()) loop.run_until_complete(main_window.exit_request.wait())
if __name__ == "__main__": if __name__ == "__main__":
main() main()

View File

@ -5,10 +5,10 @@ import shlex
from functools import partial from functools import partial
from quamash import QtCore, QtGui, QtWidgets from quamash import QtCore, QtGui, QtWidgets
from pyqtgraph import dockarea
from artiq.protocols.pipe_ipc import AsyncioParentComm from artiq.protocols.pipe_ipc import AsyncioParentComm
from artiq.protocols import pyon from artiq.protocols import pyon
from artiq.gui.tools import QDockWidgetCloseDetect
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -85,12 +85,12 @@ class AppletIPCServer(AsyncioParentComm):
await asyncio.wait([self.server_task]) await asyncio.wait([self.server_task])
class AppletDock(dockarea.Dock): class AppletDock(QDockWidgetCloseDetect):
def __init__(self, datasets_sub, uid, name, command): def __init__(self, datasets_sub, uid, name, command):
dockarea.Dock.__init__(self, "applet" + str(uid), QDockWidgetCloseDetect.__init__(self, "Applet: " + name)
label="Applet: " + name, self.setObjectName("applet" + str(uid))
closable=True) self.setMinimumSize(QtCore.QSize(100, 100))
self.setMinimumSize(QtCore.QSize(500, 400))
self.datasets_sub = datasets_sub self.datasets_sub = datasets_sub
self.applet_name = name self.applet_name = name
self.command = command self.command = command
@ -99,7 +99,7 @@ class AppletDock(dockarea.Dock):
def rename(self, name): def rename(self, name):
self.applet_name = name self.applet_name = name
self.label.setText("Applet: " + name) self.setWindowTitle("Applet: " + name)
async def start(self): async def start(self):
if self.starting_stopping: if self.starting_stopping:
@ -127,7 +127,7 @@ class AppletDock(dockarea.Dock):
self.embed_window = QtGui.QWindow.fromWinId(win_id) self.embed_window = QtGui.QWindow.fromWinId(win_id)
self.embed_widget = QtWidgets.QWidget.createWindowContainer( self.embed_widget = QtWidgets.QWidget.createWindowContainer(
self.embed_window) self.embed_window)
self.addWidget(self.embed_widget) self.setWidget(self.embed_widget)
# HACK: This function would not be needed if Qt window embedding # HACK: This function would not be needed if Qt window embedding
# worked correctly. # worked correctly.
@ -181,16 +181,17 @@ _templates = [
] ]
class AppletsDock(dockarea.Dock): class AppletsDock(QtWidgets.QDockWidget):
def __init__(self, dock_area, datasets_sub): def __init__(self, main_window, datasets_sub):
self.dock_area = dock_area QtWidgets.QDockWidget.__init__(self, "Applets")
self.setObjectName("Applets")
self.setFeatures(QtWidgets.QDockWidget.DockWidgetMovable |
QtWidgets.QDockWidget.DockWidgetFloatable)
self.main_window = main_window
self.datasets_sub = datasets_sub self.datasets_sub = datasets_sub
self.dock_to_checkbox = dict() self.dock_to_checkbox = dict()
self.applet_uids = set() self.applet_uids = set()
self.workaround_pyqtgraph_bug = False
dockarea.Dock.__init__(self, "Applets")
self.setMinimumSize(QtCore.QSize(850, 450))
self.table = QtWidgets.QTableWidget(0, 3) self.table = QtWidgets.QTableWidget(0, 3)
self.table.setHorizontalHeaderLabels(["Enable", "Name", "Command"]) self.table.setHorizontalHeaderLabels(["Enable", "Name", "Command"])
@ -203,7 +204,7 @@ class AppletsDock(dockarea.Dock):
QtGui.QHeaderView.ResizeToContents) QtGui.QHeaderView.ResizeToContents)
self.table.verticalHeader().hide() self.table.verticalHeader().hide()
self.table.setTextElideMode(QtCore.Qt.ElideNone) self.table.setTextElideMode(QtCore.Qt.ElideNone)
self.addWidget(self.table) self.setWidget(self.table)
self.table.setContextMenuPolicy(QtCore.Qt.ActionsContextMenu) self.table.setContextMenuPolicy(QtCore.Qt.ActionsContextMenu)
new_action = QtGui.QAction("New applet", self.table) new_action = QtGui.QAction("New applet", self.table)
@ -232,12 +233,8 @@ class AppletsDock(dockarea.Dock):
def create(self, uid, name, command): def create(self, uid, name, command):
dock = AppletDock(self.datasets_sub, uid, name, command) dock = AppletDock(self.datasets_sub, uid, name, command)
# If a dock is floated and then dock state is restored, pyqtgraph self.main_window.addDockWidget(QtCore.Qt.RightDockWidgetArea, dock)
# leaves a "phantom" window open. dock.setFloating(True)
if self.workaround_pyqtgraph_bug:
self.dock_area.addDock(dock)
else:
self.dock_area.floatDock(dock)
asyncio.ensure_future(dock.start()) asyncio.ensure_future(dock.start())
dock.sigClosed.connect(partial(self.on_dock_closed, dock)) dock.sigClosed.connect(partial(self.on_dock_closed, dock))
return dock return dock
@ -340,7 +337,6 @@ class AppletsDock(dockarea.Dock):
return state return state
def restore_state(self, state): def restore_state(self, state):
self.workaround_pyqtgraph_bug = True
for uid, enabled, name, command in state: for uid, enabled, name, command in state:
row = self.new(uid) row = self.new(uid)
item = QtWidgets.QTableWidgetItem() item = QtWidgets.QTableWidgetItem()
@ -351,4 +347,3 @@ class AppletsDock(dockarea.Dock):
self.table.setItem(row, 2, item) self.table.setItem(row, 2, item)
if enabled: if enabled:
self.table.item(row, 0).setCheckState(QtCore.Qt.Checked) self.table.item(row, 0).setCheckState(QtCore.Qt.Checked)
self.workaround_pyqtgraph_bug = False

View File

@ -3,8 +3,7 @@ from collections import OrderedDict
from functools import partial from functools import partial
import logging import logging
from quamash import QtGui, QtCore from quamash import QtGui, QtCore, QtWidgets
from pyqtgraph import dockarea
from pyqtgraph import LayoutWidget from pyqtgraph import LayoutWidget
from artiq.tools import short_format from artiq.tools import short_format
@ -29,14 +28,16 @@ class Model(DictSyncTreeSepModel):
raise ValueError raise ValueError
class DatasetsDock(dockarea.Dock): class DatasetsDock(QtWidgets.QDockWidget):
def __init__(self, dialog_parent, datasets_sub, dataset_ctl): def __init__(self, datasets_sub, dataset_ctl):
dockarea.Dock.__init__(self, "Datasets") QtWidgets.QDockWidget.__init__(self, "Datasets")
self.dialog_parent = dialog_parent self.setObjectName("Datasets")
self.setFeatures(QtWidgets.QDockWidget.DockWidgetMovable |
QtWidgets.QDockWidget.DockWidgetFloatable)
self.dataset_ctl = dataset_ctl self.dataset_ctl = dataset_ctl
grid = LayoutWidget() grid = LayoutWidget()
self.addWidget(grid) self.setWidget(grid)
self.search = QtGui.QLineEdit() self.search = QtGui.QLineEdit()
self.search.setPlaceholderText("search...") self.search.setPlaceholderText("search...")

View File

@ -3,11 +3,11 @@ import asyncio
from functools import partial from functools import partial
from collections import OrderedDict from collections import OrderedDict
from quamash import QtGui, QtCore from quamash import QtGui, QtCore, QtWidgets
from pyqtgraph import dockarea, LayoutWidget from pyqtgraph import LayoutWidget
from artiq.gui.tools import log_level_to_name from artiq.gui.tools import log_level_to_name, QDockWidgetCloseDetect
from artiq.gui.entries import argty_to_entry from artiq.gui.entries import argty_to_entry
@ -133,10 +133,16 @@ class _ArgumentEditor(QtGui.QTreeWidget):
pass pass
class _ExperimentDock(dockarea.Dock): class _ExperimentDock(QDockWidgetCloseDetect):
def __init__(self, manager, expurl): def __init__(self, manager, expurl):
dockarea.Dock.__init__(self, "Exp: " + expurl, closable=True) name = "Exp: " + expurl
self.setMinimumSize(QtCore.QSize(740, 470)) QDockWidgetCloseDetect.__init__(self, name)
self.setObjectName(name)
self.layout = QtWidgets.QGridLayout()
top_widget = QtWidgets.QWidget()
top_widget.setLayout(self.layout)
self.setWidget(top_widget)
self.layout.setSpacing(5) self.layout.setSpacing(5)
self.layout.setContentsMargins(5, 5, 5, 5) self.layout.setContentsMargins(5, 5, 5, 5)
@ -144,7 +150,7 @@ class _ExperimentDock(dockarea.Dock):
self.expurl = expurl self.expurl = expurl
self.argeditor = _ArgumentEditor(self.manager, self, self.expurl) self.argeditor = _ArgumentEditor(self.manager, self, self.expurl)
self.addWidget(self.argeditor, 0, 0, colspan=5) self.layout.addWidget(self.argeditor, 0, 0, 1, 5)
self.layout.setRowStretch(0, 1) self.layout.setRowStretch(0, 1)
scheduling = manager.get_submission_scheduling(expurl) scheduling = manager.get_submission_scheduling(expurl)
@ -153,8 +159,8 @@ class _ExperimentDock(dockarea.Dock):
datetime = QtGui.QDateTimeEdit() datetime = QtGui.QDateTimeEdit()
datetime.setDisplayFormat("MMM d yyyy hh:mm:ss") datetime.setDisplayFormat("MMM d yyyy hh:mm:ss")
datetime_en = QtGui.QCheckBox("Due date:") datetime_en = QtGui.QCheckBox("Due date:")
self.addWidget(datetime_en, 1, 0) self.layout.addWidget(datetime_en, 1, 0)
self.addWidget(datetime, 1, 1) self.layout.addWidget(datetime, 1, 1)
if scheduling["due_date"] is None: if scheduling["due_date"] is None:
datetime.setDate(QtCore.QDate.currentDate()) datetime.setDate(QtCore.QDate.currentDate())
@ -175,8 +181,8 @@ class _ExperimentDock(dockarea.Dock):
datetime_en.stateChanged.connect(update_datetime_en) datetime_en.stateChanged.connect(update_datetime_en)
pipeline_name = QtGui.QLineEdit() pipeline_name = QtGui.QLineEdit()
self.addWidget(QtGui.QLabel("Pipeline:"), 1, 2) self.layout.addWidget(QtGui.QLabel("Pipeline:"), 1, 2)
self.addWidget(pipeline_name, 1, 3) self.layout.addWidget(pipeline_name, 1, 3)
pipeline_name.setText(scheduling["pipeline_name"]) pipeline_name.setText(scheduling["pipeline_name"])
def update_pipeline_name(text): def update_pipeline_name(text):
@ -185,8 +191,8 @@ class _ExperimentDock(dockarea.Dock):
priority = QtGui.QSpinBox() priority = QtGui.QSpinBox()
priority.setRange(-99, 99) priority.setRange(-99, 99)
self.addWidget(QtGui.QLabel("Priority:"), 2, 0) self.layout.addWidget(QtGui.QLabel("Priority:"), 2, 0)
self.addWidget(priority, 2, 1) self.layout.addWidget(priority, 2, 1)
priority.setValue(scheduling["priority"]) priority.setValue(scheduling["priority"])
def update_priority(value): def update_priority(value):
@ -195,7 +201,7 @@ class _ExperimentDock(dockarea.Dock):
flush = QtGui.QCheckBox("Flush") flush = QtGui.QCheckBox("Flush")
flush.setToolTip("Flush the pipeline before starting the experiment") flush.setToolTip("Flush the pipeline before starting the experiment")
self.addWidget(flush, 2, 2, colspan=2) self.layout.addWidget(flush, 2, 2, 1, 2)
flush.setChecked(scheduling["flush"]) flush.setChecked(scheduling["flush"])
def update_flush(checked): def update_flush(checked):
@ -209,8 +215,8 @@ class _ExperimentDock(dockarea.Dock):
log_level.setToolTip("Minimum level for log entry production") log_level.setToolTip("Minimum level for log entry production")
log_level_label = QtGui.QLabel("Logging level:") log_level_label = QtGui.QLabel("Logging level:")
log_level_label.setToolTip("Minimum level for log message production") log_level_label.setToolTip("Minimum level for log message production")
self.addWidget(log_level_label, 3, 0) self.layout.addWidget(log_level_label, 3, 0)
self.addWidget(log_level, 3, 1) self.layout.addWidget(log_level, 3, 1)
log_level.setCurrentIndex(log_levels.index( log_level.setCurrentIndex(log_levels.index(
log_level_to_name(options["log_level"]))) log_level_to_name(options["log_level"])))
@ -224,8 +230,8 @@ class _ExperimentDock(dockarea.Dock):
repo_rev_label = QtGui.QLabel("Revision:") repo_rev_label = QtGui.QLabel("Revision:")
repo_rev_label.setToolTip("Experiment repository revision " repo_rev_label.setToolTip("Experiment repository revision "
"(commit ID) to use") "(commit ID) to use")
self.addWidget(repo_rev_label, 3, 2) self.layout.addWidget(repo_rev_label, 3, 2)
self.addWidget(repo_rev, 3, 3) self.layout.addWidget(repo_rev, 3, 3)
if options["repo_rev"] is not None: if options["repo_rev"] is not None:
repo_rev.setText(options["repo_rev"]) repo_rev.setText(options["repo_rev"])
@ -243,7 +249,7 @@ class _ExperimentDock(dockarea.Dock):
submit.setShortcut("CTRL+RETURN") submit.setShortcut("CTRL+RETURN")
submit.setSizePolicy(QtGui.QSizePolicy.Expanding, submit.setSizePolicy(QtGui.QSizePolicy.Expanding,
QtGui.QSizePolicy.Expanding) QtGui.QSizePolicy.Expanding)
self.addWidget(submit, 1, 4, rowspan=2) self.layout.addWidget(submit, 1, 4, 2, 1)
submit.clicked.connect(self.submit_clicked) submit.clicked.connect(self.submit_clicked)
reqterm = QtGui.QPushButton("Terminate instances") reqterm = QtGui.QPushButton("Terminate instances")
@ -253,7 +259,7 @@ class _ExperimentDock(dockarea.Dock):
reqterm.setShortcut("CTRL+BACKSPACE") reqterm.setShortcut("CTRL+BACKSPACE")
reqterm.setSizePolicy(QtGui.QSizePolicy.Expanding, reqterm.setSizePolicy(QtGui.QSizePolicy.Expanding,
QtGui.QSizePolicy.Expanding) QtGui.QSizePolicy.Expanding)
self.addWidget(reqterm, 3, 4) self.layout.addWidget(reqterm, 3, 4)
reqterm.clicked.connect(self.reqterm_clicked) reqterm.clicked.connect(self.reqterm_clicked)
def submit_clicked(self): def submit_clicked(self):
@ -287,7 +293,7 @@ class _ExperimentDock(dockarea.Dock):
self.argeditor.deleteLater() self.argeditor.deleteLater()
self.argeditor = _ArgumentEditor(self.manager, self, self.expurl) self.argeditor = _ArgumentEditor(self.manager, self, self.expurl)
self.addWidget(self.argeditor, 0, 0, colspan=5) self.layout.addWidget(self.argeditor, 0, 0, 1, 5)
def save_state(self): def save_state(self):
return self.argeditor.save_state() return self.argeditor.save_state()
@ -297,11 +303,10 @@ class _ExperimentDock(dockarea.Dock):
class ExperimentManager: class ExperimentManager:
def __init__(self, status_bar, dock_area, def __init__(self, main_window,
explist_sub, schedule_sub, explist_sub, schedule_sub,
schedule_ctl, experiment_db_ctl): schedule_ctl, experiment_db_ctl):
self.status_bar = status_bar self.main_window = main_window
self.dock_area = dock_area
self.schedule_ctl = schedule_ctl self.schedule_ctl = schedule_ctl
self.experiment_db_ctl = experiment_db_ctl self.experiment_db_ctl = experiment_db_ctl
@ -385,11 +390,12 @@ class ExperimentManager:
def open_experiment(self, expurl): def open_experiment(self, expurl):
if expurl in self.open_experiments: if expurl in self.open_experiments:
dock = self.open_experiments[expurl] dock = self.open_experiments[expurl]
self.dock_area.floatDock(dock) dock.setFloating(True)
return dock return dock
dock = _ExperimentDock(self, expurl) dock = _ExperimentDock(self, expurl)
self.open_experiments[expurl] = dock self.open_experiments[expurl] = dock
self.dock_area.floatDock(dock) self.main_window.addDockWidget(QtCore.Qt.RightDockWidgetArea, dock)
dock.setFloating(True)
dock.sigClosed.connect(partial(self.on_dock_closed, expurl)) dock.sigClosed.connect(partial(self.on_dock_closed, expurl))
return dock return dock
@ -398,7 +404,8 @@ class ExperimentManager:
async def _submit_task(self, *args): async def _submit_task(self, *args):
rid = await self.schedule_ctl.submit(*args) rid = await self.schedule_ctl.submit(*args)
self.status_bar.showMessage("Submitted RID {}".format(rid)) self.main_window.statusBar().showMessage(
"Submitted RID {}".format(rid))
def submit(self, expurl): def submit(self, expurl):
file, class_name, _ = self.resolve_expurl(expurl) file, class_name, _ = self.resolve_expurl(expurl)
@ -436,7 +443,8 @@ class ExperimentManager:
rid, exc_info=True) rid, exc_info=True)
def request_inst_term(self, expurl): def request_inst_term(self, expurl):
self.status_bar.showMessage("Requesting termination of all instances " self.main_window.statusBar().showMessage(
"Requesting termination of all instances "
"of '{}'".format(expurl)) "of '{}'".format(expurl))
file, class_name, use_repository = self.resolve_expurl(expurl) file, class_name, use_repository = self.resolve_expurl(expurl)
rids = [] rids = []

View File

@ -2,8 +2,7 @@ import asyncio
import logging import logging
from functools import partial from functools import partial
from quamash import QtGui, QtCore from quamash import QtGui, QtCore, QtWidgets
from pyqtgraph import dockarea
from pyqtgraph import LayoutWidget from pyqtgraph import LayoutWidget
from artiq.gui.models import DictSyncTreeSepModel from artiq.gui.models import DictSyncTreeSepModel
@ -116,13 +115,15 @@ class Model(DictSyncTreeSepModel):
DictSyncTreeSepModel.__init__(self, "/", ["Experiment"], init) DictSyncTreeSepModel.__init__(self, "/", ["Experiment"], init)
class ExplorerDock(dockarea.Dock): class Explorer(QtWidgets.QWidget):
def __init__(self, status_bar, exp_manager, d_shortcuts, def __init__(self, status_bar, exp_manager, d_shortcuts,
explist_sub, schedule_ctl, experiment_db_ctl): explist_sub, schedule_ctl, experiment_db_ctl):
dockarea.Dock.__init__(self, "Explorer") QtWidgets.QWidget.__init__(self)
self.setMinimumSize(QtCore.QSize(300, 300))
self.layout.setSpacing(5) layout = QtWidgets.QGridLayout()
self.layout.setContentsMargins(5, 5, 5, 5) self.setLayout(layout)
layout.setSpacing(5)
layout.setContentsMargins(5, 5, 5, 5)
self.status_bar = status_bar self.status_bar = status_bar
self.exp_manager = exp_manager self.exp_manager = exp_manager
@ -132,7 +133,7 @@ class ExplorerDock(dockarea.Dock):
self.el = QtGui.QTreeView() self.el = QtGui.QTreeView()
self.el.setHeaderHidden(True) self.el.setHeaderHidden(True)
self.el.setSelectionBehavior(QtGui.QAbstractItemView.SelectItems) self.el.setSelectionBehavior(QtGui.QAbstractItemView.SelectItems)
self.addWidget(self.el, 0, 0, colspan=2) layout.addWidget(self.el, 0, 0, 1, 2)
self.el.doubleClicked.connect( self.el.doubleClicked.connect(
partial(self.expname_action, "open_experiment")) partial(self.expname_action, "open_experiment"))
@ -140,7 +141,7 @@ class ExplorerDock(dockarea.Dock):
open.setIcon(QtGui.QApplication.style().standardIcon( open.setIcon(QtGui.QApplication.style().standardIcon(
QtGui.QStyle.SP_DialogOpenButton)) QtGui.QStyle.SP_DialogOpenButton))
open.setToolTip("Open the selected experiment (Return)") open.setToolTip("Open the selected experiment (Return)")
self.addWidget(open, 1, 0) layout.addWidget(open, 1, 0)
open.clicked.connect( open.clicked.connect(
partial(self.expname_action, "open_experiment")) partial(self.expname_action, "open_experiment"))
@ -148,7 +149,7 @@ class ExplorerDock(dockarea.Dock):
submit.setIcon(QtGui.QApplication.style().standardIcon( submit.setIcon(QtGui.QApplication.style().standardIcon(
QtGui.QStyle.SP_DialogOkButton)) QtGui.QStyle.SP_DialogOkButton))
submit.setToolTip("Schedule the selected experiment (Ctrl+Return)") submit.setToolTip("Schedule the selected experiment (Ctrl+Return)")
self.addWidget(submit, 1, 1) layout.addWidget(submit, 1, 1)
submit.clicked.connect( submit.clicked.connect(
partial(self.expname_action, "submit")) partial(self.expname_action, "submit"))

View File

@ -4,10 +4,10 @@ import time
import re import re
from functools import partial from functools import partial
from quamash import QtGui, QtCore from quamash import QtGui, QtCore, QtWidgets
from pyqtgraph import dockarea, LayoutWidget from pyqtgraph import LayoutWidget
from artiq.gui.tools import log_level_to_name from artiq.gui.tools import log_level_to_name, QDockWidgetCloseDetect
def _make_wrappable(row, width=30): def _make_wrappable(row, width=30):
@ -140,13 +140,13 @@ class _LogFilterProxyModel(QtCore.QSortFilterProxyModel):
self.invalidateFilter() self.invalidateFilter()
class _LogDock(dockarea.Dock): class _LogDock(QDockWidgetCloseDetect):
def __init__(self, manager, name, log_sub): def __init__(self, manager, name, log_sub):
dockarea.Dock.__init__(self, name, label="Log") QDockWidgetCloseDetect.__init__(self, "Log")
self.setMinimumSize(QtCore.QSize(720, 250)) self.setObjectName(name)
grid = LayoutWidget() grid = LayoutWidget()
self.addWidget(grid) self.setWidget(grid)
grid.addWidget(QtGui.QLabel("Minimum level: "), 0, 0) grid.addWidget(QtGui.QLabel("Minimum level: "), 0, 0)
self.filter_level = QtGui.QComboBox() self.filter_level = QtGui.QComboBox()
@ -279,8 +279,8 @@ class _LogDock(dockarea.Dock):
class LogDockManager: class LogDockManager:
def __init__(self, dock_area, log_sub): def __init__(self, main_window, log_sub):
self.dock_area = dock_area self.main_window = main_window
self.log_sub = log_sub self.log_sub = log_sub
self.docks = dict() self.docks = dict()
@ -294,7 +294,8 @@ class LogDockManager:
dock = _LogDock(self, name, self.log_sub) dock = _LogDock(self, name, self.log_sub)
self.docks[name] = dock self.docks[name] = dock
if add_to_area: if add_to_area:
self.dock_area.floatDock(dock) self.main_window.addDockWidget(QtCore.Qt.RightDockWidgetArea, dock)
dock.setFloating(True)
dock.sigClosed.connect(partial(self.on_dock_closed, name)) dock.sigClosed.connect(partial(self.on_dock_closed, name))
self.update_closable() self.update_closable()
return dock return dock
@ -304,9 +305,12 @@ class LogDockManager:
self.update_closable() self.update_closable()
def update_closable(self): def update_closable(self):
closable = len(self.docks) > 1 flags = (QtWidgets.QDockWidget.DockWidgetMovable |
QtWidgets.QDockWidget.DockWidgetFloatable)
if len(self.docks) > 1:
flags |= QtWidgets.QDockWidget.DockWidgetClosable
for dock in self.docks.values(): for dock in self.docks.values():
dock.setClosable(closable) dock.setFeatures(flags)
def save_state(self): def save_state(self):
return {name: dock.save_state() for name, dock in self.docks.items()} return {name: dock.save_state() for name, dock in self.docks.items()}
@ -317,8 +321,9 @@ class LogDockManager:
for name, dock_state in state.items(): for name, dock_state in state.items():
dock = _LogDock(self, name, self.log_sub) dock = _LogDock(self, name, self.log_sub)
dock.restore_state(dock_state) dock.restore_state(dock_state)
self.dock_area.addDock(dock) self.main_window.addDockWidget(QtCore.Qt.RightDockWidgetArea, dock)
self.docks[name] = dock self.docks[name] = dock
self.update_closable()
def first_log_dock(self): def first_log_dock(self):
if self.docks: if self.docks:

View File

@ -4,8 +4,7 @@ import socket
import struct import struct
from operator import itemgetter from operator import itemgetter
from quamash import QtGui, QtCore from quamash import QtGui, QtCore, QtWidgets
from pyqtgraph import dockarea
from artiq.tools import TaskObject from artiq.tools import TaskObject
from artiq.protocols.sync_struct import Subscriber from artiq.protocols.sync_struct import Subscriber
@ -213,14 +212,17 @@ class _DeviceManager:
return None return None
class _MonInjDock(dockarea.Dock): class _MonInjDock(QtWidgets.QDockWidget):
def __init__(self, name): def __init__(self, name):
dockarea.Dock.__init__(self, name) QtWidgets.QDockWidget.__init__(self, name)
self.setObjectName(name)
self.setFeatures(QtWidgets.QDockWidget.DockWidgetMovable |
QtWidgets.QDockWidget.DockWidgetFloatable)
self.grid = QtGui.QGridLayout() self.grid = QtGui.QGridLayout()
gridw = QtGui.QWidget() gridw = QtGui.QWidget()
gridw.setLayout(self.grid) gridw.setLayout(self.grid)
self.addWidget(gridw) self.setWidget(gridw)
def layout_widgets(self, widgets): def layout_widgets(self, widgets):
w = self.grid.itemAt(0) w = self.grid.itemAt(0)

View File

@ -2,7 +2,7 @@ import asyncio
import time import time
from functools import partial from functools import partial
from quamash import QtGui, QtCore from quamash import QtGui, QtCore, QtWidgets
from pyqtgraph import dockarea from pyqtgraph import dockarea
from artiq.gui.models import DictSyncModel from artiq.gui.models import DictSyncModel
@ -55,10 +55,12 @@ class Model(DictSyncModel):
raise ValueError raise ValueError
class ScheduleDock(dockarea.Dock): class ScheduleDock(QtWidgets.QDockWidget):
def __init__(self, status_bar, schedule_ctl, schedule_sub): def __init__(self, status_bar, schedule_ctl, schedule_sub):
dockarea.Dock.__init__(self, "Schedule") QtWidgets.QDockWidget.__init__(self, "Schedule")
self.setMinimumSize(QtCore.QSize(740, 200)) self.setObjectName("Schedule")
self.setFeatures(QtWidgets.QDockWidget.DockWidgetMovable |
QtWidgets.QDockWidget.DockWidgetFloatable)
self.status_bar = status_bar self.status_bar = status_bar
self.schedule_ctl = schedule_ctl self.schedule_ctl = schedule_ctl
@ -71,7 +73,7 @@ class ScheduleDock(dockarea.Dock):
self.table.verticalHeader().setResizeMode( self.table.verticalHeader().setResizeMode(
QtGui.QHeaderView.ResizeToContents) QtGui.QHeaderView.ResizeToContents)
self.table.verticalHeader().hide() self.table.verticalHeader().hide()
self.addWidget(self.table) self.setWidget(self.table)
self.table.setContextMenuPolicy(QtCore.Qt.ActionsContextMenu) self.table.setContextMenuPolicy(QtCore.Qt.ActionsContextMenu)
request_termination_action = QtGui.QAction("Request termination", self.table) request_termination_action = QtGui.QAction("Request termination", self.table)

View File

@ -2,53 +2,61 @@ import logging
from functools import partial from functools import partial
from quamash import QtGui, QtCore, QtWidgets from quamash import QtGui, QtCore, QtWidgets
from pyqtgraph import dockarea from pyqtgraph import LayoutWidget
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class ShortcutsDock(dockarea.Dock): class ShortcutsDock(QtWidgets.QDockWidget):
def __init__(self, main_window, exp_manager): def __init__(self, main_window, exp_manager):
dockarea.Dock.__init__(self, "Shortcuts") QtWidgets.QDockWidget.__init__(self, "Shortcuts")
self.layout.setSpacing(5) self.setObjectName("Shortcuts")
self.layout.setContentsMargins(5, 5, 5, 5) self.setFeatures(QtWidgets.QDockWidget.DockWidgetMovable |
QtWidgets.QDockWidget.DockWidgetFloatable)
layout = QtWidgets.QGridLayout()
top_widget = QtWidgets.QWidget()
top_widget.setLayout(layout)
self.setWidget(top_widget)
layout.setSpacing(5)
layout.setContentsMargins(5, 5, 5, 5)
self.exp_manager = exp_manager self.exp_manager = exp_manager
self.shortcut_widgets = dict() self.shortcut_widgets = dict()
for n, title in enumerate(["Key", "Experiment"]): for n, title in enumerate(["Key", "Experiment"]):
label = QtGui.QLabel("<b>" + title + "</b>") label = QtGui.QLabel("<b>" + title + "</b>")
self.addWidget(label, 0, n) layout.addWidget(label, 0, n)
label.setMaximumHeight(label.sizeHint().height()) label.setMaximumHeight(label.sizeHint().height())
self.layout.setColumnStretch(1, 1) layout.setColumnStretch(1, 1)
for i in range(12): for i in range(12):
row = i + 1 row = i + 1
self.addWidget(QtGui.QLabel("F" + str(i+1)), row, 0) layout.addWidget(QtGui.QLabel("F" + str(i+1)), row, 0)
label = QtGui.QLabel() label = QtGui.QLabel()
label.setSizePolicy(QtGui.QSizePolicy.Ignored, label.setSizePolicy(QtGui.QSizePolicy.Ignored,
QtGui.QSizePolicy.Ignored) QtGui.QSizePolicy.Ignored)
self.addWidget(label, row, 1) layout.addWidget(label, row, 1)
clear = QtGui.QToolButton() clear = QtGui.QToolButton()
clear.setIcon(QtGui.QApplication.style().standardIcon( clear.setIcon(QtGui.QApplication.style().standardIcon(
QtGui.QStyle.SP_DialogResetButton)) QtGui.QStyle.SP_DialogResetButton))
self.addWidget(clear, row, 2) layout.addWidget(clear, row, 2)
clear.clicked.connect(partial(self.set_shortcut, i, "")) clear.clicked.connect(partial(self.set_shortcut, i, ""))
open = QtGui.QToolButton() open = QtGui.QToolButton()
open.setIcon(QtGui.QApplication.style().standardIcon( open.setIcon(QtGui.QApplication.style().standardIcon(
QtGui.QStyle.SP_DialogOpenButton)) QtGui.QStyle.SP_DialogOpenButton))
self.addWidget(open, row, 3) layout.addWidget(open, row, 3)
open.clicked.connect(partial(self._open_experiment, i)) open.clicked.connect(partial(self._open_experiment, i))
submit = QtGui.QPushButton("Submit") submit = QtGui.QPushButton("Submit")
submit.setIcon(QtGui.QApplication.style().standardIcon( submit.setIcon(QtGui.QApplication.style().standardIcon(
QtGui.QStyle.SP_DialogOkButton)) QtGui.QStyle.SP_DialogOkButton))
self.addWidget(submit, row, 4) layout.addWidget(submit, row, 4)
submit.clicked.connect(partial(self._activated, i)) submit.clicked.connect(partial(self._activated, i))
clear.hide() clear.hide()

View File

@ -1,6 +1,6 @@
import logging import logging
from quamash import QtCore from PyQt5 import QtCore, QtWidgets, QtGui
def log_level_to_name(level): def log_level_to_name(level):
@ -27,3 +27,11 @@ class _WheelFilter(QtCore.QObject):
def disable_scroll_wheel(widget): def disable_scroll_wheel(widget):
widget.setFocusPolicy(QtCore.Qt.StrongFocus) widget.setFocusPolicy(QtCore.Qt.StrongFocus)
widget.installEventFilter(_WheelFilter(widget)) widget.installEventFilter(_WheelFilter(widget))
class QDockWidgetCloseDetect(QtWidgets.QDockWidget):
sigClosed = QtCore.pyqtSignal()
def closeEvent(self, event):
self.sigClosed.emit()
QtWidgets.QDockWidget.closeEvent(self, event)