From 91a362c89865374af0f4f74fcd3e32546961580d Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Mon, 4 Apr 2016 23:37:51 +0800 Subject: [PATCH] add artiq_browser --- artiq/frontend/artiq_browser.py | 134 ++++++++++++++++++++++++++++++++ artiq/gui/results.py | 71 +++++++++++++++++ 2 files changed, 205 insertions(+) create mode 100755 artiq/frontend/artiq_browser.py create mode 100644 artiq/gui/results.py diff --git a/artiq/frontend/artiq_browser.py b/artiq/frontend/artiq_browser.py new file mode 100755 index 000000000..a9630d57b --- /dev/null +++ b/artiq/frontend/artiq_browser.py @@ -0,0 +1,134 @@ +#!/usr/bin/env python3.5 + +import argparse +import asyncio +import atexit +import os + +from PyQt5 import QtCore, QtGui, QtWidgets +from quamash import QEventLoop + +from artiq import __artiq_dir__ as artiq_dir +from artiq.tools import * +from artiq.gui import (state, results, + datasets, applets) + + +def get_argparser(): + parser = argparse.ArgumentParser(description="ARTIQ results browser") + parser.add_argument( + "--db-file", default="artiq_browser.pyon", + help="database file for local browser settings") + parser.add_argument("PATH", nargs="?", help="browse path") + verbosity_args(parser) + return parser + + +class MainWindow(QtWidgets.QMainWindow): + def __init__(self): + QtWidgets.QMainWindow.__init__(self) + + icon = QtGui.QIcon(os.path.join(artiq_dir, "gui", "logo.svg")) + self.setWindowIcon(icon) + self.setWindowTitle("ARTIQ - Browser") + + qfm = QtGui.QFontMetrics(self.font()) + self.resize(140*qfm.averageCharWidth(), 38*qfm.lineSpacing()) + + self.exit_request = asyncio.Event() + + def closeEvent(self, *args): + self.exit_request.set() + + def save_state(self): + return { + "state": bytes(self.saveState()), + "geometry": bytes(self.saveGeometry()) + } + + def restore_state(self, state): + self.restoreGeometry(QtCore.QByteArray(state["geometry"])) + self.restoreState(QtCore.QByteArray(state["state"])) + + +class MdiArea(QtWidgets.QMdiArea): + def __init__(self): + QtWidgets.QMdiArea.__init__(self) + self.pixmap = QtGui.QPixmap(os.path.join(artiq_dir, "gui", "logo.svg")) + + def paintEvent(self, event): + QtWidgets.QMdiArea.paintEvent(self, event) + painter = QtGui.QPainter(self.viewport()) + x = (self.width() - self.pixmap.width())//2 + y = (self.height() - self.pixmap.height())//2 + painter.setOpacity(0.5) + painter.drawPixmap(x, y, self.pixmap) + + +class LocalModel( + + + +def main(): + # initialize application + args = get_argparser().parse_args() + init_logger(args) + + app = QtWidgets.QApplication([]) + loop = QEventLoop(app) + asyncio.set_event_loop(loop) + atexit.register(loop.close) + smgr = state.StateManager(args.db_file) + + subscriber = ModelSubscriber("datasets", datasets.Model) + loop.run_until_complete(subscriber.connect( + args.server, args.port_notify)) + atexit_register_coroutine(subscriber.close) + sub_clients[notifier_name] = subscriber + + ("datasets", datasets.Model), + + # initialize main window + main_window = MainWindow() + smgr.register(main_window) + status_bar = QtWidgets.QStatusBar() + main_window.setStatusBar(status_bar) + mdi_area = MdiArea() + mdi_area.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded) + mdi_area.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded) + main_window.setCentralWidget(mdi_area) + + d_results = results.ResultsDock() + smgr.register(d_results) + + d_applets = applets.AppletsDock(main_window, datasets_sub) + atexit_register_coroutine(d_applets.stop) + smgr.register(d_applets) + + # lay out docks + right_docks = [d_results] + main_window.addDockWidget(QtCore.Qt.RightDockWidgetArea, right_docks[0]) + for d1, d2 in zip(right_docks, right_docks[1:]): + main_window.tabifyDockWidget(d1, d2) + + # load/initialize state + 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() + + if args.PATH: + d_results.select(args.PATH) + + smgr.start() + atexit_register_coroutine(smgr.stop) + + # run + main_window.show() + loop.run_until_complete(main_window.exit_request.wait()) + +if __name__ == "__main__": + main() diff --git a/artiq/gui/results.py b/artiq/gui/results.py new file mode 100644 index 000000000..63a565745 --- /dev/null +++ b/artiq/gui/results.py @@ -0,0 +1,71 @@ +import asyncio +import logging +from functools import partial + +from PyQt5 import QtCore, QtWidgets + +from artiq.gui.tools import LayoutWidget + + +logger = logging.getLogger(__name__) + + +class ResultsDock(QtWidgets.QDockWidget): + def __init__(self): + QtWidgets.QDockWidget.__init__(self, "Results") + self.setObjectName("Results") + self.setFeatures(QtWidgets.QDockWidget.DockWidgetMovable | + QtWidgets.QDockWidget.DockWidgetFloatable) + + top_widget = LayoutWidget() + self.setWidget(top_widget) + + self.stack = QtWidgets.QStackedWidget() + top_widget.addWidget(self.stack, 1, 0, colspan=2) + + self.rt_buttons = LayoutWidget() + self.rt_buttons.layout.setContentsMargins(0, 0, 0, 0) + self.stack.addWidget(self.rt_buttons) + + self.rt_model = QtWidgets.QFileSystemModel() + self.rt_model.setRootPath(QtCore.QDir.currentPath()) + self.rt_model.setNameFilters(["HDF5 files (*.h5)"]) + self.rt_model.setNameFilterDisables(False) + + self.rt = QtWidgets.QTreeView() + self.rt.setModel(self.rt_model) + self.rt.setRootIndex(self.rt_model.index(QtCore.QDir.currentPath())) + self.rt.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectItems) + self.rt.selectionModel().selectionChanged.connect( + self.selection_changed) + self.rt_buttons.addWidget(self.rt, 0, 0, colspan=2) + + def selection_changed(self, selected, deselected): + indexes = selected.indexes() + if indexes: + print(self.rt_model.filePath(indexes[0])) + + def select(self, path): + s = self.rt_model.index(path) + self.rt.selectionModel().setCurrentIndex( + s, + QtCore.QItemSelectionModel.ClearAndSelect) + self.rt.scrollTo(s) # TODO: call_soon? + + def resultname_action(self, action): + pass + + def save_state(self): + return { + "selected": self.rt_model.filePath( + self.rt.selectionModel().currentIndex()), + "header": bytes(self.rt.header().saveState()), + } + + def restore_state(self, state): + selected = state.get("selected") + if selected: + self.select(selected) + header = state.get("header") + if header: + self.rt.header().restoreState(QtCore.QByteArray(header))