def test_screen_geometry(): # just check that these don't raise an exception assert isinstance(utils.screen_geometry(), QtCore.QRect) assert isinstance(utils.screen_geometry(QtWidgets.QLabel()), QtCore.QRect) assert isinstance( utils.screen_geometry(QtWidgets.QLabel(parent=QtWidgets.QLabel())), QtCore.QRect)
def __init__(self): app = application() self.tab_widget = QtWidgets.QTabWidget() self.main_window = QtWidgets.QMainWindow() self.main_window.setWindowTitle('Standard Icons') self.main_window.setCentralWidget(self.tab_widget) self.main_window.closeEvent = self.close_event # add a progress bar to the status bar self.progress_bar = QtWidgets.QProgressBar( self.main_window.statusBar()) self.progress_bar.setAlignment(QtCore.Qt.AlignCenter) self.main_window.statusBar().addPermanentWidget(self.progress_bar) self.main_window.showMaximized() self.num_icons = 0 self.file_index = 0 self.zoom_widget = QtWidgets.QDialog() self.zoom_widget.setSizeGripEnabled(True) self.zoom_widget.resize(QtCore.QSize(256, 256)) self.zoom_widget.setWindowFlags(QtCore.Qt.WindowCloseButtonHint) vbox = QtWidgets.QVBoxLayout() self.zoom_label = QtWidgets.QLabel() self.zoom_label.setScaledContents(True) vbox.addWidget(self.zoom_label) self.zoom_widget.setLayout(vbox) qt_icons = [sp for sp in dir(QtWidgets.QStyle) if sp.startswith('SP_')] self.windows_files = [ 'accessibilitycpl', 'compstui', 'ddores', 'dmdskres', 'explorer', 'gameux', 'ieframe', 'imageres', 'mmcndmgr', 'mmres', 'moricons', 'netcenter', 'netshell', 'networkexplorer', 'pifmgr', 'pnidui', 'sensorscpl', 'setupapi', 'shell32', 'wmploc', 'wpdshext' ] self.num_files = 1 + len(self.windows_files) self.progress_bar.setRange(0, self.num_files) self.add_qt_tab('Qt Icons', qt_icons) if has_clr: self.windows_index = 0 self.timer = QtCore.QTimer() self.timer.timeout.connect(self.add_windows_tab) self.timer.start(0) else: self.update_message('Loaded {} icons.'.format(self.num_icons)) self.progress_bar.hide() app.exec()
def __init__(self, parent): """Display a QDialog to edit the settings""" super(_Settings, self).__init__(flags=QtCore.Qt.WindowCloseButtonHint) self.conn = parent._connection info = self.conn.get_hardware_info() self.setWindowTitle( info.modelNumber.decode('utf-8') + ' || ' + info.notes.decode('utf-8')) # move info max_vel, max_acc = self.conn.get_motor_velocity_limits() vel, acc = self.conn.get_vel_params() vel = self.conn.get_real_value_from_device_unit(vel, UnitType.VELOCITY) acc = self.conn.get_real_value_from_device_unit( acc, UnitType.ACCELERATION) backlash = self.conn.get_real_value_from_device_unit( self.conn.get_backlash(), UnitType.DISTANCE) # move widgets self.acc_spinbox = QtWidgets.QDoubleSpinBox() self.acc_spinbox.setMinimum(0) self.acc_spinbox.setMaximum(max_acc) self.acc_spinbox.setValue(acc) self.acc_spinbox.setToolTip( '<html><b>Range:</b><br>0 - {} mm/s<sup>2</sup></html>'.format( max_acc)) self.vel_spinbox = QtWidgets.QDoubleSpinBox() self.vel_spinbox.setMinimum(0) self.vel_spinbox.setMaximum(max_vel) self.vel_spinbox.setValue(vel) self.vel_spinbox.setToolTip( '<html><b>Range:</b><br>0 - {} mm/s</html>'.format(max_vel)) self.backlash_spinbox = QtWidgets.QDoubleSpinBox() self.backlash_spinbox.setMinimum(0) self.backlash_spinbox.setMaximum(5) self.backlash_spinbox.setValue(backlash) self.backlash_spinbox.setToolTip( '<html><b>Range:</b><br>0 - 5 mm</html>') move_group = QtWidgets.QGroupBox('Move Parameters') move_grid = QtWidgets.QGridLayout() move_grid.addWidget(QtWidgets.QLabel('Backlash'), 0, 0, alignment=QtCore.Qt.AlignRight) move_grid.addWidget(self.backlash_spinbox, 0, 1) move_grid.addWidget(QtWidgets.QLabel('mm'), 0, 2, alignment=QtCore.Qt.AlignLeft) move_grid.addWidget(QtWidgets.QLabel('Maximum Velocity'), 1, 0, alignment=QtCore.Qt.AlignRight) move_grid.addWidget(self.vel_spinbox, 1, 1) move_grid.addWidget(QtWidgets.QLabel('mm/s'), 1, 2, alignment=QtCore.Qt.AlignLeft) move_grid.addWidget(QtWidgets.QLabel('Acceleration'), 2, 0, alignment=QtCore.Qt.AlignRight) move_grid.addWidget(self.acc_spinbox, 2, 1) move_grid.addWidget(QtWidgets.QLabel('mm/s<sup>2</sup>'), 2, 2, alignment=QtCore.Qt.AlignLeft) move_group.setLayout(move_grid) # jog info jog_size = self.conn.get_real_value_from_device_unit( self.conn.get_jog_step_size(), UnitType.DISTANCE) vel, acc = self.conn.get_jog_vel_params() jog_vel = self.conn.get_real_value_from_device_unit( vel, UnitType.VELOCITY) jog_acc = self.conn.get_real_value_from_device_unit( acc, UnitType.ACCELERATION) # jog widgets min_jog, max_jog = 0.002, parent._max_pos_mm / 2.0 self.jog_size_spinbox = QtWidgets.QDoubleSpinBox() self.jog_size_spinbox.setMinimum(min_jog) self.jog_size_spinbox.setMaximum(max_jog) self.jog_size_spinbox.setDecimals(3) self.jog_size_spinbox.setValue(jog_size) self.jog_size_spinbox.setToolTip( '<html><b>Range:</b><br>{} - {} mm</html>'.format( min_jog, max_jog)) self.jog_acc_spinbox = QtWidgets.QDoubleSpinBox() self.jog_acc_spinbox.setMinimum(0) self.jog_acc_spinbox.setMaximum(max_acc) self.jog_acc_spinbox.setValue(jog_acc) self.jog_acc_spinbox.setToolTip( '<html><b>Range:</b><br>0 - {} mm/s<sup>2</sup></html>'.format( max_acc)) self.jog_vel_spinbox = QtWidgets.QDoubleSpinBox() self.jog_vel_spinbox.setMinimum(0) self.jog_vel_spinbox.setMaximum(max_vel) self.jog_vel_spinbox.setValue(jog_vel) self.jog_vel_spinbox.setToolTip( '<html><b>Range:</b><br>0 - {} mm/s</html>'.format(max_vel)) jog_group = QtWidgets.QGroupBox('Jog Parameters') jog_grid = QtWidgets.QGridLayout() jog_grid.addWidget(QtWidgets.QLabel('Step Size'), 0, 0, alignment=QtCore.Qt.AlignRight) jog_grid.addWidget(self.jog_size_spinbox, 0, 1) jog_grid.addWidget(QtWidgets.QLabel('mm'), 0, 2, alignment=QtCore.Qt.AlignLeft) jog_grid.addWidget(QtWidgets.QLabel('Maximum Velocity'), 1, 0, alignment=QtCore.Qt.AlignRight) jog_grid.addWidget(self.jog_vel_spinbox, 1, 1) jog_grid.addWidget(QtWidgets.QLabel('mm/s'), 1, 2, alignment=QtCore.Qt.AlignLeft) jog_grid.addWidget(QtWidgets.QLabel('Acceleration'), 2, 0, alignment=QtCore.Qt.AlignRight) jog_grid.addWidget(self.jog_acc_spinbox, 2, 1) jog_grid.addWidget(QtWidgets.QLabel('mm/s<sup>2</sup>'), 2, 2, alignment=QtCore.Qt.AlignLeft) jog_group.setLayout(jog_grid) hbox = QtWidgets.QHBoxLayout() hbox.addWidget(move_group) hbox.addWidget(jog_group) update_button = QtWidgets.QPushButton('Update') update_button.setToolTip('Update the device settings') update_button.clicked.connect(self.update_settings) cancel_button = QtWidgets.QPushButton('Cancel') cancel_button.setToolTip('Update the device settings') cancel_button.clicked.connect(self.close) info_button = QtWidgets.QPushButton() info_button.setIcon(get_icon('imageres|109')) info_button.clicked.connect( lambda: show_hardware_info(parent._connection)) info_button.setToolTip('Display the hardware information') button_layout = QtWidgets.QGridLayout() button_layout.addWidget(cancel_button, 0, 0) button_layout.addItem( QtWidgets.QSpacerItem(1, 1, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding), 0, 1) button_layout.addWidget(update_button, 0, 2) button_layout.addWidget(info_button, 0, 3) vbox = QtWidgets.QVBoxLayout() vbox.addLayout(hbox) vbox.addLayout(button_layout) self.setLayout(vbox)
def __init__(self, connection, config=None, parent=None): """A :class:`~QtWidgets.QWidget` for controlling a Thorlabs translation stage. Parameters ---------- connection : :class:`~msl.equipment.connection.Connection` The connection to the translational stage motor controller (e.g., LTS150, LTS300, KST101, KDC101, ...). config : :class:`~msl.equipment.config.Config`, optional A configuration file. The following elements can be defined in a :class:`~msl.equipment.config.Config` file to initialize a :class:`TranslationStage`: .. code-block:: xml <!-- The following attributes can be defined for a "preset" and a "jog size" element. For a "preset" you must define a name attribute: units - can be either "mm" or "device". If omitted then the default unit value is "mm" name - the text that will displayed in the GUI as the name of the preset If multiple translation stages are being used then you can uniquely identify which stage will have its properties updated by including one of the additional attributes: serial - the serial number of the translation stage motor controller alias - the same alias that is used in the <equipment> XML tag If you do not include one of 'serial' or 'alias' then all stages will be updated to the XML element value. --> <thorlabs_translation_stage_preset name='Si-PD' serial="123456789">54.232</thorlabs_translation_stage_preset> <thorlabs_translation_stage_preset name='InGaAs-PD' units="mm" serial="123456789">75.2</thorlabs_translation_stage_preset> <thorlabs_translation_stage_preset name='Reference' units="device" serial="123456789">10503037</thorlabs_translation_stage_preset> <!-- Note: In the following you can also specify the calibration path to be a path relative to the configuration file --> <thorlabs_translation_stage_calibration_path serial="123456789">path/to/calibration/file.dat</thorlabs_translation_stage_calibration_path> <!-- Since the 'serial', 'alias' and 'unit' attributes are not defined then all stages will have the jog size set to 2.0 mm --> <thorlabs_translation_stage_jog_size>2.0</thorlabs_translation_stage_jog_size> parent : :class:`QtWidgets.QWidget`, optional The parent widget. """ super(TranslationStage, self).__init__(parent=parent) if signaler is None: raise ImportError( 'This widget requires that the MSL-Equipment package is installed' ) if config is not None and not issubclass(config.__class__, Config): raise TypeError( 'Must pass in a MSL Equipment configuration object. Received {}' .format(config.__class__)) self._connection = connection self._supports_calibration = hasattr(self._connection, 'set_calibration_file') self._uncalibrated_mm = np.array([]) self._calibrated_mm = np.array([]) self._calibration_label = '' # set the calibration file if config is not None and self._supports_calibration: elements = self._find_xml_elements( config, 'thorlabs_translation_stage_calibration_path') if elements: cal_path = elements[0].text rel_path = os.path.join(os.path.dirname(config.path), cal_path) if os.path.isfile(cal_path): self.set_calibration_file(cal_path) elif os.path.isfile(rel_path): self.set_calibration_file(rel_path) else: prompt.critical('Cannot find calibration file\n' + cal_path) # set the presets self._preset_combobox = QtWidgets.QComboBox() self._preset_combobox.setToolTip('Preset positions') self._preset_combobox.addItems(['', 'Home']) self.preset_positions = {} if config is not None: for element in self._find_xml_elements( config, 'thorlabs_translation_stage_preset'): self.add_preset(element.attrib['name'], float(element.text), element.attrib.get('units', 'mm') == 'mm') self._min_pos_mm, self._max_pos_mm = self._connection.get_motor_travel_limits( ) self._position_display = QtWidgets.QLineEdit() self._position_display.setReadOnly(True) self._position_display.setFont(QtGui.QFont('Helvetica', 24)) self._position_display.mouseDoubleClickEvent = self._ask_move_to fm = QtGui.QFontMetrics(self._position_display.font()) self._position_display.setFixedWidth( fm.width(' {}.xxx'.format(int(self._max_pos_mm)))) self._home_button = QtWidgets.QPushButton() self._home_button.setToolTip('Go to the Home position') self._home_button.clicked.connect(self.go_home) self._home_button.setIcon(get_icon('ieframe|0')) self._stop_button = QtWidgets.QPushButton('Stop') self._stop_button.setToolTip('Stop moving immediately') self._stop_button.clicked.connect(self._connection.stop_immediate) self._stop_button.setIcon(get_icon('wmploc|155')) if config is not None: elements = self._find_xml_elements( config, 'thorlabs_translation_stage_jog_size') if elements: element = elements[0] if element.attrib.get('units', 'mm') == 'mm': jog_mm = float(element.text) jog = self._connection.get_device_unit_from_real_value( jog_mm, UnitType.DISTANCE) s = element.text + ' mm' else: jog = int(float(element.text)) jog_mm = self._connection.get_real_value_from_device_unit( jog, UnitType.DISTANCE) s = element.text + ' device units' if jog_mm > self._max_pos_mm or jog_mm < self._min_pos_mm: prompt.critical('Invalid jog size of ' + s) else: self._connection.set_jog_step_size(jog) self._jog_forward_button = QtWidgets.QPushButton() self._jog_forward_button.clicked.connect( lambda: self.jog_forward(False)) self._jog_forward_button.setIcon(get_icon(QtWidgets.QStyle.SP_ArrowUp)) self._jog_backward_button = QtWidgets.QPushButton() self._jog_backward_button.clicked.connect( lambda: self.jog_backward(False)) self._jog_backward_button.setIcon( get_icon(QtWidgets.QStyle.SP_ArrowDown)) settings_button = QtWidgets.QPushButton() settings_button.clicked.connect(self._show_settings) settings_button.setIcon(get_icon('shell32|71')) settings_button.setToolTip('Edit the jog and move settings') grid = QtWidgets.QGridLayout() grid.addWidget(QtWidgets.QLabel('Presets:'), 0, 0, alignment=QtCore.Qt.AlignRight) grid.addWidget(self._preset_combobox, 0, 1) grid.addWidget(self._stop_button, 0, 2, 1, 2) grid.addWidget(self._position_display, 1, 0, 2, 2) grid.addWidget(self._home_button, 1, 2) grid.addWidget(self._jog_forward_button, 1, 3) grid.addWidget(settings_button, 2, 2) grid.addWidget(self._jog_backward_button, 2, 3) grid.setSpacing(0) grid.setRowStretch(3, 1) grid.setColumnStretch(4, 1) self.setLayout(grid) self._connection.start_polling(200) self._polling_duration = self._connection.polling_duration() * 1e-3 self._connection.register_message_callback(callback) signaler.signal.connect(self._update_display) self._requested_mm = None self._update_jog_tooltip() self._update_display() self._requested_mm = float(self._position_display.text()) self._preset_combobox.setCurrentText( self._get_preset_name(self._requested_mm)) self._preset_combobox.currentIndexChanged[str].connect( self._go_to_preset)
def __init__(self, connection, parent=None): """ A :class:`~QtWidgets.QWidget` for a :class:`~msl.equipment.connection_message_based.ConnectionMessageBased` connection. This widget allows for reading/writing messages from/to equipment. Parameters ---------- connection : :class:`~msl.equipment.connection_message_based.ConnectionMessageBased` The connection to the equipment. parent : :class:`QtWidgets.QWidget` The parent widget. Example ------- To view an example of the :class:`MessageBased` widget that will send messages to a *dummy* :class:`~msl.equipment.record_types.EquipmentRecord` in demo mode, run: >>> from msl.examples.qt.equipment import message_based # doctest: +SKIP >>> message_based.show() # doctest: +SKIP """ super(MessageBased, self).__init__(parent=parent) r = connection.equipment_record self.setWindowTitle('{} || {} || {}'.format(r.manufacturer, r.model, r.serial)) self.setAcceptDrops(True) self._conn = connection self._dropped_commands = [] self._abort_execution = False self._command_list = [] self._header = ['Action', 'Delay', 'Message', 'Reply'] self._actions = ['write', 'read', 'query', 'delay'] self._table = QtWidgets.QTableWidget(0, len(self._header), self) self._table.setHorizontalHeaderLabels(self._header) self._table.horizontalHeader().setStretchLastSection(True) self._table.horizontalHeader().setContextMenuPolicy( QtCore.Qt.CustomContextMenu) self._table.horizontalHeader().customContextMenuRequested.connect( self._show_horizontal_popup_menu) self._table.verticalHeader().setContextMenuPolicy( QtCore.Qt.CustomContextMenu) self._table.verticalHeader().customContextMenuRequested.connect( self._show_vertical_popup_menu) self._timeout_spinbox = QtWidgets.QDoubleSpinBox() self._timeout_spinbox.setToolTip( '<html>The timeout value to use for <i>read</i> commands</html>') self._timeout_spinbox.setRange(0, 999999999) if 'ConnectionPyVISA' in '{}'.format( connection.__class__.__bases__): # a PyVISA connection self._timeout_spinbox.setSuffix(' ms') self._timeout_spinbox.setDecimals(0) else: self._timeout_spinbox.setSuffix(' s') self._timeout_spinbox.setDecimals(2) try: self._timeout_spinbox.setValue(self._conn.timeout) except TypeError: # in case the connection is established in demo mode self._timeout_spinbox.setValue(0) self._timeout_spinbox.valueChanged.connect(self._update_timeout) self._use_rows = QtWidgets.QLineEdit() self._use_rows.setToolTip( 'Enter the rows to execute or leave blank to execute all rows.\nFor example: 1,3,5-8' ) self._execute_icon = get_icon(QtWidgets.QStyle.SP_ArrowRight) self._continuous_icon = get_icon(QtWidgets.QStyle.SP_BrowserReload) self._abort_icon = get_icon(QtWidgets.QStyle.SP_BrowserStop) self._clear_icon = get_icon(QtWidgets.QStyle.SP_DialogResetButton) self._remove_icon = get_icon(QtWidgets.QStyle.SP_DialogCancelButton) self._insert_before_icon = get_icon(QtWidgets.QStyle.SP_DialogOkButton) # create an insert_after_icon by transforming the insert_before_icon size = self._insert_before_icon.availableSizes()[-1] pixmap = self._insert_before_icon.pixmap(size).transformed( QtGui.QTransform().scale(-1, 1)) self._insert_after_icon = QtGui.QIcon(pixmap) self._execute_thread = _Execute(self) self._execute_thread.finished.connect(self._check_if_looping) self._execute_thread.sig_error.connect(self._execute_error) self._execute_thread.sig_update_row_color.connect( self._update_row_appearance) self._execute_thread.sig_highlight_row.connect(self._highlight_row) self._execute_thread.sig_update_reply.connect(self._update_reply) self._execute_thread.sig_show_execute_icon.connect( self._show_execute_icon) self._loop_checkbox = QtWidgets.QCheckBox() self._loop_checkbox.setToolTip('Run continuously?') self._loop_checkbox.clicked.connect(self._show_execute_icon) save_icon = get_icon(QtWidgets.QStyle.SP_DriveFDIcon) self._save_button = QtWidgets.QPushButton(save_icon, 'Save') self._save_button.setToolTip('Save the table to a tab-delimited file') self._save_button.clicked.connect(self._save) self._info_button = QtWidgets.QPushButton( get_icon(QtWidgets.QStyle.SP_FileDialogInfoView), '') self._info_button.setToolTip( 'Display the information about the equipment') self._info_button.clicked.connect( lambda clicked, record=r: show_record(record)) self._status_label = QtWidgets.QLabel() self._execute_button = QtWidgets.QPushButton() self._execute_button.clicked.connect(self._execute_start) self._show_execute_icon() self._status_label.setText('Create a new Execution Table or\n' 'Drag & Drop or Copy & Paste\n' 'a previous Execution Table') execute_widget = QtWidgets.QWidget() grid = QtWidgets.QGridLayout() grid.addWidget(QtWidgets.QLabel('Timeout'), 1, 0, alignment=QtCore.Qt.AlignRight) grid.addWidget(self._timeout_spinbox, 1, 1, 1, 2) grid.addWidget(QtWidgets.QLabel('Rows'), 2, 0, alignment=QtCore.Qt.AlignRight) grid.addWidget(self._use_rows, 2, 1, 1, 2) grid.addWidget(self._execute_button, 3, 0, 1, 2) grid.addWidget(self._loop_checkbox, 3, 2, 1, 1, alignment=QtCore.Qt.AlignLeft) grid.addWidget(self._save_button, 4, 0, 1, 2) grid.addWidget(self._info_button, 4, 2, 1, 1) grid.addWidget(self._status_label, 5, 0, 1, 3, alignment=QtCore.Qt.AlignBottom) grid.setRowStretch(5, 1) execute_widget.setLayout(grid) self._create_row() self._table.resizeColumnsToContents() splitter = QtWidgets.QSplitter() splitter.addWidget(self._table) splitter.addWidget(execute_widget) splitter.setStretchFactor(0, 1) splitter.setChildrenCollapsible(False) splitter.setSizes([1, 0]) hbox = QtWidgets.QHBoxLayout() hbox.addWidget(splitter) self.setLayout(hbox)
def __init__(self, client, **kwargs): super(Gui, self).__init__() self.path = kwargs.pop('path', None) self.client = client self.original_image = None self.delta = 0.01 # the amount to translate image on UP DOWN LEFT RIGHT key presses self.client_queue = {} # a queue of requests to send to the RPi zoom = kwargs.get('zoom') if zoom: self.zoom_history = [zoom] # (x, y, width, height) values between 0.0 and 1.0 else: self.zoom_history = [] self.ocr_params = { 'zoom': zoom, 'rotate': kwargs.get('rotate'), 'threshold': kwargs.get('threshold'), 'dilate': kwargs.get('dilate'), 'erode': kwargs.get('erode'), 'blur': kwargs.get('blur'), 'algorithm': kwargs.get('algorithm', 'tesseract').lower(), 'lang': kwargs.get('lang', 'eng').lower(), } self.ocr_label = QtWidgets.QLabel() self.ocr_label.setFont(QtGui.QFont('Helvetica', 14)) # the canvas widget to display the image self.graphics_view = pg.GraphicsView(background=None) self.view_box = pg.ViewBox(invertY=True, lockAspect=True, enableMouse=False, enableMenu=False) self.graphics_view.setCentralItem(self.view_box) self.canvas = pg.ImageItem(axisOrder='row-major') self.view_box.setMouseMode(pg.ViewBox.RectMode) self.view_box.addItem(self.canvas) self.view_box.mouseDragEvent = self.select_zoom graphing_layout = QtWidgets.QVBoxLayout() graphing_layout.addWidget(self.ocr_label, alignment=QtCore.Qt.AlignCenter) graphing_layout.addWidget(self.graphics_view) # the container for all the image-processing widgets self.image_processing_group = QtWidgets.QGroupBox('Image Processing') # rotate self.rotate_label = QtWidgets.QLabel('<html>Rotate [0°]</html>') self.rotate_slider = QtWidgets.QSlider(orientation=QtCore.Qt.Horizontal) self.rotate_slider.setToolTip('The angle to rotate the image') self.rotate_slider.setMinimum(-180) self.rotate_slider.setMaximum(180) self.rotate_slider.setSingleStep(1) self.rotate_slider.setPageStep(15) self.rotate_slider.valueChanged.connect(self.update_rotate) if self.ocr_params['rotate']: self.rotate_slider.setValue(self.ocr_params['rotate']) # Gaussian blur self.blur_label = QtWidgets.QLabel('Gaussian Blur [0]') self.blur_slider = QtWidgets.QSlider(orientation=QtCore.Qt.Horizontal) self.blur_slider.setToolTip('The pixel radius to use for the Gaussian blur') self.blur_slider.setMinimum(0) self.blur_slider.setMaximum(kwargs.pop('max_blur', 9)) self.blur_slider.setSingleStep(1) self.blur_slider.valueChanged.connect(self.update_blur) if self.ocr_params['blur']: self.blur_slider.setValue(self.ocr_params['blur']) # threshold self.threshold_label = QtWidgets.QLabel('Threshold [0]') self.threshold_checkbox = QtWidgets.QCheckBox() self.threshold_checkbox.setToolTip('Apply thresholding?') self.threshold_checkbox.clicked.connect(self.update_threshold_checkbox) self.threshold_slider = QtWidgets.QSlider(orientation=QtCore.Qt.Horizontal) self.threshold_slider.setToolTip('The threshold value') self.threshold_slider.setMinimum(0) self.threshold_slider.setMaximum(255) self.threshold_slider.setSingleStep(1) self.threshold_slider.valueChanged.connect(self.update_threshold) if self.ocr_params['threshold']: self.threshold_slider.setValue(self.ocr_params['threshold']) self.threshold_checkbox.setChecked(True) else: self.threshold_slider.setEnabled(False) self.threshold_checkbox.setChecked(False) # dilate self.dilate_label = QtWidgets.QLabel('Dilate [0]') self.dilate_slider = QtWidgets.QSlider(orientation=QtCore.Qt.Horizontal) self.dilate_spinbox = QtWidgets.QSpinBox() self.dilate_spinbox.setToolTip('The number of iterations to apply dilation at the specified radius') self.dilate_spinbox.setMinimum(1) self.dilate_spinbox.setMaximum(99) self.dilate_spinbox.setSingleStep(1) self.dilate_spinbox.valueChanged.connect(self.update_dilate_iter) self.dilate_slider.setToolTip('The pixel radius to use for dilation') self.dilate_slider.setMinimum(0) self.dilate_slider.setMaximum(kwargs.pop('max_dilate', 9)) self.dilate_slider.setSingleStep(1) self.dilate_slider.valueChanged.connect(self.update_dilate) if self.ocr_params['dilate']: self.dilate_slider.setValue(self.ocr_params['dilate']) # erode self.erode_label = QtWidgets.QLabel('Erode [0]') self.erode_slider = QtWidgets.QSlider(orientation=QtCore.Qt.Horizontal) self.erode_spinbox = QtWidgets.QSpinBox() self.erode_spinbox.setToolTip('The number of iterations to apply erosion at the specified radius') self.erode_spinbox.setMinimum(1) self.erode_spinbox.setMaximum(99) self.erode_spinbox.setSingleStep(1) self.erode_spinbox.valueChanged.connect(self.update_erode_iter) self.erode_slider.setToolTip('The pixel radius to use for erosion') self.erode_slider.setMinimum(0) self.erode_slider.setMaximum(kwargs.pop('max_erode', 9)) self.erode_slider.setSingleStep(1) self.erode_slider.valueChanged.connect(self.update_erode) if self.ocr_params['erode']: self.erode_slider.setValue(self.ocr_params['erode']) # image-processing layout ip_layout = QtWidgets.QGridLayout() ip_layout.addWidget(self.rotate_label, 0, 0) ip_layout.addWidget(self.rotate_slider, 0, 1) ip_layout.addWidget(self.blur_label, 1, 0) ip_layout.addWidget(self.blur_slider, 1, 1) ip_layout.addWidget(self.threshold_label, 2, 0) ip_layout.addWidget(self.threshold_slider, 2, 1) ip_layout.addWidget(self.threshold_checkbox, 2, 2) ip_layout.addWidget(self.dilate_label, 3, 0) ip_layout.addWidget(self.dilate_slider, 3, 1) ip_layout.addWidget(self.dilate_spinbox, 3, 2) ip_layout.addWidget(self.erode_label, 4, 0) ip_layout.addWidget(self.erode_slider, 4, 1) ip_layout.addWidget(self.erode_spinbox, 4, 2) self.image_processing_group.setLayout(ip_layout) # the container for all the camera widgets self.camera_config_group = QtWidgets.QGroupBox('Camera Settings') # ISO self.iso_combobox = QtWidgets.QComboBox() self.iso_combobox.setToolTip('The ISO setting of the camera') self.iso_combobox.addItems(['auto', '100', '200', '320', '400', '500', '640', '800']) iso = str(kwargs.pop('iso', 'auto')) if iso == '0': iso = 'auto' self.iso_combobox.setCurrentText(iso) self.update_iso(self.iso_combobox.currentText()) self.iso_combobox.currentTextChanged.connect(self.update_iso) self.iso_combobox.setEnabled(not self.path) # resolution self.resolution_combobox = QtWidgets.QComboBox() self.resolution_combobox.setToolTip('The resolution of the camera') self.resolution_combobox.addItems(['VGA', 'SVGA', 'XGA', '720p', 'SXGA', 'UXGA', '1080p', 'MAX']) self.resolution_combobox.setCurrentText(str(kwargs.pop('resolution', 'VGA'))) self.update_resolution(self.resolution_combobox.currentText()) self.resolution_combobox.currentTextChanged.connect(self.update_resolution) self.resolution_combobox.setEnabled(not self.path) # exposure mode self.exposure_mode_combobox = QtWidgets.QComboBox() self.exposure_mode_combobox.setToolTip('The exposure mode of the camera') self.exposure_mode_combobox.addItems(['off', 'auto', 'night', 'nightpreview', 'backlight', 'spotlight', 'sports', 'snow', 'beach', 'verylong', 'fixedfps', 'antishake', 'fireworks']) self.exposure_mode_combobox.setCurrentText(str(kwargs.pop('exposure_mode', 'auto'))) self.update_exposure_mode(self.exposure_mode_combobox.currentText()) self.exposure_mode_combobox.currentTextChanged.connect(self.update_exposure_mode) self.exposure_mode_combobox.setEnabled(not self.path) camera_layout = QtWidgets.QGridLayout() camera_layout.addWidget(QtWidgets.QLabel('ISO'), 0, 0) camera_layout.addWidget(self.iso_combobox, 0, 1) camera_layout.addItem(QtWidgets.QSpacerItem(1, 1, QtWidgets.QSizePolicy.MinimumExpanding), 0, 2) camera_layout.addWidget(QtWidgets.QLabel('Resolution'), 1, 0) camera_layout.addWidget(self.resolution_combobox, 1, 1) camera_layout.addWidget(QtWidgets.QLabel('Exposure Mode'), 2, 0) camera_layout.addWidget(self.exposure_mode_combobox, 2, 1) self.camera_config_group.setLayout(camera_layout) if self.client is None: self.iso_combobox.setEnabled(False) self.resolution_combobox.setEnabled(False) self.exposure_mode_combobox.setEnabled(False) # the container for all the OCR algorithm widgets self.ocr_config_group = QtWidgets.QGroupBox('OCR Algorithm Settings') # tesseract languages self.tess_lang_combobox = QtWidgets.QComboBox() self.tess_lang_combobox.addItems(['eng', 'letsgodigital']) self.tess_lang_combobox.currentTextChanged.connect(self.update_tess_lang) self.tess_lang_combobox.setToolTip('The language to use for Tesseract') # tesseract or ssocr self.tess_radio = QtWidgets.QRadioButton('Tesseract') self.tess_radio.setToolTip('Use Tesseract') self.tess_radio.toggled.connect(self.update_algorithm) self.ssocr_radio = QtWidgets.QRadioButton('SSOCR') self.ssocr_radio.setToolTip('Use SSOCR') self.ssocr_radio.toggled.connect(self.update_algorithm) if self.ocr_params['algorithm'] == 'ssocr': self.ssocr_radio.setChecked(True) else: self.tess_radio.setChecked(True) algo_layout = QtWidgets.QGridLayout() algo_layout.addWidget(self.tess_radio, 0, 0) algo_layout.addWidget(self.tess_lang_combobox, 0, 1) algo_layout.addItem(QtWidgets.QSpacerItem(1, 1, QtWidgets.QSizePolicy.MinimumExpanding), 0, 2) algo_layout.addWidget(self.ssocr_radio, 1, 0) self.ocr_config_group.setLayout(algo_layout) options_layout = QtWidgets.QVBoxLayout() options_layout.addWidget(self.image_processing_group) options_layout.addWidget(self.camera_config_group) options_layout.addWidget(self.ocr_config_group) options_layout.addStretch(1) layout = QtWidgets.QHBoxLayout() layout.addLayout(graphing_layout) layout.addLayout(options_layout) self.setLayout(layout) if self.path: self.setAcceptDrops(True) self.capture_thread = None self.original_image = utils.to_cv2(self.path) height, width = self.original_image.shape[:2] try: # could pass in an already-opened image object (which isn't a path) basename = os.path.basename(self.path) except OSError: basename = 'UNKNOWN' self.setWindowTitle('OCR || {} [{} x {}]'.format(basename, width, height)) else: self.setAcceptDrops(False) self.original_image = None self.capture_index = 0 self.setWindowTitle('OCR || Capture 0') self.capture_thread = Capture() self.capture_thread.add_callback(self.capture) self.capture_thread.start(self.client) self.apply_ocr()