Source code for holypipette.gui.paramecium_droplet

'''
GUI for Paramecium electrophysiology
'''
from __future__ import absolute_import

from types import MethodType

from PyQt5 import QtCore, QtGui
from PyQt5.QtCore import Qt
import numpy as np

from holypipette.controller import TaskController
from holypipette.interface.paramecium_droplet import ParameciumDropletInterface
from holypipette.gui.manipulator import ManipulatorGui

# Helper functions for drawing
[docs]def create_painter(pixmap, color, width=1): """ Setup a ``QPainter`` with a `QPen` of a given color and width. Parameters ---------- pixmap : `.QPixMap` The pixmap on which to draw. color : tuple The 4-element tuple defining the color (R, G, B, alpha). width : int The width in pixels. Returns ------- painter : `~.QtGui.QPainter` The painter that can be used for drawing on the pixmap. """ painter = QtGui.QPainter(pixmap) pen = QtGui.QPen(QtGui.QColor(*color)) pen.setWidth(width) painter.setPen(pen) return painter
[docs]def draw_contour(contour, painter, scale): path = QtGui.QPainterPath(QtCore.QPoint(*contour[0][0]) / scale) for point in contour[1:]: path.lineTo(QtCore.QPoint(*point[0]) / scale) painter.drawPath(path)
[docs]def draw_ellipse(painter, x, y, width, height, angle, pixel_per_um, scale): width *= pixel_per_um / scale height *= pixel_per_um / scale rotate_by = angle * 180 / np.pi painter.translate(x / scale, y / scale) painter.rotate(rotate_by) painter.drawEllipse(-width / 2, -height / 2, width, height) painter.drawPoint(0, 0) painter.rotate(-rotate_by) painter.translate(-x / scale, -y / scale)
################################################# # Paramecium experiment with the droplet method # #################################################
[docs]class ParameciumDropletGui(ManipulatorGui): paramecium_command_signal = QtCore.pyqtSignal(MethodType, object) paramecium_reset_signal = QtCore.pyqtSignal(TaskController) def __init__(self, camera, pipette_interface): super(ParameciumDropletGui, self).__init__(camera, pipette_interface=pipette_interface, with_tracking=False) self.pipette_interface = pipette_interface self.paramecium_interface = ParameciumDropletInterface(pipette_interface, camera) self.image_edit_funcs.append(self.track_paramecium) self.display_edit_funcs.append(self.show_paramecium) self.display_edit_funcs.append(self.display_timer) # show_tip seems to interfere with movements #self.display_edit_funcs.append(self.show_tip) self.paramecium_position = (None, None, None, None, None) self.paramecium_interface.moveToThread(pipette_interface.thread()) self.interface_signals[self.paramecium_interface] = (self.paramecium_command_signal, self.paramecium_reset_signal) self.setWindowTitle("Paramecium droplet GUI") self.add_config_gui(self.paramecium_interface.config)
[docs] def register_commands(self): super(ParameciumDropletGui, self).register_commands(manipulator_keys = False) self.register_mouse_action(Qt.LeftButton, Qt.ShiftModifier, self.paramecium_interface.move_pipette_floor) self.register_mouse_action(Qt.LeftButton, Qt.ControlModifier, self.paramecium_interface.move_pipette_working_level) self.register_mouse_action(Qt.RightButton, Qt.ShiftModifier, self.paramecium_interface.start_tracking) self.register_mouse_action(Qt.RightButton, Qt.ControlModifier, self.paramecium_interface.autofocus) self.register_key_action(Qt.Key_Space, None, self.paramecium_interface.move_pipette_down) self.register_key_action(Qt.Key_T, None, self.paramecium_interface.toggle_tracking) self.register_key_action(Qt.Key_Return, None, self.paramecium_interface.toggle_following) self.register_key_action(Qt.Key_P, None, self.paramecium_interface.display_z_manipulator) self.register_key_action(Qt.Key_K, None, self.paramecium_interface.automatic_experiment) self.register_key_action(Qt.Key_N, None, self.paramecium_interface.detect_contact) self.register_key_action(Qt.Key_U, None, self.paramecium_interface.focus) self.register_key_action(Qt.Key_B, None, self.paramecium_interface.autofocus_paramecium) self.register_key_action(Qt.Key_V, None, self.paramecium_interface.move_pipettes_paramecium) self.register_key_action(Qt.Key_0, None, self.pipette_interface.reset_timer)
[docs] def track_paramecium(self, frame): self.paramecium_interface.track_paramecium(frame) return frame
[docs] def show_paramecium(self, pixmap): interface = self.paramecium_interface if (not interface.tracking or any(p is None for p in interface.paramecium_position)): return scale = 1.0 * self.camera.width / pixmap.size().width() pixel_per_um = getattr(self.camera, 'pixel_per_um', None) if pixel_per_um is None: pixel_per_um = interface.calibrated_unit.stage.pixel_per_um()[0] # print('pixel_per_um', pixel_per_um, 'scale', scale) painter = create_painter(pixmap, color=(0, 0, 200, 125), width=3) x, y, width, height, angle = interface.paramecium_position draw_ellipse(painter, x, y, width, height, angle, pixel_per_um, scale) painter.end() if interface.config.draw_fitted_ellipses: painter = create_painter(pixmap, color=(0, 200, 200, 125), width=2) for ellipse in interface.paramecium_info['all_ellipses']: x, y, width, height, angle = ellipse draw_ellipse(painter, x, y, width, height, angle, pixel_per_um, scale) painter.end() painter = create_painter(pixmap, color=(200, 0, 200, 125), width=2) for ellipse in interface.paramecium_info['good_ellipses']: x, y, width, height, angle = ellipse draw_ellipse(painter, x, y, width, height, angle, pixel_per_um, scale) painter.end() if interface.config.draw_contours: # Draw all contours painter = create_painter(pixmap, color=(200, 200, 0, 125)) for contour in interface.paramecium_info['all_contours']: draw_contour(contour, painter, scale) painter.end() # Draw unused contours that were long enough/had enough points painter = create_painter(pixmap, color=(200, 0, 0, 125)) for contour in interface.paramecium_info['valid_contours']: draw_contour(contour, painter, scale) painter.end() # Draw best contour contour = interface.paramecium_info['best_contour'] if contour is not None: painter = create_painter(pixmap, color=(0, 200, 0, 125), width=2) draw_contour(contour, painter, scale) painter.end()