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-07-18 22:50:27 +08:00
|
|
|
from artiq.tools import (verbosity_args, atexit_register_coroutine,
|
|
|
|
get_user_config_dir)
|
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-07-03 12:21:36 +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-07-18 22:50:27 +08:00
|
|
|
default_db_file = os.path.join(get_user_config_dir(), "artiq_browser.pyon")
|
2016-04-15 19:05:12 +08:00
|
|
|
|
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-07-03 12:21:36 +08:00
|
|
|
parser.add_argument(
|
|
|
|
"-s", "--server", default="::1",
|
|
|
|
help="hostname or IP of the master to connect to "
|
|
|
|
"when uploading datasets")
|
|
|
|
parser.add_argument(
|
|
|
|
"--port", default=3251, type=int,
|
|
|
|
help="TCP port to use to connect to the master")
|
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-07-31 23:24:08 +08:00
|
|
|
def __init__(self, smgr, datasets_sub, browse_root,
|
2016-07-03 12:21:36 +08:00
|
|
|
master_host, master_port):
|
2016-04-04 23:37:51 +08:00
|
|
|
QtWidgets.QMainWindow.__init__(self)
|
2016-07-07 22:16:32 +08:00
|
|
|
smgr.register(self)
|
2016-04-04 23:37:51 +08:00
|
|
|
|
|
|
|
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)
|
2016-07-07 22:16:32 +08:00
|
|
|
smgr.register(self.experiments)
|
2016-05-25 23:21:15 +08:00
|
|
|
self.experiments.setHorizontalScrollBarPolicy(
|
|
|
|
QtCore.Qt.ScrollBarAsNeeded)
|
|
|
|
self.experiments.setVerticalScrollBarPolicy(
|
|
|
|
QtCore.Qt.ScrollBarAsNeeded)
|
|
|
|
self.setCentralWidget(self.experiments)
|
|
|
|
|
2016-07-31 23:24:08 +08:00
|
|
|
self.files = files.FilesDock(datasets_sub, browse_root)
|
2016-07-07 22:16:32 +08:00
|
|
|
smgr.register(self.files)
|
2016-05-25 23:21:15 +08:00
|
|
|
|
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)
|
2016-07-07 22:16:32 +08:00
|
|
|
smgr.register(self.applets)
|
2016-05-25 23:21:15 +08:00
|
|
|
atexit_register_coroutine(self.applets.stop)
|
|
|
|
|
2016-07-03 12:21:36 +08:00
|
|
|
self.datasets = datasets.DatasetsDock(
|
|
|
|
datasets_sub, master_host, master_port)
|
2016-07-07 22:16:32 +08:00
|
|
|
smgr.register(self.datasets)
|
2016-07-10 00:29:01 +08:00
|
|
|
self.files.metadata_changed.connect(self.datasets.metadata_changed)
|
2016-05-25 23:21:15 +08:00
|
|
|
|
2016-05-28 12:43:20 +08:00
|
|
|
self.log = log.LogDock(None, "log")
|
2016-07-07 22:16:32 +08:00
|
|
|
smgr.register(self.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-07-01 18:47:04 +08:00
|
|
|
def closeEvent(self, event):
|
|
|
|
event.ignore()
|
2016-04-04 23:37:51 +08:00
|
|
|
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()),
|
|
|
|
}
|
|
|
|
|
|
|
|
def restore_state(self, state):
|
|
|
|
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-07-10 00:29:01 +08:00
|
|
|
browser = Browser(smgr, datasets_sub, args.browse_root,
|
2016-07-31 23:24:08 +08:00
|
|
|
args.server, args.port)
|
2016-07-10 00:29:01 +08:00
|
|
|
widget_log_handler.callback = browser.log.append_message
|
|
|
|
|
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.
|
2016-07-10 00:29:01 +08:00
|
|
|
browser.show()
|
2016-04-04 23:37:51 +08:00
|
|
|
smgr.load()
|
|
|
|
smgr.start()
|
|
|
|
atexit_register_coroutine(smgr.stop)
|
2016-07-31 23:24:08 +08:00
|
|
|
|
|
|
|
if args.select is not None:
|
|
|
|
browser.files.select(args.select)
|
|
|
|
|
2016-07-10 00:29:01 +08:00
|
|
|
browser.show()
|
|
|
|
loop.run_until_complete(browser.exit_request.wait())
|
2016-04-04 23:37:51 +08:00
|
|
|
|
2016-05-20 22:12:02 +08:00
|
|
|
|
2016-04-04 23:37:51 +08:00
|
|
|
if __name__ == "__main__":
|
|
|
|
main()
|