Source code for qtt.utilities.tools

import copy
from collections import OrderedDict
from typing import Type
import dateutil
import sys
import os
import numpy as np
import pprint
import matplotlib
import uuid
import logging
import qcodes
import warnings
import functools
import pickle
import inspect
import tempfile
from itertools import chain
import scipy.ndimage as ndimage
from functools import wraps
import datetime
import time
import importlib
import platform
import matplotlib.pyplot as plt
from matplotlib.widgets import Button

from qcodes.data.data_set import DataSet

NotGitRepositoryError: Type[Exception]

try:
    from dulwich.repo import Repo, NotGitRepository
    from dulwich import porcelain
    NotGitRepositoryError = NotGitRepository
except ModuleNotFoundError:
    warnings.warn('please install dulwich: pip install dulwich --global-option="--pure"')
    NotGitRepositoryError = Exception

# explicit import
from qcodes.plots.qcmatplotlib import MatPlot

try:
    from qcodes.plots.pyqtgraph import QtPlot
except BaseException:
    pass

from qcodes.data.data_array import DataArray

import qtt.pgeometry
from qtt.pgeometry import mpl2clipboard

from qtt.pgeometry import tilefigs, mkdirc  # import for backwards compatibility

# do NOT load any other qtt submodules here

try:
    import qtpy.QtGui as QtGui
    import qtpy.QtCore as QtCore
    import qtpy.QtWidgets as QtWidgets
except BaseException:
    pass


# %%

