Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: m-labs/artiq
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 1ff01a43ffaf
Choose a base ref
...
head repository: m-labs/artiq
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: aaa81a63d12d
Choose a head ref
  • 15 commits
  • 10 files changed
  • 1 contributor

Commits on Apr 5, 2016

  1. Copy the full SHA
    4e7b004 View commit details
  2. RELEASE_NOTES: style

    jordens committed Apr 5, 2016
    Copy the full SHA
    fbe89c8 View commit details
  3. Copy the full SHA
    7a5b3a1 View commit details
  4. Copy the full SHA
    8078e59 View commit details
  5. Copy the full SHA
    587a0f4 View commit details
  6. Copy the full SHA
    05c6146 View commit details
  7. RELEASE_NOTES: HDF5 encoding

    jordens committed Apr 5, 2016
    Copy the full SHA
    8af8365 View commit details
  8. worker_impl: style

    jordens committed Apr 5, 2016
    Copy the full SHA
    4759ea3 View commit details
  9. add artiq_browser

    jordens committed Apr 5, 2016
    Copy the full SHA
    91a362c View commit details
  10. Copy the full SHA
    6686383 View commit details
  11. Copy the full SHA
    fa63637 View commit details
  12. Copy the full SHA
    670e890 View commit details
  13. gui/results: add QListView

    jordens committed Apr 5, 2016
    Copy the full SHA
    826d529 View commit details
  14. Copy the full SHA
    4edfd6c View commit details
  15. Copy the full SHA
    aaa81a6 View commit details
Showing with 264 additions and 77 deletions.
  1. +0 −2 .gitignore
  2. +12 −0 RELEASE_NOTES.rst
  3. +125 −0 artiq/frontend/artiq_browser.py
  4. +20 −3 artiq/gui/models.py
  5. +85 −0 artiq/gui/results.py
  6. +3 −51 artiq/master/worker_db.py
  7. +9 −15 artiq/master/worker_impl.py
  8. +8 −6 artiq/test/test_h5types.py
  9. +1 −0 conda/artiq/meta.yaml
  10. +1 −0 setup.py
2 changes: 0 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -17,7 +17,6 @@ __pycache__/
/misoc_*/

/artiq/test/results
/artiq/test/h5types.h5
/artiq/examples/master/results
/artiq/examples/master/last_rid.pyon
/artiq/examples/master/dataset_db.pyon
@@ -31,5 +30,4 @@ __pycache__/
/last_rid.pyon
/dataset_db.pyon
/device_db.pyon
/h5types.h5
/test*.py
12 changes: 12 additions & 0 deletions RELEASE_NOTES.rst
Original file line number Diff line number Diff line change
@@ -20,6 +20,18 @@ unreleased [2.x]
just "-m qcX" or "-m clock" (#290).



unreleased [1.0rc3]
-------------------

* The HDF5 format has changed.

* The datasets are located in the HDF5 subgroup ``datasets``.
* Datasets are now stored without additional type annotations from ARTIQ,
trusting that h5py maps and convert types between HDF5 and python/numpy
"as expected".


1.0rc2
------

125 changes: 125 additions & 0 deletions artiq/frontend/artiq_browser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
#!/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 verbosity_args, init_logger, atexit_register_coroutine
from artiq.gui import state, results, datasets, applets, models


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 or file")
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)


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)

