Commit 8255ecb6 authored by Martin Drechsler's avatar Martin Drechsler

Merge remote-tracking branch 'control_app/master'

parents 5fe7d72a 40d9ccf5
# -*- coding: utf-8 -*-
"""
Created on Thu Oct 25 15:35:49 2018
@author: Usuario
For now, this will work with one measruement computing daq. It is expected to be the USB-3105 and
the board number will be 0.
If the daq is not found, whenever trying to send signals, the command will be printed in the console.
"""
import warnings
try:
from mcculw import ul
from mcculw.enums import InterfaceType, ULRange, InfoType, BoardInfo, DigitalIODirection
from mcculw.ul import ULError
from props.digital import DigitalProps
from props.ao import AnalogOutputProps
except:
warnings.warn('Problem importing ul from mcculw, Thus might happen in Linux')
import uldaq as ul
from uldaq import InterfaceType
from uldaq import Range as ULRange
try:
ul.ignore_instacal() #instacal will be ignored
except NameError:
print('Instacall not ignored because mcculw not imported')
except AttributeError:
print('Instacall not ignored because you imported uldaq and there is no instacal in that case')
# First of all, I check which daqs are available and print it.
devices = ul.get_daq_device_inventory(interface_type=InterfaceType.USB)
if len(devices) == 1 :
device = devices[0]
print('daq %s is connected' % device.product_name)
else:
warnings.warn('No daq connected')
def discover_device(board_num, board_name):
"""
Checks if daq is found.
"""
devices = ul.get_daq_device_inventory(InterfaceType.USB) #get device inventory
device = next((device for device in devices
if board_name in device.product_name), None)
if device != None: #if it is found,
try:
ul.create_daq_device(board_num, device) #create an instance
except ULError:
ul.release_daq_device(board_num)
ul.create_daq_device(board_num, device)
warnings.warn('The board number %i was already in use, and it was released and redefined ' % board_num);
finally:
return True
return False
daqfound = discover_device(0, 'USB-3105')
class daq_AO(object):
def __init__(self, out_num, board_num = 0, output_range = ULRange.BIP10VOLTS):
global daqfound
self.out_num = out_num
self.board_num = board_num
self.output_range = output_range
if daqfound:
ul.set_config(InfoType.BOARDINFO, self.board_num, self.out_num, BoardInfo.DACRANGE, self.output_range)
self.ao_props = AnalogOutputProps(self.board_num)
#methods
if daqfound:
def set_out(self, value):
try:
ul.v_out(self.board_num, self.out_num, self.output_range, value)
except ULError as e:
self.show_ul_error(e)
else:
def set_out(self, value):
print('Analog out %i set to %f' % (self.out_num, value) )
return value, self.out_num
class daq_DO(object):
def __init__(self, out_num):
global daqfound
self.board_num = 0
self.out_num = out_num
if daqfound:
self.digital_props = DigitalProps(self.board_num)
# Find the first port that supports output, defaulting to None
# if one is not found.
self.port = next(
(port for port in self.digital_props.port_info
if port.supports_output), None)
# If the port is configurable, configure it for output
if self.port != None and self.port.is_port_configurable:
try:
ul.d_config_port(
self.board_num, self.port.type, DigitalIODirection.OUT)
except ULError as e:
self.show_ul_error(e)
if daqfound:
def set_out(self, bit_value):
try:
# Output the value to the board
ul.d_bit_out(self.board_num, self.port.type, self.out_num, bit_value)
except ULError as e:
self.show_ul_error(e)
else:
def set_out(self, bit_value):
print('Digital out %i set to %s' % (self.out_num, bit_value) )
return bit_value, self.out_num
This diff is collapsed.
# -*- coding: utf-8 -*-
"""
Here the class of control signals are created. There are two types: analog and digital.
"""
import numpy as np
from ADoutputs import daq_AO, daq_DO
from PyQt5 import QtCore
from subclasses.controllayouts import LaserControlLayout
from subclasses.controllayouts import LaserScanLayout
AO_TYPES = ['cavity_piezo', 'electrode', 'laser_piezo']
class anal_control_signal(QtCore.QObject):
_registry = []
scanStepSignal = QtCore.pyqtSignal(float)
scanEndSignal = QtCore.pyqtSignal(float)
def __init__(self, name, channel, ao_type, out_minimum = -10, out_maximum = 10, out_step = 0.1, feedfoward = False):
super().__init__()
self._registry.append(self)
if ao_type not in AO_TYPES:
raise ValueError("Invalid ao type. Expected one of: %s" % AO_TYPES)
self.name = name
self.ch = channel
self.ao_type = ao_type
self.AO = daq_AO(self.ch)
self.is_feedwoward_enabled = feedfoward
self.initial_value = 0
self.max = out_maximum
self.min = out_minimum
self.step = out_step
self.siPrefix = True
self.suffix = 'V'
self.scanTimer = QtCore.QTimer()
self.current_scan_step = 0
self.scan_array_gen = None
def make_connections(self, frontend):
for layout in LaserControlLayout._registry:
if self.name in layout.spinboxes:
layout.spinboxes[self.name].setMinimum(self.min)
layout.spinboxes[self.name].setMaximum(self.max)
layout.spinboxes[self.name].setValue(self.initial_value)
layout.spinboxes[self.name].sigValueChanging.connect(self.sb_change)
scan_layouts = [l for l in LaserScanLayout._registry if l.name == self.name]
for scan_layout in scan_layouts:
scan_layout.spinboxes['start'].setMinimum(self.min)
scan_layout.spinboxes['start'].setMaximum(self.max)
scan_layout.spinboxes['stop'].setMinimum(self.min)
scan_layout.spinboxes['stop'].setMaximum(self.max)
def name(self):
return self.name
def scan_event(self):
self.current_scan_step = next(self.scan_array_gen)
self.AO.set_out(self.current_scan_step)
self.scanStepSignal.emit(self.current_scan_step)
@QtCore.pyqtSlot(object)
def scan_action(self, scan_array_gen):
print('action')
if self.scanTimer.isActive():
print('stop timer')
self.scanTimer.stop()
self.scanEndSignal.emit(self.current_scan_step)
else:
print('start timer')
self.scan_array_gen = scan_array_gen
self.scanTimer.start(5.0)
@QtCore.pyqtSlot(object, object)
def sb_change(self, sb, value):
self.AO.set_out(value)
class digital_control_signal(QtCore.QObject):
_registry = []
def __init__(self, name, channel, inverse=False):
super().__init__()
self._registry.append(self)
self.name = name
self.ch = channel
self.initial_value = 0
self.DO = daq_DO(self.ch)
def make_connections(self, frontend):
for layout in LaserControlLayout._registry:
if self.name in layout.checkboxes:
layout.checkboxes[self.name].stateChanged.connect(self.when_change_digital)
@QtCore.pyqtSlot(int)
def when_change_digital(self, state):
if state == QtCore.Qt.Checked:
self.DO.set_out(True)
else:
self.DO.set_out(False)
\ No newline at end of file
# -*- coding: utf-8 -*-
"""
This is the gui where the spinboxes and checkboxes for controlling the mc daq live. Nevertheless, measurements are not
controlled from this Gui. It is more for searching and exploring the paremeters.
@author: Usuario
"""
import sys
from PyQt5 import QtGui, QtCore
import qdarkstyle
import os
os.environ['PYQTGRAPH_QT_LIB'] = 'PyQt5'
from subclasses.controllayouts import LaserControlLayout
from subclasses.controllayouts import LaserScanLayout
from pyqtgraph import dockarea
from MCDAQcontrolsignals import anal_control_signal
from MCDAQcontrolsignals import digital_control_signal
class ControlGui(QtGui.QFrame):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.area = dockarea.DockArea()
self.grid = QtGui.QGridLayout()
self.grid.addWidget(self.area, 0, 0)
self.setLayout(self.grid)
self.dock_397 = dockarea.Dock('397', (100, 100))
self.dock_866 = dockarea.Dock('866', (100, 100))
self.dock_423 = dockarea.Dock('423', (100, 100))
self.dock_397_scan = dockarea.Dock('397 scan', (100, 100))
self.dock_866_scan = dockarea.Dock('866 scan', (100, 100))
self.dock_423_scan = dockarea.Dock('423 scan', (100, 100))
self.dock_electrodes = dockarea.Dock('dcElectrodes', (100, 100))
self.area.addDock(self.dock_electrodes)
self.area.addDock(self.dock_397, 'bottom', self.dock_electrodes)
self.area.addDock(self.dock_866, 'bottom', self.dock_397)
self.area.addDock(self.dock_423, 'bottom', self.dock_866)
self.area.addDock(self.dock_397_scan, 'right', self.dock_397)
self.area.addDock(self.dock_866_scan, 'right', self.dock_866)
self.area.addDock(self.dock_423_scan, 'right', self.dock_423)
self.laser397_control_layout = LaserControlLayout('397', checkboxes=['shutter397'], spinboxes = ['piezoA397', 'piezoB397'])
self.laser397_scan_layout = LaserScanLayout(name='piezoA397', signal_to_scan_layout=self.laser397_control_layout)
self.dock_397.addWidget(self.laser397_control_layout, 0, 0)
self.dock_397_scan.addWidget(self.laser397_scan_layout, 0, 1)
self.laser866_control_layout = LaserControlLayout('866', checkboxes=['shutter866', 'shuter866WM'], spinboxes = ['piezoA866', 'piezoB866'])
self.laser866_scan_layout = LaserScanLayout(name='piezoA866', signal_to_scan_layout=self.laser866_control_layout)
self.dock_866.addWidget(self.laser866_control_layout)
self.dock_866_scan.addWidget(self.laser866_scan_layout)
self.laser423_control_layout = LaserControlLayout('423', checkboxes=['shutter423'], spinboxes = ['piezo423'])
self.laser423_scan_layout = LaserScanLayout(name='piezo423', signal_to_scan_layout=self.laser423_control_layout)
self.dock_423.addWidget(self.laser423_control_layout)
self.dock_423_scan.addWidget(self.laser423_scan_layout)
self.electrodes_control_layout = LaserControlLayout('electrodes', checkboxes=[], spinboxes = ['dcA', 'dcB', 'compC', 'compD', 'compOven', 'compExYb'], custom_geometry=True)
self.dock_electrodes.addWidget(self.electrodes_control_layout)
self.electrodes_control_layout.addWidget(QtGui.QLabel('dcA'), 0, 0)
self.electrodes_control_layout.addWidget(self.electrodes_control_layout.spinboxes['dcA'], 0, 1)
self.electrodes_control_layout.addWidget(QtGui.QLabel('dcB'), 1, 0)
self.electrodes_control_layout.addWidget(self.electrodes_control_layout.spinboxes['dcB'], 1, 1)
self.electrodes_control_layout.addWidget(QtGui.QLabel('compC'), 0, 2)
self.electrodes_control_layout.addWidget(self.electrodes_control_layout.spinboxes['compC'], 0, 3)
self.electrodes_control_layout.addWidget(QtGui.QLabel('compD'), 1, 2)
self.electrodes_control_layout.addWidget(self.electrodes_control_layout.spinboxes['compD'], 1, 3)
self.electrodes_control_layout.addWidget(QtGui.QLabel('compOven'), 0, 4)
self.electrodes_control_layout.addWidget(self.electrodes_control_layout.spinboxes['compOven'], 0, 5)
self.electrodes_control_layout.addWidget(QtGui.QLabel('compExYb'), 1, 4)
self.electrodes_control_layout.addWidget(self.electrodes_control_layout.spinboxes['compExYb'], 1, 5)
self.show()
def make_connections(self, backend):
# external connections
scan_layout_list = [scan_layout for scan_layout in LaserScanLayout._registry if scan_layout.name == backend.name]
for scan_layout in scan_layout_list:
scan_layout.scanActionSignal.connect(backend.scan_action)
backend.scanStepSignal.connect(scan_layout.scan_step)
backend.scanEndSignal.connect(scan_layout.scan_end)
if __name__ == '__main__':
app = QtGui.QApplication([])
controlGui = ControlGui()
piezoA397 = anal_control_signal('piezoA397', channel=0, ao_type='cavity_piezo', out_minimum = 0, out_maximum = 4)
piezoB397 = anal_control_signal('piezoB397', channel=1, ao_type='cavity_piezo')
piezoA866 = anal_control_signal('piezoA866', channel=2, ao_type='cavity_piezo', out_minimum = 0, out_maximum = 4)
piezoB866 = anal_control_signal('piezoB866', channel=3, ao_type='cavity_piezo')
piezo423 = anal_control_signal('piezo423', channel=4, ao_type='laser_piezo')
trapDCA = anal_control_signal('dcA', channel = 5, ao_type='electrode')
trapDCB = anal_control_signal('dcB', channel = 6, ao_type='electrode')
trapCOMPC = anal_control_signal('compC', channel = 7, ao_type='electrode')
trapCOMPD = anal_control_signal('compD', channel = 8, ao_type='electrode')
trapOven = anal_control_signal('compOven', channel = 9, ao_type='electrode')
trapExYb = anal_control_signal('CompExYb', channel = 10, ao_type='electrode')
shutter866 = digital_control_signal('shutter866', channel = 0)
shutter397 = digital_control_signal('shutter397', channel = 1)
shutter423 = digital_control_signal('shutter423', channel = 2)
shutter866WM = digital_control_signal('shutter866WM', channel = 3)
mcDAQthread = QtCore.QThread()
for ao in anal_control_signal._registry:
controlGui.make_connections(ao)
ao.make_connections(controlGui)
ao.moveToThread(mcDAQthread)
ao.scanTimer.moveToThread(mcDAQthread)
ao.scanTimer.timeout.connect(ao.scan_event)
for do in digital_control_signal._registry:
do.make_connections(controlGui)
do.moveToThread(mcDAQthread)
mcDAQthread.start()
sys.exit(app.exec_())
\ No newline at end of file
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Mon Oct 22 11:46:00 2018
@author: martindrech
"""
from PyQt5.QtWidgets import QMessageBox
def show_warning(text, details_text = ''):
msg = QMessageBox()
msg.setIcon(QMessageBox.Warning)
msg.setText(text)
msg.setWindowTitle("Oh dear")
if details_text:
msg.setDetailedText(details_text)
msg.setStandardButtons(QMessageBox.Ok)
msg.buttonClicked.connect(msgbtn)
retval = msg.exec_()
#print("value of pressed message box button:", retval)
def msgbtn(i):
print("Button pressed is:",i.text())
\ No newline at end of file
This diff is collapsed.
# -*- coding: utf-8 -*-
"""
This example demonstrates the use of pyqtgraph's parametertree system. This provides
a simple way to generate user interfaces that control sets of parameters. The example
demonstrates a variety of different parameter types (int, float, list, etc.)
as well as some customized parameter types
"""
import pyqtgraph as pg
from pyqtgraph.Qt import QtCore, QtGui
app = QtGui.QApplication([])
from pyqtgraph.parametertree import Parameter, ParameterTree, registerParameterType
from ADoutputs import daq_AO, daq_DO
params = [
{'name': 'laser_parameters_397', 'type': 'group', 'title': '397 laser parameters', 'children': [
{'name': 'piezoA', 'type': 'float', 'value': 0, 'step': 0.2, 'limits': (-10, 10), 'siPrefix': True, 'suffix': 'V'},
{'name': 'piezoB', 'type': 'float', 'value': 0, 'step': 0.05, 'limits': (-1, 1), 'siPrefix': True, 'suffix': 'V'},
{'name': 'shutter1', 'type': 'bool', 'value': True, 'tip': "This is a checkbox"},
{'name': 'shutter2', 'type': 'bool', 'value': True, 'tip': "This is a checkbox"},
]},
{'name': '866 laser parameters', 'type': 'group', 'children': [
{'name': 'piezoA', 'type': 'float', 'value': 0, 'step': 0.05, 'limits': (-1, 1), 'siPrefix': True, 'suffix': 'V'},
{'name': 'piezoB', 'type': 'float', 'value': 0, 'step': 0.05, 'limits': (-1, 1), 'siPrefix': True, 'suffix': 'V'},
{'name': 'shutter1', 'type': 'bool', 'value': True, 'tip': "This is a checkbox"},
{'name': 'shutter2', 'type': 'bool', 'value': True, 'tip': "This is a checkbox"},
]},
{'name': '423 laser parameters', 'type': 'group', 'children': [
{'name': 'piezo', 'type': 'float', 'value': 0, 'step': 0.05, 'limits': (-1, 1), 'siPrefix': True, 'suffix': 'V'},
{'name': 'shutter1', 'type': 'bool', 'value': True, 'tip': "This is a checkbox"},
]},
{'name': 'Save/Restore functionality', 'type': 'group', 'children': [
{'name': 'Save State', 'type': 'action'},
{'name': 'Restore State', 'type': 'action', 'children': [
{'name': 'Add missing items', 'type': 'bool', 'value': True},
{'name': 'Remove extra items', 'type': 'bool', 'value': True},
]},
]},
]
## Create tree of Parameter objects
p = Parameter.create(name='params', type='group', children=params)
"""
## If anything changes in the tree, print a message
def change(param, changes):
print("tree changes:")
for param, change, data in changes:
path = p.childPath(param)
if path is not None:
childName = '_'.join(path)
else:
childName = param.name()
#print(' parameter: %s'% childName)
#print(' change: %s'% change)
#print(' data: %s'% str(data))
#print(path)
myDict[childName].set_analog_out(data)
#print(' ----------')
p.sigTreeStateChanged.connect(change)
def valueChanging(param, value):
print("Value changing (not finalized): %s %s" % (param, value))
"""
def valueChanging(box):
#print(box.name())
#print(box.value())
laser_parameters_397_piezoA.set_analog_out(box.value())
p.children()[0].children()[0].sigValueChanging.connect(valueChanging)
laser_parameters_397_piezoA = daq_AO(0)
myDict = {'laser_parameters_397_piezoA': laser_parameters_397_piezoA}
#def whenChange(sb):
# analog0.set_analog_out(sb.value())
#spin.sigValueChanging.connect(whenChange)
def save():
global state
state = p.saveState()
def restore():
global state
add = p['Save/Restore functionality', 'Restore State', 'Add missing items']
rem = p['Save/Restore functionality', 'Restore State', 'Remove extra items']
p.restoreState(state, addChildren=add, removeChildren=rem)
p.param('Save/Restore functionality', 'Save State').sigActivated.connect(save)
p.param('Save/Restore functionality', 'Restore State').sigActivated.connect(restore)
## Create ParameterTree widgets
t = ParameterTree()
t.setParameters(p, showTop=False)
t.setWindowTitle('pyqtgraph example: Parameter Tree')
win = QtGui.QWidget()
layout = QtGui.QGridLayout()
win.setLayout(layout)
layout.addWidget(QtGui.QLabel("These are two views of the same data. They should always display the same values."), 0, 0, 1, 2)
layout.addWidget(t, 1, 0, 1, 1)
win.show()
win.resize(800,800)
## test save/restore
s = p.saveState()
p.restoreState(s)
## Start Qt event loop unless running in interactive mode or using pyside.
if __name__ == '__main__':
import sys
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()
# -*- coding: utf-8 -*-
"""
Created on Thu Jul 5 12:17:30 2018
@author: Usuario
"""
from PyQt5.QtCore import QSettings
from PyQt5.QtWidgets import QAction, QFileDialog, QInputDialog, QWidget
from threading import Thread
#from webcamROI import roiWindow
#%%
class myToolbarMenu(QWidget):
"""
This will create a toolbar menu
"""
def __init__(self, win, analog_control_signals):
super().__init__()
self.MainGuiWindow = win
self.aos = analog_control_signals
self.first_load(self.aos)
self.incorporate_toolbar(self.MainGuiWindow, self.aos)
self.createAosDict()
def createAosDict(self):
analogs = self.aos
self.aosDicc = {}
for i in range(len(analogs)):
self.aosDicc[analogs[i].name] = analogs[i]
def incorporate_toolbar(self, win, analog_control_signals):
menubar = win.menuBar()
# saveAct = QAction('Save', win)
loadAct = QAction('Load', win)
saveAsAct = QAction('Save as' , win)
openFromAct = QAction('open from', win)
loadElectrodesAct = QAction('Load electrodes', win)
win.statusBar()
menubar = win.menuBar()
fileMenu = menubar.addMenu('&File')
#fileMenu.addAction(saveAct)
fileMenu.addAction(loadAct)
fileMenu.addAction(saveAsAct)
fileMenu.addAction(openFromAct)
fileMenu.addAction(loadElectrodesAct)
viewMenu = menubar.addMenu('Monitor')
viewWebcams = QAction('View webcams', win, checkable=True)
viewWebcams.setChecked(False)
viewMenu.addAction(viewWebcams)
roiMenu = menubar.addMenu('Roi')
plotRoi = QAction('Open roi plot', win)
roiMenu.addAction(plotRoi)
#☺saveAct.triggered.connect(lambda: self.save(analog_control_signals))
loadAct.triggered.connect(lambda: self.load(analog_control_signals))
loadElectrodesAct.triggered.connect(lambda: self.open_from([ao for ao in analog_control_signals if ao.ao_type == 'electrode'], win))
saveAsAct.triggered.connect(lambda: self.save_as(analog_control_signals, win))
openFromAct.triggered.connect(lambda: self.open_from(analog_control_signals, win))
viewWebcams.toggled.connect(lambda: self.putWebcams(viewWebcams.isChecked()) )
plotRoi.triggered.connect(self.openRoi)
def save(self, aos):
settings = QSettings('C:\\Users\\Usuario\\Documents\\control_app\\config.ini', QSettings.IniFormat)
settings.beginGroup('Ventana')
for ao in aos:
spin_boxes = ao.spin_boxes
for text, spinb in spin_boxes:
settings.setValue(str(ao.name) + '_' + text, spinb.value())
#print(spinb.value() )
settings.endGroup()
#print('saving')
def load(self, aos):
settings = QSettings('C:\\Users\\Usuario\\Documents\\control_app\\config.ini', QSettings.IniFormat)
settings.beginGroup('Ventana')
for ao in aos:
spin_boxes = ao.spin_boxes
for text, spinb in spin_boxes:
spin = settings.value(str(ao.name) + '_' + text)
try:
spinb.setValue( float(spin) )
except TypeError:
spinb.setValue( float(ao.initial_value) )
settings.endGroup()
def first_load(self, aos):
settings = QSettings('C:\\Users\\Usuario\\Documents\\control_app\\config.ini', QSettings.IniFormat)
if not settings:
print('Warning: no config.ini file in control_app folder. Pleace, be kindful and create one')
return 0
settings.beginGroup('Ventana')
for ao in aos:
spin_boxes = ao.spin_boxes
for text, spinb in spin_boxes:
spin = settings.value(str(ao.name) + '_' + text)
spinb.setValue( float(spin) )
settings.endGroup()
def save_as(self, aos, window):
name = QFileDialog.getSaveFileName(window, 'Save File')
if not name[0]:
print('Saving was canceled')
return 0
file = open(name[0], 'w+')
file.close()
settings = QSettings(name[0], QSettings.IniFormat)
settings.beginGroup('Ventana')
for ao in aos:
spin_boxes = ao.spin_boxes
for text, spinb in spin_boxes:
settings.setValue(str(ao.name) + '_' + text, spinb.value())
#print(spinb.value() )
settings.endGroup()
print('saving to %s' % name[0])
def open_from(self, aos, window):
name = QFileDialog.getOpenFileName(window, 'Save File')
if not name[0]:
print('Opening was canceled')
return 0
settings = QSettings(name[0], QSettings.IniFormat)
settings.beginGroup('Ventana')
for ao in aos:
spin_boxes = ao.spin_boxes
for text, spinb in spin_boxes:
spin = settings.value(str(ao.name) + '_' + text)
spinb.setValue( float(spin) )
settings.endGroup()
def startWebcamsThread(self):
exec(open('C:\\Users\\Usuario\\Documents\\control_app\\webcams.py').read())
def putWebcams(self, checked):
if checked:
t_webcams = Thread(target=self.startWebcamsThread, args=())
t_webcams.start()
else:
pass
def openRoi(self):
print('opening roi window')
items = self.aosDicc.keys()
item, okPressed = QInputDialog.getItem(self, "Get item","Color:", items, 0, False)
if okPressed and item:
ao = self.aosDicc[item]
self.roiW = roiWindow(ao)
self.roiW.w.show()
else:
print('Roi canceled')
def startRoiThread(self, ao):
self.roiW = roiWindow(ao)
self.roiW.w.show()
def getChoiceForRoi(self):
items = self.aosDicc.keys()
item, okPressed = QInputDialog.getItem(self, "Get item","Color:", items, 0, False)
if okPressed and item:
self.startRoiThread(item)
return self.aosDicc[item]
\ No newline at end of file
This diff is collapsed.
from __future__ import absolute_import, division, print_function
from builtins import * # @UnusedWildImport
from mcculw import ul
from mcculw.enums import BoardInfo, InfoType, ULRange, ErrorCode, ScanOptions
from mcculw.ul import ULError
from props.propsbase import Props
class AnalogOutputProps(Props):
"""Provides analog output information on the hardware configured at the
board number given.
This class is used to provide hardware information for the library
examples, and may change hardware values. It is recommended that the
values provided by this class be hard-coded in production code.
"""
def __init__(self, board_num):
self._board_num = board_num
self.resolution = self._get_resolution()
self.num_chans = self._get_num_chans()
self.available_ranges = self._get_available_ranges()
self.supports_v_out = self._get_supports_v_out(
self.available_ranges)
self.supported_scan_options = self._get_supported_scan_options()
self.supports_scan = (
ScanOptions.CONTINUOUS in self.supported_scan_options)
def _get_resolution(self):
return ul.get_config(
InfoType.BOARDINFO, self._board_num, 0, BoardInfo.DACRES)
def _get_num_chans(self):
return ul.get_config(
InfoType.BOARDINFO, self._board_num, 0, BoardInfo.NUMDACHANS)
def _get_supported_scan_options(self):
try:
return ScanOptions(ul.get_config(
InfoType.BOARDINFO, self._board_num, 0,
BoardInfo.DACSCANOPTIONS))
except ULError:
return ScanOptions(0)
def _get_available_ranges(self):
result = []
# Check if the range is ignored by passing a bogus range in
try:
ul.v_out(self._board_num, 0, -5, 0)
range_ignored = True
except ULError as e:
if (e.errorcode == ErrorCode.NETDEVINUSE or
e.errorcode == ErrorCode.NETDEVINUSEBYANOTHERPROC):
raise
range_ignored = False
if range_ignored:
# Try and get the range configured in InstaCal
try:
curr_range = ULRange(ul.get_config(
InfoType.BOARDINFO, self._board_num, 0,
BoardInfo.DACRANGE))
result.append(curr_range)
except ULError as e:
if (e.errorcode == ErrorCode.NETDEVINUSE or
e.errorcode == ErrorCode.NETDEVINUSEBYANOTHERPROC):
raise
return result
for dac_range in ULRange:
try:
ul.v_out(self._board_num, 0, dac_range, 0)
result.append(dac_range)
print(dac_range)
except ULError as e:
if (e.errorcode == ErrorCode.NETDEVINUSE or
e.errorcode == ErrorCode.NETDEVINUSEBYANOTHERPROC):
raise
return result
def get_units_string(self, ao_range):
if ao_range in [
ULRange.MA4TO20,
ULRange.MA2TO10,
ULRange.MA1TO5,
ULRange.MAPT5TO2PT5]:
return "mA"
else:
return "V"
def _get_supports_v_out(self, available_ranges):
if len(available_ranges) == 0:
return False
try:
ul.v_out(self._board_num, 0, available_ranges[0], 0)
except ULError:
return False
return True
from __future__ import absolute_import, division, print_function
from builtins import * # @UnusedWildImport
from mcculw import ul
from mcculw.enums import InfoType, BoardInfo, DigitalInfo, DigitalPortType, \
DigitalIODirection, FunctionType
from props.propsbase import Props
from mcculw.ul import ULError
class DigitalProps(Props):
"""Provides digital IO information on the hardware configured at the
board number given.
This class is used to provide hardware information for the library
examples, and may change hardware values. It is recommended that the
values provided by this class be hard-coded in production code.
"""
def __init__(self, board_num):
self._board_num = board_num
self.num_ports = self._get_num_digital_chans()
self.port_info = []
for port_index in range(self.num_ports):
self.port_info.append(PortInfo(board_num, port_index))
def _get_num_digital_chans(self):
try:
return ul.get_config(
InfoType.BOARDINFO, self._board_num, 0,
BoardInfo.DINUMDEVS)
except ULError:
return 0
class PortInfo(object):
def __init__(self, board_num, port_index):
self._board_num = board_num
self._port_index = port_index
self.type = self._get_digital_dev_type()
self.first_bit = self._get_first_bit(port_index, self.type)
self.num_bits = self._get_num_bits()
self.in_mask = self._get_in_mask()
self.out_mask = self._get_out_mask()
self.is_bit_configurable = self._get_is_bit_configurable(
self.type, self.first_bit, self.in_mask, self.out_mask)
self.is_port_configurable = self._get_is_port_configurable(
self.type, self.in_mask, self.out_mask)
self.supports_input = self._get_supports_input(
self.in_mask, self.is_port_configurable)
self.supports_input_scan = self._get_supports_input_scan()
self.supports_output = self._get_supports_output(
self.out_mask, self.is_port_configurable)
self.supports_output_scan = self._get_supports_output_scan()
def _get_num_bits(self):
return ul.get_config(
InfoType.DIGITALINFO, self._board_num, self._port_index,
DigitalInfo.NUMBITS)
def _get_supports_input(self, in_mask, is_port_programmable):
return in_mask > 0 or is_port_programmable
def _get_supports_input_scan(self):
try:
ul.get_status(self._board_num, FunctionType.DIFUNCTION)
except ULError:
return False
return True
def _get_supports_output_scan(self):
try:
ul.get_status(self._board_num, FunctionType.DOFUNCTION)
except ULError:
return False
return True
def _get_supports_output(self, out_mask, is_port_programmable):
return out_mask > 0 or is_port_programmable
def _get_first_bit(self, port_index, port_type):
# A few devices (USB-SSR08 for example) start at FIRSTPORTCL and
# number the bits as if FIRSTPORTA and FIRSTPORTB exist for
# compatibility with older digital peripherals
if port_index == 0 and port_type == DigitalPortType.FIRSTPORTCL:
return 16
return 0
def _get_is_bit_configurable(self, port_type, first_bit, in_mask,
out_mask):
if in_mask & out_mask > 0:
return False
# AUXPORT type ports might be configurable, check if d_config_bit
# completes without error
if port_type == DigitalPortType.AUXPORT:
try:
ul.d_config_bit(
self._board_num, port_type, first_bit,
DigitalIODirection.OUT)
ul.d_config_bit(
self._board_num, port_type, first_bit,
DigitalIODirection.IN)
except ULError:
return False
return True
return False
def _get_is_port_configurable(self, port_type, in_mask, out_mask):
if in_mask & out_mask > 0:
return False
# Check if d_config_port completes without error
try:
ul.d_config_port(self._board_num, port_type,
DigitalIODirection.OUT)
ul.d_config_port(self._board_num, port_type,
DigitalIODirection.IN)
except ULError:
return False
return True
def _get_digital_dev_type(self):
return DigitalPortType(ul.get_config(
InfoType.DIGITALINFO, self._board_num, self._port_index,
DigitalInfo.DEVTYPE))
def _get_in_mask(self):
return ul.get_config(
InfoType.DIGITALINFO, self._board_num, self._port_index,
DigitalInfo.INMASK)
def _get_out_mask(self):
return ul.get_config(
InfoType.DIGITALINFO, self._board_num, self._port_index,
DigitalInfo.OUTMASK)
from __future__ import absolute_import, division, print_function
from builtins import * # @UnusedWildImport
from mcculw import ul
class Props(object):
"""The base class for classes that provide hardware information for the
library examples. Subclasses of this class may change hardware values.
It is recommended that the values provided by these classes be
hard-coded in production code.
"""
def __init__(self, params): pass
def get_config_array(self, info_type, board_num, count_item, value_item,
wrapper_type=None):
result = []
count = ul.get_config(info_type, board_num, 0, count_item)
for item_num in range(count):
config_value = ul.get_config(
info_type, board_num, item_num, value_item)
if wrapper_type == None:
result.append(config_value)
else:
result.append(wrapper_type(config_value))
return result
# Control App
## Getting Started
These instructions will get you a copy of the project up and running on your local machine for development and testing purposes. See deployment for notes on how to deploy the project on a live system.
## Requisites
In windows, the mcculw package is needed. You can install it from https://pypi.org/project/mcculw/, where you need to follow some simple instrucions. After following those, open the installed
program Instalcal at least once for getting some neccesary updates.
In Linux, you will need the mccdaq package. You can install it from https://pypi.org/project/uldaq/. There you should go to the github repository and follow the build instructions.
Also, check requisites.txt, and install the python packages listed there. This can be usually done via PIP.
\ No newline at end of file
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Fri Oct 26 11:24:28 2018
@author: martindrech
Auxiliary functions for performing scans in a cleaner way
"""
import numpy as np
def create_scan_array(scan_start, scan_stop, scan_period, first_value, dt, normalized = False, normalization_constant = 0):
"""
Returns the array that represents one period of the scan, including up and down paths of the triangle.
The first_value param is were the scan actually starts. The scan_start and scan_top give the range.
If normalized is true, then it returns the array from 1 to -1 (including up and down paths) multiplied by
the value normalization_constant
"""
scan_start = float(scan_start)
scan_stop = float(scan_stop)
scan_period = float(scan_period)
number_of_points = 1 + (scan_period+dt)//(2*dt)
scan_array_norm = np.linspace(1* normalization_constant, -1*normalization_constant, int(number_of_points) )
scan_array_norm = np.concatenate((scan_array_norm, np.flip(scan_array_norm, 0)[1:]))
scan_array = np.linspace(scan_start, scan_stop, int(number_of_points))
scan_array = np.concatenate((scan_array, np.flip(scan_array, 0)[1:]))
starting_index = np.abs(scan_array-first_value).argmin()+1
scan_array = np.roll(scan_array, starting_index)
scan_array_norm = np.roll(scan_array_norm, starting_index)
if normalized:
return scan_array_norm
else:
return scan_array
def _yield_array(arr):
larr = len(arr)
cursor = 0
while cursor < larr:
yield arr[cursor]
cursor = cursor + 1
def yield_scan_array(arr):
while True:
yield from _yield_array(arr)
#%%
#scan_start = 0
#scan_stop = 1
#scan_period = 1
#first_value = .6
#dt = 0.1
#
#a = create_scan_array(scan_start, scan_stop, scan_period, first_value, dt)
#print(a)
\ No newline at end of file
from pyqtgraph import dockarea
from pyqtgraph.Qt import QtGui, QtCore
from PyQt5.QtWidgets import QPushButton, QProgressBar, QLabel
import pyqtgraph as pg
import numpy as np
import scanFunctions
from messages import show_warning
class CustomSpinBox(pg.SpinBox):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if self.opts['suffix'] == 'V': # fixing a context menu with some scales
self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
self.customContextMenuRequested.connect(self._on_context_menu)
self.popMenu = QtGui.QMenu(self)
self.act_2_step = QtGui.QAction('2 mV step', self)
self.act_5_step = QtGui.QAction('5 mV step', self)
self.act_10_step = QtGui.QAction('10 mV step', self)
self.act_100_step = QtGui.QAction('100 mV step', self)
self.popMenu.addAction(self.act_2_step)
self.popMenu.addAction(self.act_5_step)
self.popMenu.addAction(self.act_10_step)
self.popMenu.addAction(self.act_100_step)
self.act_2_step.triggered.connect(lambda: self.setSingleStep(0.002))
self.act_5_step.triggered.connect(lambda: self.setSingleStep(0.005))
self.act_10_step.triggered.connect(lambda: self.setSingleStep(0.01))
self.act_100_step.triggered.connect(lambda: self.setSingleStep(0.1))
def _on_context_menu(self, point):
# show context menu
self.popMenu.exec_(self.mapToGlobal(point))
class LaserControlLayout(pg.LayoutWidget):
"""
A class for creating a layout with spinboxes and checkboxes to control a laser.
"""
_registry = []
def __init__(self, name, spinboxes = ['sbA', 'sbB'], checkboxes = ['cbA', 'cbB'], custom_geometry = False):
super().__init__()
self._registry.append(self)
self.name = name
if custom_geometry != True:
self.layout.addWidget(QtGui.QLabel(self.name), 0, 0)
self.spinboxes = {}
self.checkboxes = {}
for i, sb_name in enumerate(spinboxes, start = 1):
self.spinboxes[sb_name] = CustomSpinBox(siPrefix=True, suffix='V')
if custom_geometry != True:
self.addWidget(QtGui.QLabel(sb_name), i, 0)
self.addWidget(self.spinboxes[sb_name], i, 1)
for i, cb_name in enumerate(checkboxes, start = 0):
self.checkboxes[cb_name] = QtGui.QCheckBox()
if custom_geometry != True:
row = len(spinboxes) + 1
self.addWidget(QtGui.QLabel(cb_name), row, 2*i)
self.addWidget(self.checkboxes[cb_name], row, 2*i + 1)
def blink(self, cb):
pass
class LaserScanLayout(pg.LayoutWidget):
"""
A class for creating a layout for scanning a laser piezo.
It is important that the name parameter coincides with the name of the spinbox to scan.
"""
_registry = []
scanActionSignal = QtCore.pyqtSignal(object)
def __init__(self, name, signal_to_scan_layout, feedforward_enables = False):
super().__init__()
self._registry.append(self)
self.name = name
self.signal_to_scan_layout = signal_to_scan_layout
self.addWidget(QtGui.QLabel(self.name), 0, 0)
self.spinboxes = {}
self.spinboxes['start'] = CustomSpinBox(siPrefix=True, suffix='V')
self.spinboxes['stop'] = CustomSpinBox(siPrefix=True, suffix='V')
self.spinboxes['period'] = CustomSpinBox(siPrefix=True, suffix='s', step=1)
self.scan_button = QPushButton('Start scan')
self.scanpBar = QProgressBar(self)
self.scanLabelValue = QLabel(self)
self.addWidget(QtGui.QLabel('start'), 1, 0),
self.addWidget(self.spinboxes['start'], 1, 1)
self.addWidget(QtGui.QLabel('stop'), 2, 0)
self.addWidget(self.spinboxes['stop'], 2, 1)
self.addWidget(QtGui.QLabel('period'), 3, 0),
self.addWidget(self.spinboxes['period'], 3, 1)
self.addWidget(QtGui.QLabel('Scanning'), 4, 0)
self.addWidget(self.scan_button, 4, 1)
self.addWidget(self.scanLabelValue, 5, 0)
self.addWidget(self.scanpBar, 5, 1)
self.scan_button.clicked.connect(self._scan_button_clicked)
def name(self):
return self.name
@QtCore.pyqtSlot()
def _scan_button_clicked(self):
start = self.spinboxes['start'].value()
stop = self.spinboxes['stop'].value()
period = self.spinboxes['period'].value()
dt = 5.0e-3
first_value = float(self.signal_to_scan_layout.spinboxes[self.name].value())
scan_array = scanFunctions.create_scan_array(start, stop, period, first_value, dt)
scan_array_gen = scanFunctions.yield_scan_array(scan_array)
if first_value > float(stop) or first_value < float(start):
details = 'Remember remember the fifth of November.' + '\n' + 'Also remember that scan starts from the value of the corresponding spin box'
show_warning('Scan can not start outside scan range and scan start should be lower than scan stop', details_text= details)
elif len(scan_array) < 5:
show_warning('%i steps are too few, you need at least 5 steps' % (len(scan_array)) )
elif stop==start:
show_warning('Oh dear, you might be trying to scan from 0 to 0. Plase never do that again.')
else:
self.scanActionSignal.emit(scan_array_gen)
self.scan_button.setText('Stop scan')
self.signal_to_scan_layout.spinboxes[self.name].setEnabled(False)
self.spinboxes['start'].setEnabled(False)
self.spinboxes['stop'].setEnabled(False)
self.scan_button.setStyleSheet("background-color: green")
self.scan_button.setText('Scanning')
@QtCore.pyqtSlot(float)
def scan_step(self, val):
self.pBarEvent(val)
@QtCore.pyqtSlot(float)
def scan_end(self, val):
self.signal_to_scan_layout.spinboxes[self.name].setValue(val)
self.scan_button.setText('Start scan')
self.scan_button.setStyleSheet('background-color: None')
self.signal_to_scan_layout.spinboxes[self.name].setEnabled(True)
self.spinboxes['start'].setEnabled(True)
self.spinboxes['stop'].setEnabled(True)
def pBarEvent(self, current_step):
start = self.spinboxes['start'].value()
stop = self.spinboxes['stop'].value()
self.scanpBar.setValue( 100 * float(current_step-float(start))/float(stop-start) )
self.scanLabelValue.setText("%.2f" % current_step + ' V')
if __name__ == '__main__':
import sys
from PyQt5.QtWidgets import QMainWindow
app = QtGui.QApplication([])
frame = QtGui.QFrame()
area = dockarea.DockArea()
grid = QtGui.QGridLayout()
frame.setLayout(grid)
myDock = dockarea.Dock('397', (100, 100))
area.addDock(myDock)
grid.addWidget(area, 0, 0)
l = LaserControlLayout('397', checkboxes = ['cb1', 'cb2', 'cb3'])
l_scan = LaserScanLayout('397 scan')
myDock.addWidget(l)
myDock.addWidget(l_scan)
frame.show()
sys.exit(app.exec_())
\ No newline at end of file
# -*- coding: utf-8 -*-
"""
Created on Wed Feb 6 13:07:39 2019
@author: Usuario
"""
#import serial
import time
#
#wavCoh=serial.Serial('COM10', timeout=1, rtscts=True)
#wavCoh.close()
#%%
def read_measurement(wm):
with wm as w:
w.write(b'VAL?\n')
#time.sleep(0.2)
a = w.readline().decode("utf-8")
a = a.split(',')[1]
a = a.split('\r')[0]
return a
def read_measurement_forever(wm):
with wm as w:
while(True):
w.write(b'VAL?\n')
#time.sleep(0.2)
a = w.readline().decode("utf-8")
try:
a = a.split(',')[1]
a = a.split('\r')[0]
print(a)
except:
print("wrong value read")
time.sleep(1)
def set_unit(wm, unit):
"""
unit can be:
A: wl in air
V: wl in vacuum
F: frequency
W: wavenumber
"""
with wm as w:
string_to_write = b'UNI ' + unit + b'\n'
w.write(string_to_write)
def check_unit(wm):
with wm as w:
w.write(b'UNI? \n')
#time.sleep(0.2)
unit = w.readline().decode("utf-8")
if unit[5:6] == 'V':
return 'U'
return unit[5:6]
#%%
if __name__ == '__main__':
import serial
import time
wavCoh=serial.Serial('COM11', timeout=1, rtscts=True)
wavCoh.close()
set_unit(wavCoh, b'F')
print(read_measurement(wavCoh))
read_measurement_forever(wavCoh)
# print(a[12:20])
# check_unit(wavCoh)
# import os
# scriptDirectory = os.path.dirname(os.path.realpath(__file__))
# -*- coding: utf-8 -*-
"""
Created on Fri Aug 10 14:21:33 2018
@author: Martindrech
"""
from pyqtgraph.Qt import QtCore
import numpy as np
import pyqtgraph as pg
import cv2
from PyQt5.QtCore import QTimer
#from myWidgets import anal_control_signal
import sys
from PyQt5.QtWidgets import QMainWindow, QApplication, QMessageBox, QPushButton, QGraphicsProxyWidget
#from myWidgets import anal_control_signal
class roiWindow(QMainWindow):
def __init__(self, aoScan):
super().__init__()
self.aoScan = aoScan
print( 'Analog output selected for roi analysis: %s' % (self.aoScan.name) )
self.cap = cv2.VideoCapture(0)
self.n = int(1e2)
self.data = np.zeros(self.n)
self.i = 0
self.rois = []
#self.rois.append(pg.EllipseROI([110, 10], [30, 20], pen=(3,9)))
self.rois.append(pg.TestROI([500, 500], [100, 100], pen=(0,9)))
self.initUI()
def initUI(self):
## create GUI
self.w = pg.GraphicsWindow(size=(800,800), border=True)
self.v = self.w.addViewBox(colspan=2)
self.v.invertY(True) ## Images usually have their Y-axis pointing downward
self.v.setAspectLocked(True)
self.im1 = pg.ImageItem(self.capture())
self.v.addItem(self.im1)
self.imgTimer = QTimer()
self.imgTimer.timeout.connect(self.updateImage)
self.imgTimer.start(2)
self.im2 = pg.ImageItem()
self.v2 = self.w.addViewBox(1,0)
self.v2.addItem(self.im2)
#plot real time
self.p = self.w.addPlot(row = 3, col = 0, title="Time plot")
self.p.setRange(QtCore.QRectF(0, -10, 5000, 20))
self.p.setAutoPan(y=True)
self.p.setRange(xRange = (0, self.n), yRange = (0, 255))
self.curve = self.p.plot(pen='y')
self.pv = self.w.addPlot(row = 4, col = 0, title = "Voltage plot")
self.pv.setRange(QtCore.QRectF(0, -10, 5000, 20))
self.pv.setAutoPan(y=True)
self.curveVoltage = self.pv.plot(pen='y')
self.voltageDataX = []
self.voltageDataY = []
self.aoScan.ScanTimer.timeout.connect(self.updateVoltagePlotvsI)
self.optionsLayout = pg.LayoutWidget()
self.eraseButton = QPushButton('Erase plot')
self.eraseButton.clicked.connect(self.erasePlot)
self.autoScaleButton = QPushButton('Autoscale')
self.autoScaleButton.clicked.connect(self.autoScalePlot)
self.optionsLayout.addWidget(self.eraseButton, 0, 0)
self.optionsLayout.addWidget(self.autoScaleButton, 0, 1)
self.optionsLayout.setGeometry(10,10,300,200)
proxy = QGraphicsProxyWidget()
proxy.setWidget(self.optionsLayout)
self.w.addItem(proxy,row=5,col=0)
self.lastRoi = None
self.newData = 0
## Add each ROI to the scene and link its data to a plot curve with the same color
for r in self.rois:
self.v.addItem(r)
r.sigRegionChanged.connect(self.updateRoi)
self.w.show()
self.w.closeEvent = self.myCloseEvent
def updateVoltagePlotvsI(self):
#plot against voltage
self.voltageDataX = np.append(self.voltageDataX, self.aoScan.scan_step)
self.voltageDataY = np.append(self.voltageDataY, self.newData)
#○print(' Roi: ', self.voltageDataX[-1], self.voltageDataY[-1])
self.curveVoltage.setData(self.voltageDataX, self.voltageDataY)
def erasePlot(self):
self.voltageDataX = []
self.voltageDataY = []
def autoScalePlot(self):
x_min = float(self.aoScan.scan_sb_start.val)
x_max = float(self.aoScan.scan_sb_stop.val)
y_min = 0
y_max = self.newData
self.pv.setRange(xRange = (x_min, x_max), yRange = (y_min, y_max))
def myCloseEvent(self, event):
# this overrides the closeEvent method
reply = QMessageBox.question(self, 'Message',
"Quit roi analysis screen?", QMessageBox.Yes |
QMessageBox.No, QMessageBox.No)
if reply == QMessageBox.Yes:
self.imgTimer.stop()
self.cap.release()
cv2.destroyAllWindows()
event.accept()
else:
event.ignore()
def capture(self):
ret, frame = self.cap.read()
#frame = cv2.resize(frame, None, fx = 0.5, fy = 0.5, interpolation = cv2.INTER_CUBIC)
return frame[:, :, 0]
def updateImage(self):
self.im1.setImage(self.capture(), cmap = 'Gray')
self.updateRoi(self.lastRoi)
def updateRoi(self, roi):
if roi is None:
return
self.lastRoi = roi
roiSlice = roi.getArrayRegion(self.im1.image, img=self.im1)
self.im2.setImage(roiSlice)
self.newData = np.mean(roiSlice)
self.data[self.i] = self.newData
self.i = np.mod(self.i + 1, len(self.data))
self.curve.setData(self.data)
def updateRoiPlot(self, data):
self.curve.setData(data)
#app.processEvents() ## force complete redraw for every plot
if __name__ == '__main__':
ao = anal_control_signal('ao', 0)
app = QApplication(sys.argv)
ex = roiWindow(ao)
sys.exit(app.exec_())
\ No newline at end of file
# -*- coding: utf-8 -*-
"""
Spyder Editor
This is a temporary script file.
"""
import cv2
cap = cv2.VideoCapture(0)
cap1 = cv2.VideoCapture(1)
while(True):
# Capture frame-by-frame
ret, frame = cap.read()
ret1, frame1 = cap1.read()
# Our operations on the frame come here
gray = cv2.cvtColor(frame, 0)
gray1 = cv2.cvtColor(frame1, 0)
# Display the resulting frame
frame = cv2.resize(frame, None, fx = 0.5, fy = 0.5, interpolation = cv2.INTER_CUBIC)
frame1 = cv2.resize(frame1, None, fx = 0.5, fy = 0.5, interpolation = cv2.INTER_CUBIC)
final_frame = cv2.vconcat((frame, frame1))
cv2.imshow('final_frame',final_frame)
#cv2.imshow('frame1',gray1)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
# When everything done, release the capture
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment