2016-04-04 23:37:51 +08:00
|
|
|
#!/usr/bin/env python3.5
|
|
|
|
|
|
|
|
import argparse
|
|
|
|
import asyncio
|
|
|
|
import atexit
|
|
|
|
import os
|
2016-05-05 04:00:09 +08:00
|
|
|
import logging
|
2016-04-04 23:37:51 +08:00
|
|
|
|
|
|
|
from PyQt5 import QtCore, QtGui, QtWidgets
|
|
|
|
from quamash import QEventLoop
|
|
|
|
|
|
|
|
from artiq import __artiq_dir__ as artiq_dir
|
2016-05-20 22:12:02 +08:00
|
|
|
from artiq.tools import verbosity_args, atexit_register_coroutine
|
2016-05-18 19:54:14 +08:00
|
|
|
from artiq.gui import state, applets, models, log
|
2016-05-29 00:04:15 +08:00
|
|
|
from artiq.browser import datasets, files, experiments
|
2016-04-04 23:37:51 +08:00
|
|
|
|
2016-05-05 04:00:09 +08:00
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
|
2016-04-04 23:37:51 +08:00
|
|
|
def get_argparser():
|
2016-04-15 19:05:12 +08:00
|
|
|
if os.name == "nt":
|
|
|
|
default_db_file = os.path.expanduser("~\\artiq_browser.pyon")
|
|
|
|
else:
|
|
|
|
default_db_file = os.path.expanduser("~/.artiq_browser.pyon")
|
|
|
|
|
2016-04-05 18:50:19 +08:00
|
|
|
parser = argparse.ArgumentParser(description="ARTIQ Browser")
|
2016-04-20 19:46:03 +08:00
|
|
|
parser.add_argument("--db-file", default=default_db_file,
|
|
|
|
help="database file for local browser settings "
|
|
|
|
"(default: %(default)s)")
|
2016-04-21 01:16:19 +08:00
|
|
|
parser.add_argument("--browse-root", default="",
|
2016-04-20 19:46:03 +08:00
|
|
|
help="root path for directory tree "
|
|
|
|
"(default %(default)s)")
|
2016-04-21 01:16:19 +08:00
|
|
|
parser.add_argument("select", metavar="SELECT", nargs="?",
|
|
|
|
help="directory to browse or file to load")
|
2016-04-04 23:37:51 +08:00
|
|
|
verbosity_args(parser)
|
|
|
|
return parser
|
|
|
|
|
|
|
|
|
2016-05-25 23:21:15 +08:00
|
|
|
class Browser(QtWidgets.QMainWindow):
|
2016-05-28 12:43:20 +08:00
|
|
|
def __init__(self, datasets_sub, browse_root, select):
|
2016-04-04 23:37:51 +08:00
|
|
|
QtWidgets.QMainWindow.__init__(self)
|
|
|
|
|
|
|
|
icon = QtGui.QIcon(os.path.join(artiq_dir, "gui", "logo.svg"))
|
|
|
|
self.setWindowIcon(icon)
|
2016-04-05 18:50:19 +08:00
|
|
|
self.setWindowTitle("ARTIQ Browser")
|
2016-04-04 23:37:51 +08:00
|
|
|
|
|
|
|
qfm = QtGui.QFontMetrics(self.font())
|
|
|
|
self.resize(140*qfm.averageCharWidth(), 38*qfm.lineSpacing())
|
|
|
|
|
|
|
|
self.exit_request = asyncio.Event()
|
|
|
|
|
2016-05-25 23:21:15 +08:00
|
|
|
self.setUnifiedTitleAndToolBarOnMac(True)
|
|
|
|
|
|
|
|
self.experiments = experiments.ExperimentsArea(
|
|
|
|
browse_root, datasets_sub)
|
|
|
|
self.experiments.setHorizontalScrollBarPolicy(
|
|
|
|
QtCore.Qt.ScrollBarAsNeeded)
|
|
|
|
self.experiments.setVerticalScrollBarPolicy(
|
|
|
|
QtCore.Qt.ScrollBarAsNeeded)
|
|
|
|
self.setCentralWidget(self.experiments)
|
|
|
|
|
|
|
|
self.files = files.FilesDock(datasets_sub, browse_root, select=select)
|
|
|
|
|
2016-06-21 19:14:22 +08:00
|
|
|
self.files.dataset_activated.connect(
|
|
|
|
self.experiments.dataset_activated)
|
|
|
|
self.files.dataset_changed.connect(
|
|
|
|
self.experiments.dataset_changed)
|
|
|
|
|
2016-05-25 23:21:15 +08:00
|
|
|
self.applets = applets.AppletsDock(self, datasets_sub)
|
|
|
|
atexit_register_coroutine(self.applets.stop)
|
|
|
|
|
|
|
|
self.datasets = datasets.DatasetsDock(datasets_sub)
|
|
|
|
|
2016-05-28 12:43:20 +08:00
|
|
|
self.log = log.LogDock(None, "log")
|
2016-05-25 23:21:15 +08:00
|
|
|
self.log.setFeatures(self.log.DockWidgetMovable |
|
|
|
|
self.log.DockWidgetFloatable)
|
|
|
|
|
|
|
|
self.addDockWidget(QtCore.Qt.LeftDockWidgetArea, self.files)
|
|
|
|
self.addDockWidget(QtCore.Qt.BottomDockWidgetArea, self.applets)
|
|
|
|
self.addDockWidget(QtCore.Qt.RightDockWidgetArea, self.datasets)
|
|
|
|
self.addDockWidget(QtCore.Qt.BottomDockWidgetArea, self.log)
|
|
|
|
|
2016-05-25 23:33:33 +08:00
|
|
|
g = self.menuBar().addMenu("&Experiment")
|
|
|
|
a = QtWidgets.QAction("&Open", self)
|
|
|
|
a.setIcon(QtWidgets.QApplication.style().standardIcon(
|
2016-05-25 23:21:15 +08:00
|
|
|
QtWidgets.QStyle.SP_DialogOpenButton))
|
2016-05-25 23:33:33 +08:00
|
|
|
a.setShortcuts(QtGui.QKeySequence.Open)
|
|
|
|
a.setStatusTip("Open an experiment")
|
|
|
|
a.triggered.connect(self.experiments.select_experiment)
|
|
|
|
g.addAction(a)
|
|
|
|
|
|
|
|
g = self.menuBar().addMenu("&View")
|
|
|
|
a = QtWidgets.QAction("Cascade", self)
|
|
|
|
a.setStatusTip("Cascade experiment windows")
|
|
|
|
a.triggered.connect(self.experiments.cascadeSubWindows)
|
|
|
|
g.addAction(a)
|
|
|
|
a = QtWidgets.QAction("Tile", self)
|
|
|
|
a.setStatusTip("Tile experiment windows")
|
|
|
|
a.triggered.connect(self.experiments.tileSubWindows)
|
|
|
|
g.addAction(a)
|
2016-05-25 23:21:15 +08:00
|
|
|
|
2016-04-04 23:37:51 +08:00
|
|
|
def closeEvent(self, *args):
|
|
|
|
self.exit_request.set()
|
|
|
|
|
|
|
|
def save_state(self):
|
|
|
|
return {
|
2016-05-25 23:21:15 +08:00
|
|
|
"geometry": bytes(self.saveGeometry()),
|
2016-04-04 23:37:51 +08:00
|
|
|
"state": bytes(self.saveState()),
|
2016-05-25 23:21:15 +08:00
|
|
|
"experiments": self.experiments.save_state(),
|
|
|
|
"files": self.files.save_state(),
|
|
|
|
"datasets": self.datasets.save_state(),
|
|
|
|
"log": self.log.save_state(),
|
|
|
|
"applets": self.applets.save_state(),
|
2016-04-04 23:37:51 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
def restore_state(self, state):
|
2016-05-25 23:21:15 +08:00
|
|
|
self.applets.restore_state(state["applets"])
|
|
|
|
self.log.restore_state(state["log"])
|
|
|
|
self.datasets.restore_state(state["datasets"])
|
|
|
|
self.files.restore_state(state["files"])
|
|
|
|
self.experiments.restore_state(state["experiments"])
|
2016-04-04 23:37:51 +08:00
|
|
|
self.restoreState(QtCore.QByteArray(state["state"]))
|
2016-05-25 23:21:15 +08:00
|
|
|
self.restoreGeometry(QtCore.QByteArray(state["geometry"]))
|
2016-04-04 23:37:51 +08:00
|
|
|
|
|
|
|
|
|
|
|
def main():
|
|
|
|
# initialize application
|
|
|
|
args = get_argparser().parse_args()
|
2016-05-29 00:04:15 +08:00
|
|
|
widget_log_handler = log.init_log(args, "browser")
|
2016-05-20 22:12:02 +08:00
|
|
|
|
2016-05-06 17:23:08 +08:00
|
|
|
app = QtWidgets.QApplication(["ARTIQ Browser"])
|
2016-04-04 23:37:51 +08:00
|
|
|
loop = QEventLoop(app)
|
|
|
|
asyncio.set_event_loop(loop)
|
|
|
|
atexit.register(loop.close)
|
2016-05-23 01:22:29 +08:00
|
|
|
|
2016-04-05 17:17:02 +08:00
|
|
|
datasets_sub = models.LocalModelManager(datasets.Model)
|
2016-04-21 01:16:19 +08:00
|
|
|
datasets_sub.init({})
|
2016-04-04 23:37:51 +08:00
|
|
|
|
2016-05-25 23:21:15 +08:00
|
|
|
smgr = state.StateManager(args.db_file)
|
|
|
|
|
2016-05-28 12:43:20 +08:00
|
|
|
main_window = Browser(datasets_sub, args.browse_root, args.select)
|
2016-05-29 00:04:15 +08:00
|
|
|
widget_log_handler.callback = main_window.log.append_message
|
2016-04-04 23:37:51 +08:00
|
|
|
smgr.register(main_window)
|
2016-05-25 23:21:15 +08:00
|
|
|
|
2016-04-04 23:37:51 +08:00
|
|
|
if os.name == "nt":
|
|
|
|
# HACK: show the main window before creating applets.
|
|
|
|
# Otherwise, the windows of those applets that are in detached
|
|
|
|
# QDockWidgets fail to be embedded.
|
|
|
|
main_window.show()
|
|
|
|
smgr.load()
|
|
|
|
smgr.start()
|
|
|
|
atexit_register_coroutine(smgr.stop)
|
|
|
|
main_window.show()
|
|
|
|
loop.run_until_complete(main_window.exit_request.wait())
|
|
|
|
|
2016-05-20 22:12:02 +08:00
|
|
|
|
2016-04-04 23:37:51 +08:00
|
|
|
if __name__ == "__main__":
|
|
|
|
main()
|