datasets_sub = models.LocalModelManager(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.ResultsBrowser(datasets_sub)
smgr.register(d_results)

d_applets = applets.AppletsDock(main_window, datasets_sub)
atexit_register_coroutine(d_applets.stop)
smgr.register(d_applets)

d_datasets = datasets.DatasetsDock(datasets_sub,
None) # TODO: datsets_ctl.delete()
smgr.register(d_datasets)

main_window.setCentralWidget(d_results)
main_window.addDockWidget(QtCore.Qt.BottomDockWidgetArea, d_applets)
main_window.addDockWidget(QtCore.Qt.RightDockWidgetArea, d_datasets)

# 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()
23 changes: 20 additions & 3 deletions artiq/gui/models.py
Original file line number Diff line number Diff line change
@@ -3,9 +3,8 @@
from artiq.protocols.sync_struct import Subscriber


class ModelSubscriber(Subscriber):
def __init__(self, notifier_name, model_factory):
Subscriber.__init__(self, notifier_name, self._create_model)
class ModelManager:
def __init__(self, model_factory):
self.model = None
self._model_factory = model_factory
self._setmodel_callbacks = []
@@ -22,6 +21,24 @@ def add_setmodel_callback(self, cb):
cb(self.model)


class ModelSubscriber(ModelManager, Subscriber):
def __init__(self, notifier_name, model_factory):
ModelManager.__init__(self, model_factory)
Subscriber.__init__(self, notifier_name, self._create_model)


class LocalModelManager(ModelManager):
def __init__(self, model_factory):
ModelManager.__init__(self, model_factory)
self.notify_cbs = []

def init(self, struct):
self._create_model(struct)
mod = {"action": "init", "struct": struct}
for notify_cb in self.notify_cbs:
notify_cb(mod)


class _SyncSubstruct:
def __init__(self, update_cb, ref):
self.update_cb = update_cb
85 changes: 85 additions & 0 deletions artiq/gui/results.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import logging

import h5py
from PyQt5 import QtCore, QtWidgets, QtGui

logger = logging.getLogger(__name__)


class ResultsBrowser(QtWidgets.QSplitter):
def __init__(self, datasets):
QtWidgets.QSplitter.__init__(self)

self.datasets = datasets

self.rt_model = QtWidgets.QFileSystemModel()
self.rt_model.setRootPath(QtCore.QDir.currentPath())
self.rt_model.setNameFilters(["*.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.SelectRows)
self.rt.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection)
self.rt.selectionModel().selectionChanged.connect(
self.selection_changed)
self.rt.setRootIsDecorated(False)
self.addWidget(self.rt)

self.rl = QtWidgets.QListView()
self.rl.setViewMode(QtWidgets.QListView.IconMode)
self.rl.setModel(self.rt_model)
self.rl.setSelectionModel(self.rt.selectionModel())
self.rl.setRootIndex(self.rt.rootIndex())
l = QtGui.QFontMetrics(self.font()).lineSpacing()
self.rl.setIconSize(QtCore.QSize(20*l, 20*l))
self.addWidget(self.rl)

def showEvent(self, ev):
if hasattr(self, "_shown"):
return
self._shown = True
self.rt.hideColumn(1)
self.rt.hideColumn(2)
self.rt.hideColumn(3)
self.rt.scrollTo(self.rt.selectionModel().currentIndex())

def selection_changed(self, selected, deselected):
indexes = selected.indexes()
if not indexes:
return
path = self.rt_model.filePath(indexes[0])
logger.info("opening %s", path)
try:
with h5py.File(path, "r") as f:
rd = {}
for k in f: #["datasets"]:
rd[k] = False, f[k].value
self.datasets.init(rd)
except:
pass

def select(self, path):
self.rt.selectionModel().setCurrentIndex(
self.rt_model.index(path),
QtCore.QItemSelectionModel.ClearAndSelect)

def save_state(self):
return {
"selected": self.rt_model.filePath(
self.rt.selectionModel().currentIndex()),
"header": bytes(self.rt.header().saveState()),
"splitter": bytes(self.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))
splitter = state.get("splitter")
if splitter:
self.restoreState(QtCore.QByteArray(splitter))
54 changes: 3 additions & 51 deletions artiq/master/worker_db.py
Original file line number Diff line number Diff line change
@@ -167,56 +167,6 @@ def get_hdf5_output(start_time, rid, name):
return h5py.File(os.path.join(dirname, filename), "w")


_type_to_hdf5 = {
int: h5py.h5t.STD_I64BE,
float: h5py.h5t.IEEE_F64BE,

np.int8: h5py.h5t.STD_I8BE,
np.int16: h5py.h5t.STD_I16BE,
np.int32: h5py.h5t.STD_I32BE,
np.int64: h5py.h5t.STD_I64BE,

np.uint8: h5py.h5t.STD_U8BE,
np.uint16: h5py.h5t.STD_U16BE,
np.uint32: h5py.h5t.STD_U32BE,
np.uint64: h5py.h5t.STD_U64BE,

np.float16: h5py.h5t.IEEE_F16BE,
np.float32: h5py.h5t.IEEE_F32BE,
np.float64: h5py.h5t.IEEE_F64BE
}

def result_dict_to_hdf5(f, rd):
for name, data in rd.items():
flag = None
# beware: isinstance(True/False, int) == True
if isinstance(data, bool):
data = np.int8(data)
flag = "py_bool"
elif isinstance(data, int):
data = np.int64(data)
flag = "py_int"

if isinstance(data, np.ndarray):
dataset = f.create_dataset(name, data=data)
else:
ty = type(data)
if ty is str:
ty_h5 = "S{}".format(len(data))
data = data.encode()
else:
try:
ty_h5 = _type_to_hdf5[ty]
except KeyError:
raise TypeError("Type {} is not supported for HDF5 output"
.format(ty)) from None
dataset = f.create_dataset(name, (), ty_h5)
dataset[()] = data

if flag is not None:
dataset.attrs[flag] = np.int8(1)


class DatasetManager:
def __init__(self, ddb):
self.broadcast = Notifier(dict())
@@ -250,4 +200,6 @@ def get(self, key):
return self.ddb.get(key)

def write_hdf5(self, f):
result_dict_to_hdf5(f, self.local)
g = f.create_group("datasets")
for k, v in self.local.items():
g[k] = v
Loading