from typing import Optional
import contextlib
[docs]@contextlib.contextmanager def logging_context(level: int = logging.INFO, logger: Optional[logging.Logger] = None): """ A context manager that changes the logging level Args: level: Logging level to set in the context logger: Logger to update, if None then update the default logger """ if logger is None: logger = logging.getLogger() previous_level = logger.getEffectiveLevel() logger.setLevel(level) try: yield finally: logger.setLevel(previous_level)
[docs]def is_spyder_environment(): """ Return True if the process is running in a Spyder environment """ return 'SPY_TESTING' in os.environ
[docs]def get_module_versions(modules, verbose=0): """ Returns the module version of the given pip packages. Args: modules ([str]): a list with pip packages, e.g. ['numpy', 'scipy']. verbose (int): verbosity (0 == silent). Returns: r (dict): dictionary with package names and version number for each given module. """ module_versions = dict() for module in modules: try: package = importlib.import_module(module) version = getattr(package, '__version__', 'none') module_versions[module] = version except ModuleNotFoundError: module_versions[module] = 'none' if verbose: print('module {0} {1}'.format(package, version)) return module_versions
[docs]def get_git_versions(repos, get_dirty_status=False, verbose=0): """ Returns the repository head guid and dirty status and package version number if installed via pip. The version is only returned if the repo is installed as pip package without edit mode. NOTE: currently the dirty status is not working correctly due to a bug in dulwich... Args: repos ([str]): a list with repositories, e.g. ['qtt', 'qcodes']. get_dirty_status (bool): selects whether to use the dulwich package and collect the local code changes for the repositories. verbose (int): verbosity (0 == silent). Retuns: r (dict): dictionary with repo names, head guid and (optionally) dirty status for each given repository. """ heads = dict() dirty_stats = dict() for repo in repos: try: package = importlib.import_module(repo) init_location = os.path.split(package.__file__)[0] repo_location = os.path.join(init_location, '..') repository = Repo(repo_location) heads[repo] = repository.head().decode('ascii') if get_dirty_status: status = porcelain.status(repository) is_dirty = len(status.unstaged) == 0 or any(len(item) != 0 for item in status.staged.values()) dirty_stats[repo] = is_dirty except (AttributeError, ModuleNotFoundError, NotGitRepositoryError): heads[repo] = 'none' if get_dirty_status: dirty_stats[repo] = 'none' if verbose: print('{0}: {1}'.format(repo, heads[repo])) return heads, dirty_stats
[docs]def get_python_version(verbose=0): """ Returns the python version.""" version = sys.version if verbose: print('Python', version) return version
[docs]def code_version(repository_names=None, package_names=None, get_dirty_status=False, verbose=0): """ Returns the python version, module version for; numpy, scipy, qupulse and the git guid and dirty status for; qcodes, qtt, spin-projects and pycqed, if present on the machine. NOTE: currently the dirty status is not working correctly due to a bug in dulwich... Args: repository_names ([str]): a list with repositories, e.g. ['qtt', 'qcodes']. package_names ([str]): a list with pip packages, e.g. ['numpy', 'scipy']. get_dirty_status (bool): selects whether the local code has changed for the repositories. verbose (int): output level. Returns: status (dict): python, modules and git repos status. """ _default_git_versions = ['qcodes', 'qtt', 'projects', 'pycqed'] _default_module_versions = ['numpy', 'scipy', 'qupulse', 'h5py', 'skimage'] if not repository_names: repository_names = _default_git_versions if not package_names: package_names = _default_module_versions result = dict() repository_stats, dirty_stats = get_git_versions(repository_names, get_dirty_status, verbose) result['python'] = get_python_version(verbose) result['git'] = repository_stats result['version'] = get_module_versions(package_names, verbose) result['timestamp'] = datetime.datetime.now().isoformat() # ISO 8601 result['system'] = {'node': platform.node()} if get_dirty_status: result['dirty'] = dirty_stats return result
# %% Debugging
[docs]def deprecated(func): """ This is a decorator which can be used to mark functions as deprecated. It will result in a warning being emitted when the function is used.""" @functools.wraps(func) def new_func(*args, **kwargs): try: filename = inspect.getfile(func) except BaseException: filename = '?' try: lineno = inspect.getlineno(func) except BaseException: lineno = -1 warnings.warn_explicit( "Call to deprecated function {}.".format(func.__name__), category=UserWarning, filename=filename, lineno=lineno, ) return func(*args, **kwargs) return new_func
[docs]def rdeprecated(txt=None, expire=None): """ This is a decorator which can be used to mark functions as deprecated. It will result in a warning being emitted when the function is used. After the expiration data the decorator will generate an Exception. Args: txt (str): reason for deprecation. expire (str): date of expiration. """ import datetime from dateutil import parser if expire is not None: now = datetime.datetime.now() expiredate = parser.parse(expire) dt = expiredate - now expired = dt.total_seconds() < 0 else: expired = None def deprecated_inner(func): """ This is a decorator which can be used to mark functions as deprecated. It will result in a warning being emitted when the function is used.""" @functools.wraps(func) def new_func(*args, **kwargs): try: filename = inspect.getfile(func) except BaseException: filename = '?' try: lineno = inspect.getlineno(func) except BaseException: lineno = -1 if txt is None: etxt = '' else: etxt = ' ' + txt if expire is not None: if expired: raise Exception("Call to deprecated function {}.{}".format(func.__name__, etxt)) else: warnings.warn_explicit( "Call to deprecated function {} (will expire on {}).{}".format( func.__name__, expiredate, etxt), category=UserWarning, filename=filename, lineno=lineno, ) else: warnings.warn_explicit( "Call to deprecated function {}.{}".format(func.__name__, etxt), category=UserWarning, filename=filename, lineno=lineno, ) return func(*args, **kwargs) return new_func return deprecated_inner
# %%
[docs]def update_dictionary(alldata, **kwargs): """ Update elements of a dictionary. Args: alldata (dict): dictionary to be updated. kwargs (dict): keyword arguments. """ for k in kwargs: alldata[k] = kwargs[k]
[docs]def stripDataset(dataset): """ Make sure a dataset can be pickled . Args: dataset (qcodes DataSet): TODO. Returns: dataset (qcodes DataSet): the dataset from the function argument. """ dataset.sync() dataset.data_manager = None dataset.background_functions = {} try: dataset.formatter.close_file(dataset) except BaseException: pass if 'scanjob' in dataset.metadata: if 'minstrumenthandle' in dataset.metadata['scanjob']: dataset.metadata['scanjob']['minstrumenthandle'] = str( dataset.metadata['scanjob']['minstrumenthandle']) return dataset
# %%
[docs]def negfloat(x): """ Helper function """ return -float(x)
[docs]def checkPickle(obj, verbose=0): """ Check whether an object can be pickled. Args: obj (object): object to be checked. verbose (int): verbosity (0 == silent). Returns: c (bool): True of the object can be pickled. """ try: _ = pickle.dumps(obj) except Exception as ex: if verbose: print(ex) return False return True
[docs]def freezeclass(cls): """ Decorator to freeze a class.""" cls.__frozen = False def frozensetattr(self, key, value): if self.__frozen and not hasattr(self, key): print("Class {} is frozen. Cannot set {} = {}" .format(cls.__name__, key, value)) else: object.__setattr__(self, key, value) def init_decorator(func): @wraps(func) def wrapper(self, *args, **kwargs): func(self, *args, **kwargs) self.__frozen = True return wrapper cls.__setattr__ = frozensetattr cls.__init__ = init_decorator(cls.__init__) return cls
# %%
[docs]def resampleImage(im): """ Resample the image so it has the similar sample rates (samples/mV) in both axis. Args: im (DataArray): input image. Returns: imr (numpy array): resampled image. setpoints (list of 2 numpy arrays): setpoint arrays from resampled image. """ setpoints = im.set_arrays mVrange = [abs(setpoints[0][-1] - setpoints[0][0]), abs(setpoints[1][0, -1] - setpoints[1][0, 0])] samprates = [im.shape[0] // mVrange[0], im.shape[1] // mVrange[1]] factor = int(max(samprates) // min(samprates)) if factor >= 2: axis = int(samprates[0] - samprates[1] < 0) if axis == 0: facrem = im.shape[0] % factor if facrem > 0: im = im[:-facrem, :] facrem = facrem + 1 im = im.reshape(im.shape[0] // factor, factor, im.shape[1]).mean(1) spy = np.linspace(setpoints[0][0], setpoints[ 0][-facrem], im.shape[0]) spx = np.tile(np.expand_dims(np.linspace( setpoints[1][0, 0], setpoints[1][0, -1], im.shape[1]), 0), im.shape[0]) setpointy = DataArray(name='Resampled_' + setpoints[0].array_id, array_id='Resampled_' + setpoints[0].array_id, label=setpoints[0].label, unit=setpoints[0].unit, preset_data=spy, is_setpoint=True) setpointx = DataArray(name='Resampled_' + setpoints[1].array_id, array_id='Resampled_' + setpoints[1].array_id, label=setpoints[1].label, unit=setpoints[1].unit, preset_data=spx, is_setpoint=True) setpoints = [setpointy, setpointx] else: facrem = im.shape[1] % factor if facrem > 0: im = im[:, :-facrem] facrem = facrem + 1 im = im.reshape(im.shape[0], im.shape[1] // factor, factor).mean(-1) spx = np.tile(np.expand_dims(np.linspace(setpoints[1][0, 0], setpoints[ 1][0, -facrem], im.shape[1]), 0), [im.shape[0], 1]) idx = setpoints[1].array_id if idx is None: idx = 'x' idy = setpoints[1].array_id if idy is None: idy = 'y' setpointx = DataArray(name='Resampled_' + idx, array_id='Resampled_' + idy, label=setpoints[1].label, unit=setpoints[1].unit, preset_data=spx, is_setpoint=True) setpoints = [setpoints[0], setpointx] return im, setpoints
[docs]def diffImage(im, dy, size=None): """ Simple differentiation of an image. Args: im (numpy array): input image. dy (integer or string): method of differentiation. For an integer it is the axis of differentiation. Allowed strings are 'x', 'y', 'xy'. size (str): describes the size e.g. 'same'. """ if dy == 0 or dy == 'x': im = np.diff(im, n=1, axis=1) if size == 'same': im = np.hstack((im, im[:, -1:])) elif dy == 1 or dy == 'y': im = np.diff(im, n=1, axis=0) if size == 'same': im = np.vstack((im, im[-1:, :])) elif dy == -1: im = -np.diff(im, n=1, axis=0) if size == 'same': im = np.vstack((im, im[-1:, :])) elif dy == 2 or dy == 'xy': imx = np.diff(im, n=1, axis=1) imy = np.diff(im, n=1, axis=0) im = imx[0:-1, :] + imy[:, 0:-1] elif dy is None: pass else: raise Exception('differentiation method %s not supported' % dy) return im
[docs]def diffImageSmooth(im, dy='x', sigma=2): """ Simple differentiation of an image. Args: im (array): input image. dy (string or integer): direction of differentiation. can be 'x' (0) or 'y' (1) or 'xy' (2) or 'g' (3). sigma (float): parameter for gaussian filter kernel. """ if sigma is None: imx = diffImage(im, dy) return imx if dy is None: imx = im.copy() elif dy == 0 or dy == 'x': if len(im.shape) == 1: raise Exception(f'invalid parameter dy={dy} for 1D image') else: imx = ndimage.gaussian_filter1d(im, axis=1, sigma=sigma, order=1, mode='nearest') elif dy == 1 or dy == 'y': imx = ndimage.gaussian_filter1d(im, axis=0, sigma=sigma, order=1, mode='nearest') elif dy == -1: imx = -ndimage.gaussian_filter1d(im, axis=0, sigma=sigma, order=1, mode='nearest') elif dy == 2 or dy == 3 or dy == 'xy' or dy == 'xmy' or dy == 'xmy2' or dy == 'g' or dy == 'x2my2' or dy == 'x2y2': if len(np.array(im).shape) != 2: raise Exception(f'differentiation mode {dy} cannot be combined with input shape {np.array(im).shape}') imx0 = ndimage.gaussian_filter1d( im, axis=1, sigma=sigma, order=1, mode='nearest') imx1 = ndimage.gaussian_filter1d( im, axis=0, sigma=sigma, order=1, mode='nearest') if dy == 2 or dy == 'xy': imx = imx0 + imx1 if dy == 'xmy': imx = imx0 - imx1 if dy == 3 or dy == 'g': imx = np.sqrt(imx0 ** 2 + imx1 ** 2) if dy == 'xmy2': warnings.warn('please do not use this option') imx = np.sqrt(imx0 ** 2 + imx1 ** 2) if dy == 'x2y2': imx = imx0 ** 2 + imx1 ** 2 if dy == 'x2my2': imx = imx0 ** 2 - imx1 ** 2 else: raise Exception('differentiation method %s not supported' % dy) return imx
[docs]def scanTime(dd): """ Return date a scan was performed.""" w = dd.metadata.get('scantime', None) if isinstance(w, str): w = dateutil.parser.parse(w) return w
[docs]@rdeprecated(txt='Use dataset.default_parameter_array instead', expire='1 Sep 2019') def plot_parameter(data, default_parameter='amplitude'): """ Return parameter to be plotted.""" if 'main_parameter' in data.metadata.keys(): return data.metadata['main_parameter'] if default_parameter in data.arrays.keys(): return default_parameter try: key = next(iter(data.arrays.keys())) return key except BaseException: return None
[docs]@rdeprecated(txt='Use plot_dataset instead', expire='1 Sep 2019') def plot1D(dataset, fig=1): """ Simple plot function.""" if isinstance(dataset, DataArray): array = dataset dataset = None else: # assume we have a dataset arrayname = plot_parameter(dataset) array = getattr(dataset, arrayname) if fig is not None and array is not None: MatPlot(array, num=fig)
# %%
[docs]def showImage(im, extent=None, fig=None, title=None): """ Show image in figure window. Args: im (array): TODO. extent (list): matplotlib style image extent. fig (None or int): figure window to show image. title (None or str): figure title. """ import matplotlib.pyplot as plt if fig is not None: plt.figure(fig) plt.clf() plt.imshow(im, extent=extent, interpolation='nearest') if extent is not None: if extent[0] > extent[1]: plt.gca().invert_xaxis() if title is not None: plt.title(title)
# %% Measurement tools
[docs]@rdeprecated(txt='Use gates.resetgates instead', expire='1 Sep 2019') def resetgates(gates, activegates, basevalues=None, verbose=2): """ Reset a set of gates to default values. Args: gates : list of gates. activegates (list or dict): list of gates to reset. basevalues (dict): new values for the gates. verbose (int): output level. """ if verbose: print('resetgates: setting gates to default values') for g in activegates: if basevalues is None: val = 0 else: if g in basevalues.keys(): val = basevalues[g] else: val = 0 if verbose >= 2: print(' setting gate %s to %.1f [mV]' % (g, val)) gates.set(g, val)
# %% Tools from pgeometry
[docs]@rdeprecated(txt='Use qtt.pgeometry.plot2Dline instead', expire='1 Sep 2019') def plot2Dline(line, *args, **kwargs): """ Plot a 2D line in a matplotlib figure. Args: line (array): 3x1 array. >>> plot2Dline([-1,1,0], 'b') """ if np.abs(line[1]) > .001: xx = plt.xlim() xx = np.array(xx) yy = (-line[2] - line[0] * xx) / line[1] plt.plot(xx, yy, *args, **kwargs) else: yy = np.array(plt.ylim()) xx = (-line[2] - line[1] * yy) / line[0] plt.plot(xx, yy, *args, **kwargs)
[docs]def cfigure(*args, **kwargs): """ Create Matplotlib figure with copy to clipboard functionality. By pressing the 'c' key figure is copied to the clipboard. """ if 'facecolor' in kwargs: fig = plt.figure(*args, **kwargs) else: fig = plt.figure(*args, facecolor='w', **kwargs) def ff(xx, figx=fig): return mpl2clipboard(fig=figx) fig.canvas.mpl_connect('key_press_event', ff) # mpl2clipboard) return fig
[docs]def static_var(varname, value): """ Helper function to create a static variable.""" def decorate(func): setattr(func, varname, value) return func return decorate
try: import qtpy.QtGui as QtGui import qtpy.QtWidgets as QtWidgets def monitorSizes(verbose=0): """ Return monitor sizes.""" _qd = QtWidgets.QDesktopWidget() if sys.platform == 'win32' and _qd is None: import ctypes user32 = ctypes.windll.user32 wa = [ [0, 0, user32.GetSystemMetrics(0), user32.GetSystemMetrics(1)]] else: _applocalqt = QtWidgets.QApplication.instance() if _applocalqt is None: _applocalqt = QtWidgets.QApplication([]) _qd = QtWidgets.QDesktopWidget() else: _qd = QtWidgets.QDesktopWidget() nmon = _qd.screenCount() wa = [_qd.screenGeometry(ii) for ii in range(nmon)] wa = [[w.x(), w.y(), w.width(), w.height()] for w in wa] if verbose: for ii, w in enumerate(wa): print('monitor %d: %s' % (ii, str(w))) return wa except BaseException:
[docs] def monitorSizes(verbose=0): """ Dummy function for monitor sizes.""" return [[0, 0, 1600, 1200]]
pass # %% Helper tools
[docs]def in_ipynb(): try: cfg = get_ipython().config if cfg['IPKernelApp']['parent_appname'] == 'ipython-notebook': return True else: return False except NameError: return False
[docs]def pythonVersion(): try: import IPython ipversion = '.'.join('%s' % x for x in IPython.version_info[:-1]) except BaseException: ipversion = 'None' pversion = '.'.join('%s' % x for x in sys.version_info[0:3]) print('python %s, ipython %s, notebook %s' % (pversion, ipversion, in_ipynb()))
# %% def _covert_integer_to_rgb_color(integer_value): if integer_value < 0 or integer_value > 256 ** 3: raise ValueError('Integer value cannot be converted to RGB!') red = integer_value & 0xFF green = (integer_value >> 8) & 0xFF blue = (integer_value >> 16) & 0xFF return red, green, blue def _convert_rgb_color_to_integer(rgb_color): if not isinstance(rgb_color, tuple) or not all(isinstance(i, int) for i in rgb_color): raise ValueError('Color should be an RGB integer tuple.') if len(rgb_color) != 3: raise ValueError('Color should be an RGB integer tuple with three items.') if any(i < 0 or i > 255 for i in rgb_color): raise ValueError('Color should be an RGB tuple in the range 0 to 255.') red = rgb_color[0] green = rgb_color[1] << 8 blue = rgb_color[2] << 16 return int(red + green + blue)
[docs]def set_ppt_slide_background(slide, color, verbose=0): """ Sets the background color of PPT slide. Args: slide (object): PowerPoint COM object for slide. color (tuple): tuple with RGB color specification. verbose (int): verbosity (0 == silent). """ fore_color = slide.Background.Fill.ForeColor ppt_color = _convert_rgb_color_to_integer(color) if verbose > 1: print('Setting PPT slide background color:') print(' - Current color: {0}'.format(_covert_integer_to_rgb_color(fore_color.RGB))) print(' - Setting to {0} -> {1}'.format(color, ppt_color)) slide.FollowMasterBackground = 0 fore_color.RGB = ppt_color
# %% def _ppt_determine_image_position(ppt, figsize, fname, verbose=1): top = 120 if figsize is not None: left = (ppt.PageSetup.SlideWidth - figsize[0]) / 2 width = figsize[0] height = figsize[1] else: slidewh = [ppt.PageSetup.SlideWidth, ppt.PageSetup.SlideHeight] width = 16 * ((slidewh[0] * .75) // 16) height = 16 * (((slidewh[1] - 120) * .9) // 16) height = min(height, 350) left = (slidewh[0] - width) / 2 try: import cv2 imwh = cv2.imread(fname).shape[1], cv2.imread(fname).shape[0] except BaseException: imwh = None if imwh is not None: imratio = imwh[0] / imwh[1] slideratio = slidewh[0] / slidewh[1] if verbose >= 1: print(' image aspect ratio %.2f, slide aspect ratio %.2f' % (imratio, slideratio)) if slideratio > imratio: # wide slide, so make the image width smaller print('adjust width %d->%d' % (width, height * imratio)) width = height * imratio else: # wide image, so make the image height smaller print('adjust height %d->%d' % (height, width / imratio)) height = int(width / imratio) if verbose: print('slide width height: %s' % (slidewh,)) print('image width height: %d, %d' % (width, height)) return left, top, width, height
[docs]def create_figure_ppt_callback(fig, title=None, notes=None, position=(0.9, 0.925, 0.075, 0.05)): """ Create a callback on a matplotlib figure to copy data to PowerPoint slide. The figure is copied to PowerPoint using @ref addPPTslide. Args: fig (int): handle to matplotlib window. title (None or str): title for the slide. notes (None or str or DataSet): notes to add to the slide. position (list): position specified as fraction left, right, width, height. Example: >>> plt.figure(10) >>> plt.plot(np.arange(100), np.random.rand(100), 'o', label='input data') >>> create_figure_ppt_callback(10, 'test') >>> plt.show() """ if fig is None: fig = plt.gcf().number plt.figure(fig) ax = plt.gca() ppt_axis = plt.axes(position, label=f'figure_ppt_callback_axis {uuid.uuid1()}') ppt_button = Button(ppt_axis, 'ppt') ppt_axis._button = ppt_button ppt_axis.set_alpha(.5) plt.sca(ax) def figure_ppt_callback(event): print('creating PowerPoint slide for figure %d' % fig) ppt_axis.set_visible(False) addPPTslide(fig=fig, title=title, notes=notes) ppt_axis.set_visible(True) ppt_button.on_clicked(figure_ppt_callback)
try: import win32com import win32com.client def addPPTslide(title=None, fig=None, txt=None, notes=None, figsize=None, subtitle=None, maintext=None, show=False, verbose=1, activate_slide=True, ppLayout=None, extranotes=None, background_color=None, maximum_notes_size: int = 10000): """ Add slide to current active Powerpoint presentation. Arguments: title (str): title added to slide. fig (matplotlib.figure.Figure or qcodes.plots.pyqtgraph.QtPlot or integer): figure added to slide. txt (str) : Deprecated, use subtitle instead. notes (str or QCoDeS station): notes added to slide. figsize (list): size (width,height) of figurebox to add to powerpoint. subtitle (str): text added to slide as subtitle. maintext (str): text in textbox added to slide. show (bool): shows the powerpoint application. verbose (int): print additional information. activate_slide (bool): activate the current slide. ppLayout (int): layout of PP-slide (TitleOnly = 11, Text = 2). extranotes (str): notes for slide. background_color (None or tuple): background color for the slide. maximum_notes_size: Maximum size of the notes in number of characters Returns: ppt: PowerPoint presentation. slide: PowerPoint slide. The interface to Powerpoint used is described here: https://msdn.microsoft.com/en-us/library/office/ff743968.aspx Example: >>> title = 'An example title' >>> fig = plt.figure(10) >>> txt = 'Some comments on the figure' >>> notes = 'some additional information' >>> addPPTslide(title,fig, subtitle = txt,notes = notes) """ Application = win32com.client.Dispatch("PowerPoint.Application") if verbose >= 2: print('Number of open PPTs: %d.' % Application.presentations.Count) try: ppt = Application.ActivePresentation except Exception: print('Could not open active Powerpoint presentation, opening blank presentation.') try: ppt = Application.Presentations.Add() except Exception as ex: warnings.warn('Could not make connection to Powerpoint presentation.') return None, None if show: Application.Visible = True # shows what's happening, not required, but helpful for now if verbose >= 2: print('addPPTslide: presentation name: %s' % ppt.Name) ppLayoutTitleOnly = 11 ppLayoutText = 2 if txt is not None: if subtitle is None: warnings.warn('please do not use the txt field any more') subtitle = txt else: raise ValueError('please do not use the txt field any more') txt = None if fig is None: # no figure, text box over entire page if ppLayout is None: ppLayout = ppLayoutText else: # we have a figure, assume textbox is for dataset name only ppLayout = ppLayoutTitleOnly max_slides_count_warning = 750 max_slides_count = 950 maximum_notes_size = int(maximum_notes_size) if ppt.Slides.Count > max_slides_count_warning: warning_message = "Your presentation has more than {} slides! \ Please start a new measurement logbook.".format(max_slides_count_warning) warnings.warn(warning_message) if ppt.Slides.Count > max_slides_count: error_message = "Your presentation has more than {} slides! \ Please start a new measurement logbook.".format(max_slides_count) raise MemoryError(error_message) if verbose: print('addPPTslide: presentation name: %s, adding slide %d' % (ppt.Name, ppt.Slides.count + 1)) slide = ppt.Slides.Add(ppt.Slides.Count + 1, ppLayout) if background_color is not None: set_ppt_slide_background(slide, background_color, verbose=verbose) if fig is None: titlebox = slide.shapes.Item(1) mainbox = slide.shapes.Item(2) if maintext is None: raise TypeError('maintext argument is None') mainbox.TextFrame.TextRange.Text = maintext else: titlebox = slide.shapes.Item(1) mainbox = None if maintext is not None: warnings.warn('maintext not implemented when figure is set') if title is not None: slide.shapes.title.textframe.textrange.text = title else: slide.shapes.title.textframe.textrange.text = 'QCoDeS measurement' from qtt.measurements.videomode import VideoMode # import here, to prevent default imports of gui code if fig is not None: fname = tempfile.mktemp(prefix='qcodesimageitem', suffix='.png') if isinstance(fig, matplotlib.figure.Figure): fig.savefig(fname) elif isinstance(fig, int): fig = plt.figure(fig) fig.savefig(fname) elif isinstance(fig, VideoMode) or \ fig.__class__.__name__ == 'VideoMode': if isinstance(fig.lp, list): # do NOT change this into a list comprehension ff = [] for jj in range(len(fig.lp)): ff.append(fig.lp[jj].plotwin.grab()) sz = ff[0].size() sz = QtCore.QSize(sz.width() * len(ff), sz.height()) figtemp = QtGui.QPixmap(sz) p = QtGui.QPainter(figtemp) offset = 0 for ii in range(len(ff)): p.drawPixmap(offset, 0, ff[ii]) offset += ff[ii].size().width() figtemp.save(fname) p.end() else: figtemp = fig.lp.plotwin.grab() figtemp.save(fname) elif isinstance(fig, QtWidgets.QWidget): # generic method figtemp = fig.plotwin.grab() figtemp.save(fname) elif isinstance(fig, QtWidgets.QWidget): try: figtemp = QtGui.QPixmap.grabWidget(fig) except BaseException: figtemp = fig.grab() figtemp.save(fname) elif isinstance(fig, qcodes.plots.pyqtgraph.QtPlot): fig.save(fname) else: if verbose: raise TypeError('figure is of an unknown type %s' % (type(fig),)) top = 120 left, top, width, height = _ppt_determine_image_position(ppt, figsize, fname, verbose=1) if verbose >= 2: print('fname %s' % fname) slide.Shapes.AddPicture(FileName=fname, LinkToFile=False, SaveWithDocument=True, Left=left, Top=top, Width=width, Height=height) if subtitle is not None: # add subtitle subtitlebox = slide.Shapes.AddTextbox( 1, Left=100, Top=80, Width=500, Height=300) subtitlebox.Name = 'subtitle box' subtitlebox.TextFrame.TextRange.Text = subtitle if notes is None: warnings.warn( 'please set notes for the powerpoint slide. e.g. use the station or reshape_metadata') if isinstance(notes, qcodes.Station): station = notes gates = getattr(station, 'gates', None) notes = reshape_metadata(station, printformat='s', add_scanjob=True) if extranotes is not None: notes = '\n' + extranotes + '\n' + notes if gates is not None: notes = 'gates: ' + str(gates.allvalues()) + '\n\n' + notes elif isinstance(notes, DataSet): notes = reshape_metadata(notes, printformat='s', add_gates=True) if not isinstance(notes, str): warnings.warn(f'type of notes argument is {type(notes)}, converting to string') notes = str(notes) if notes is not None: if notes == '': notes = ' ' if len(notes) > maximum_notes_size: warnings.warn(f'notes for powerpoint are {len(notes)} characters, reducing to {maximum_notes_size}') notes = notes[:maximum_notes_size] slide.notespage.shapes.placeholders[ 2].textframe.textrange.insertafter(notes) if activate_slide: idx = int(slide.SlideIndex) if verbose >= 2: print('addPPTslide: goto slide %d' % idx) Application.ActiveWindow.View.GotoSlide(idx) return ppt, slide def addPPT_dataset(dataset, title=None, notes=None, show=False, verbose=1, paramname='measured', printformat='fancy', customfig=None, extranotes=None, **kwargs): """ Add slide based on dataset to current active Powerpoint presentation. Args: dataset (DataSet): data and metadata from DataSet added to slide. customfig (QtPlot): custom QtPlot object to be added to slide (for dataviewer). notes (string): notes added to slide. show (bool): shows the powerpoint application. verbose (int): print additional information. paramname (None or str): passed to dataset.default_parameter_array. printformat (string): 'fancy' for nice formatting or 'dict' for easy copy to python. Returns: ppt: PowerPoint presentation. slide: PowerPoint slide. Example: ------- >>> notes = 'some additional information' >>> addPPT_dataset(dataset,notes) """ if len(dataset.arrays) < 2: raise IndexError('The dataset contains less than two data arrays') if customfig is None: if isinstance(paramname, str): if title is None: parameter_name = dataset.default_parameter_name(paramname=paramname) title = 'Parameter: %s' % parameter_name temp_fig = QtPlot(dataset.default_parameter_array( paramname=paramname), show_window=False) else: if title is None: title = 'Parameter: %s' % (str(paramname),) for idx, parameter_name in enumerate(paramname): if idx == 0: temp_fig = QtPlot(dataset.default_parameter_array( paramname=parameter_name), show_window=False) else: temp_fig.add(dataset.default_parameter_array( paramname=parameter_name)) else: temp_fig = customfig text = 'Dataset location: %s' % dataset.location if notes is None: try: metastring = reshape_metadata(dataset, printformat=printformat) except Exception as ex: metastring = 'Could not read metadata: %s' % str(ex) notes = 'Dataset %s metadata:\n\n%s' % (dataset.location, metastring) scanjob = dataset.metadata.get('scanjob', None) if scanjob is not None: s = pprint.pformat(scanjob) notes = 'scanjob: ' + str(s) + '\n\n' + notes gatevalues = dataset.metadata.get('allgatevalues', None) if gatevalues is not None: notes = 'gates: ' + str(gatevalues) + '\n\n' + notes ppt, slide = addPPTslide(title=title, fig=temp_fig, subtitle=text, notes=notes, show=show, verbose=verbose, extranotes=extranotes, **kwargs) return ppt, slide except ImportError:
[docs] def addPPTslide(title=None, fig=None, txt=None, notes=None, figsize=None, subtitle=None, maintext=None, show=False, verbose=1, activate_slide=True, ppLayout=None, extranotes=None, background_color=None, maximum_notes_size: int = 10000): """ Add slide to current active Powerpoint presentation. Dummy implementation. """ warnings.warn( 'addPPTslide is not available on your system. Install win32com from https://pypi.org/project/pypiwin32/.')
[docs] def addPPT_dataset(dataset, title=None, notes=None, show=False, verbose=1, paramname='measured', printformat='fancy', customfig=None, extranotes=None, **kwargs): """ Add slide based on dataset to current active Powerpoint presentation. Dummy implementation. """ warnings.warn( 'addPPT_dataset is not available on your system. Install win32com from https://pypi.org/project/pypiwin32/.')
# %%
[docs]def reshape_metadata(dataset, printformat='dict', add_scanjob=True, add_gates=True, add_analysis_results=True, verbose=0): """ Reshape the metadata of a DataSet. Args: dataset (DataSet or qcodes.Station): a dataset of which the metadata will be reshaped. printformat (str): can be 'dict' or 'txt','fancy' (text format). add_scanjob (bool): If True, then add the scanjob at the beginning of the notes. add_analysis_results (bool): If True, then add the analysis_results at the beginning of the notes. add_gates (bool): If True, then add the scanjob at the beginning of the notes. verbose (int): verbosity (0 == silent). Returns: str: the reshaped metadata. """ if isinstance(dataset, qcodes.Station): station = dataset all_md = station.snapshot(update=False)['instruments'] header = None else: tmp = dataset.metadata.get('station', None) if tmp is None: all_md = {} else: all_md = tmp['instruments'] header = 'dataset: %s' % dataset.location if hasattr(dataset.io, 'base_location'): header += ' (base %s)' % dataset.io.base_location if add_gates: gate_values = dataset.metadata.get('allgatevalues', None) if gate_values is not None: gate_values = dict([(key, np.around(value, 3)) for key, value in gate_values.items()]) header += '\ngates: ' + str(gate_values) + '\n' scanjob = dataset.metadata.get('scanjob', None) if scanjob is not None and add_scanjob: s = pprint.pformat(scanjob) header += '\n\nscanjob: ' + str(s) + '\n' analysis_results = dataset.metadata.get('analysis_results', None) if analysis_results is not None and add_analysis_results: s = pprint.pformat(analysis_results) header += '\n\analysis_results: ' + str(s) + '\n' metadata = OrderedDict() # make sure the gates instrument is in front all_md_keys = sorted(sorted(all_md), key=lambda x: x == 'gates', reverse=True) for x in all_md_keys: metadata[x] = OrderedDict() if 'IDN' in all_md[x]['parameters']: metadata[x]['IDN'] = dict({'name': 'IDN', 'value': all_md[ x]['parameters']['IDN']['value']}) metadata[x]['IDN']['unit'] = '' for y in sorted(all_md[x]['parameters'].keys()): try: if y != 'IDN': metadata[x][y] = OrderedDict() param_md = all_md[x]['parameters'][y] metadata[x][y]['name'] = y if isinstance(param_md['value'], (float, np.float64)): metadata[x][y]['value'] = float( format(param_md['value'], '.3f')) else: metadata[x][y]['value'] = str(param_md['value']) metadata[x][y]['unit'] = param_md.get('unit', None) metadata[x][y]['label'] = param_md.get('label', None) except KeyError as ex: if verbose: print('failed on parameter %s / %s: %s' % (x, y, str(ex))) if printformat == 'dict': ss = str(metadata).replace('(', '').replace( ')', '').replace('OrderedDict', '') else: # 'txt' or 'fancy' ss = '' for k in metadata: if verbose: print('--- %s' % k) s = metadata[k] ss += '\n## %s:\n' % k for p in s: pp = s[p] if verbose: print(' --- %s: %s' % (p, pp.get('value', '??'))) ss += '%s: %s (%s)' % (pp['name'], pp.get('value', '?'), pp.get('unit', '')) ss += '\n' if header is not None: ss = header + '\n\n' + ss return ss
# %%
[docs]def setupMeasurementWindows(*args, **kwargs): raise Exception('use qtt.gui.live_plotting.setupMeasurementWindows instead')
[docs]def updatePlotTitle(qplot, basetxt='Live plot'): """ Update the plot title of a QtPlot window.""" txt = basetxt + ' (%s)' % time.asctime() qplot.win.setWindowTitle(txt)
[docs]@rdeprecated(txt='Method will be removed in future release of qtt.', expire='1 Sep 2018') def timeProgress(data): """ Simple progress meter, should be integrated with either loop or data object.""" data.sync() tt = data.arrays['timestamp'] vv = ~np.isnan(tt) ttx = tt[vv] t0 = ttx[0] t1 = ttx[-1] logging.debug('t0 %f t1 %f' % (t0, t1)) fraction = ttx.size / tt.size[0] remaining = (t1 - t0) * (1 - fraction) / fraction return fraction, remaining
# %%
[docs]def flatten(lst): """ Flatten a list. Args: lst (list): list to be flattened. Returns: list: flattened list. Example: >>> flatten([ [1,2], [3,4], [10] ]) [1, 2, 3, 4, 10] """ return list(chain(*lst))
# %%
[docs]def cutoffFilter(x, thr, omega): """ Smooth cutoff filter. Filter definition from: http://paulbourke.net/miscellaneous/imagefilter/ Example ------- >>> plt.clf() >>> x=np.arange(0, 4, .01) >>> _=plt.plot(x, cutoffFilter(x, 2, .25), '-r') """ y = .5 * (1 - np.sin(np.pi * (x - thr) / (2 * omega))) y[x < thr - omega] = 1 y[x > thr + omega] = 0 return y
# %%
[docs]def smoothFourierFilter(fs=100, thr=6, omega=2, fig=None): """ Create smooth ND filter for Fourier high or low-pass filtering. Example ------- >>> F=smoothFourierFilter([24,24], thr=6, omega=2) >>> _=plt.figure(10); plt.clf(); _=plt.imshow(F, interpolation='nearest') """ rr = np.meshgrid(*[range(f) for f in fs]) x = np.dstack(rr) x = x - (np.array(fs) / 2 - .5) x = np.linalg.norm(x, axis=2) F = cutoffFilter(x, thr, omega) if fig is not None: plt.figure(10) plt.clf() plt.imshow(F, interpolation='nearest') return F
F = smoothFourierFilter([36, 36]) # %%
[docs]def fourierHighPass(imx, nc=40, omega=4, fs=1024, fig=None): """ Implement simple high pass filter using the Fourier transform.""" f = np.fft.fft2(imx, s=[fs, fs]) # do the fourier transform fx = np.fft.fftshift(f) if fig: plt.figure(fig) plt.clf() plt.imshow(np.log(np.abs(f) + 1), interpolation='nearest') plt.title('Fourier spectrum (real part)') plt.figure(fig + 1) plt.clf() plt.imshow(np.log(np.abs(fx) + 1), interpolation='nearest') plt.title('Fourier spectrum (real part)') if nc > 0 and omega == 0: f[0:nc, 0:nc] = 0 f[-nc:, -nc:] = 0 f[-nc:, 0:nc] = 0 f[0:nc, -nc:] = 0 img_back = np.fft.ifft2(f) # inverse fourier transform else: # smooth filtering F = 1 - smoothFourierFilter(fx.shape, thr=nc, omega=omega) fx = F * fx ff = np.fft.ifftshift(fx) # inverse shift img_back = np.fft.ifft2(ff) # inverse fourier transform imf = img_back.real imf = imf[0:imx.shape[0], 0:imx.shape[1]] return imf
# %%
[docs]def slopeClick(drawmode='r--', **kwargs): """ Calculate slope for line piece of two points clicked by user. Works with matplotlib but not with pyqtgraph. Uses the currently active figure. Args: drawmode (string): plotting style. Returns: coords (2 x 2 array): coordinates of the two clicked points. signedslope (float): slope of linepiece connecting the two points. """ ax = plt.gca() ax.set_autoscale_on(False) coords = qtt.pgeometry.ginput(2, drawmode, **kwargs) plt.pause(1e-6) signedslope = (coords[1, 0] - coords[1, 1]) / (coords[0, 0] - coords[0, 1]) return coords, signedslope
[docs]def clickGatevals(plot, drawmode='ro'): """ Get gate values for all gates at clicked point in a heatmap. Args: plot (qcodes MatPlot object): plot of measurement data. drawmode (string): plotting style. Returns: gatevals (dict): values of the gates at clicked point. """ # TODO: implement for virtual gates if not isinstance(plot, qcodes.plots.qcmatplotlib.MatPlot): raise Exception( 'The plot object is not based on the MatPlot class from qcodes.') ax = plt.gca() ax.set_autoscale_on(False) coords = qtt.pgeometry.ginput(drawmode=drawmode) data_array = plot.traces[0]['config']['z'] dataset = data_array.data_set gatevals = copy.deepcopy(dataset.metadata['allgatevalues']) if len(data_array.set_arrays) != 2: raise Exception('The DataArray does not have exactly two set_arrays.') for arr in data_array.set_arrays: if len(arr.ndarray.shape) == 1: gatevals[arr.name] = coords[1, 0] else: gatevals[arr.name] = coords[0, 0] return gatevals
# %%
[docs]def connect_slot(target): """ Create a slot by dropping signal arguments.""" def signal_drop_arguments(*args, **kwargs): target() return signal_drop_arguments