class UArmUI(object): def __init__(self, ui, layout): self.main_ui = ui self.layout = layout super(UArmUI, self).__init__() self.handler = UArmHandler(self) self.lang = self.main_ui.lang self.status = 0 self.set_ui() self.set_disable(True) def set_ui(self): self._set_common_top_ui() self._set_tab() self._set_common_down_ui() self.connect_slot() def _set_common_top_ui(self): top_frame = QFrame() top_frame.setMaximumHeight(60) top_layout = QVBoxLayout(top_frame) self.layout.addWidget(top_frame) common_top_frame = QFrame() common_top_frame.setMinimumHeight(50) common_top_frame.setMaximumHeight(50) common_top_layout = QHBoxLayout(common_top_frame) top_layout.addWidget(common_top_frame) common_top_layout.addStretch(0) label = QLabel(i18n[self.lang]['Type'] + ':') self.label_type = QLabel('') self.label_type.setStyleSheet('''color: gray;font:bold;''') common_top_layout.addWidget(label) common_top_layout.addWidget(self.label_type) label = QLabel(i18n[self.lang]['Mode'] + ':') self.label_mode = QLabel('') self.label_mode.setStyleSheet('''color: gray;font:bold;''') common_top_layout.addWidget(label) common_top_layout.addWidget(self.label_mode) label = QLabel(i18n[self.lang]['HardwareVersion'] + ':') self.label_hard_version = QLabel('') self.label_hard_version.setStyleSheet('''color: gray;font:bold;''') common_top_layout.addWidget(label) common_top_layout.addWidget(self.label_hard_version) label = QLabel(i18n[self.lang]['FirmwareVersion'] + ':') self.label_firm_version = QLabel('') self.label_firm_version.setStyleSheet('''color: gray;font:bold;''') common_top_layout.addWidget(label) common_top_layout.addWidget(self.label_firm_version) label_1 = QLabel(i18n[self.lang]['Connected'] + ':') self.label_connected = QLabel() img = QImage() self.label_connected.setMaximumHeight(20) self.label_connected.setMaximumWidth(20) self.label_connected.setScaledContents(True) if img.load(disconnect_icon_path): self.label_connected.setPixmap(QPixmap.fromImage(img)) self.lnt_addr = QLineEdit('COM12') self.lnt_addr.setMaximumWidth(50) self.lnt_addr.setMinimumWidth(30) self.btn_connect = QPushButton(i18n[self.lang]['Connect']) # self.btn_connect.setMaximumWidth(50) # common_top_layout.addStretch(0) common_top_layout.setSpacing(10) common_top_layout.addWidget(label_1) common_top_layout.addWidget(self.label_connected) common_top_layout.addWidget(self.lnt_addr) common_top_layout.addWidget(self.btn_connect) def _set_common_down_ui(self): slider_frame = QFrame() slider_layout = QGridLayout(slider_frame) self.layout.addWidget(slider_frame) label = QLabel(i18n[self.lang]['Speed'] + ':') self.slider_speed = QSlider(Qt.Horizontal) self.spinbox_speed = QSpinBox() self.slider_speed.setMinimum(1) self.slider_speed.setMaximum(100000) self.slider_speed.setValue(10000) self.spinbox_speed.setSingleStep(1) self.spinbox_speed.setMinimum(1) self.spinbox_speed.setMaximum(100000) self.spinbox_speed.setValue(10000) slider_layout.addWidget(label, 0, 0) slider_layout.addWidget(self.slider_speed, 0, 1) slider_layout.addWidget(self.spinbox_speed, 0, 2) # label = QLabel(i18n[self.lang]['Acc'] + ':') # self.slider_acc = QSlider(Qt.Horizontal) # self.spinbox_acc = QSpinBox() # self.slider_acc.setMinimum(1) # self.slider_acc.setMaximum(100000) # self.slider_acc.setValue(5000) # self.spinbox_acc.setSingleStep(1) # self.spinbox_acc.setMinimum(1) # self.spinbox_acc.setMaximum(100000) # self.spinbox_acc.setValue(5000) # slider_layout.addWidget(label, 0, 3) # slider_layout.addWidget(self.slider_acc, 0, 4) # slider_layout.addWidget(self.spinbox_acc, 0, 5) btn_frame = QFrame() btn_layout = QGridLayout(btn_frame) self.layout.addWidget(btn_frame) self.btn_get_device_info = QPushButton(i18n[self.lang]['GetDeviceInfo']) self.btn_get_position = QPushButton(i18n[self.lang]['GetPosition']) self.btn_get_polar = QPushButton(i18n[self.lang]['GetPolar']) self.btn_get_servo_angle = QPushButton(i18n[self.lang]['GetServoAngle']) self.btn_get_mode = QPushButton(i18n[self.lang]['GetMode']) btn_layout.addWidget(self.btn_get_device_info, 0, 0) btn_layout.addWidget(self.btn_get_position, 0, 1) btn_layout.addWidget(self.btn_get_polar, 0, 2) btn_layout.addWidget(self.btn_get_servo_angle, 0, 3) btn_layout.addWidget(self.btn_get_mode, 0, 4) self.combobox_servo = QComboBox() self.combobox_servo.setStyleSheet('''color: blue;''') for item in ['axis-1', 'axis-2', 'axis-3', 'axis-all']: self.combobox_servo.addItem(item) self.combobox_servo.setCurrentIndex(0) btn_layout.addWidget(self.combobox_servo, 0, 0) self.btn_servo_attach = QPushButton(i18n[self.lang]['ServoAttach']) self.btn_servo_detach = QPushButton(i18n[self.lang]['ServoDetach']) self.combobox_mode = QComboBox() self.combobox_mode.setStyleSheet('''color: blue;''') for item in ['normal', 'laser', '3D', 'pen']: self.combobox_mode.addItem(item) self.combobox_mode.setCurrentIndex(0) self.btn_set_mode = QPushButton(i18n[self.lang]['SetMode']) btn_layout.addWidget(self.combobox_servo, 1, 0) btn_layout.addWidget(self.btn_servo_attach, 1, 1) btn_layout.addWidget(self.btn_servo_detach, 1, 2) btn_layout.addWidget(self.combobox_mode, 1, 3) btn_layout.addWidget(self.btn_set_mode, 1, 4) self.btn_reset = QPushButton(i18n[self.lang]['Reset']) self.btn_pump_on = QPushButton(i18n[self.lang]['PumpOn']) self.btn_pump_off = QPushButton(i18n[self.lang]['PumpOff']) self.btn_gripper_catch = QPushButton(i18n[self.lang]['GripperCatch']) self.btn_gripper_release = QPushButton(i18n[self.lang]['GripperRelease']) btn_layout.addWidget(self.btn_reset, 2, 0) btn_layout.addWidget(self.btn_pump_on, 2, 1) btn_layout.addWidget(self.btn_pump_off, 2, 2) btn_layout.addWidget(self.btn_gripper_catch, 2, 3) btn_layout.addWidget(self.btn_gripper_release, 2, 4) @staticmethod def slider_spinbox_related(value, master=None, slave=None, scale=1): try: slave.setValue(value * scale) except Exception as e: print(e) def connect_slot(self): self.btn_connect.clicked.connect(self.connect) self.slider_speed.valueChanged.connect( functools.partial(self.slider_spinbox_related, slave=self.spinbox_speed, scale=1)) self.spinbox_speed.valueChanged.connect( functools.partial(self.slider_spinbox_related, slave=self.slider_speed, scale=1)) # self.slider_acc.valueChanged.connect( # functools.partial(self.slider_spinbox_related, slave=self.spinbox_acc, scale=1)) # self.spinbox_acc.valueChanged.connect( # functools.partial(self.slider_spinbox_related, slave=self.slider_acc, scale=1)) self.btn_get_device_info.clicked.connect(self.get_device_info) self.btn_get_position.clicked.connect(self.get_position) self.btn_get_polar.clicked.connect(self.get_polar) self.btn_get_servo_angle.clicked.connect(self.get_servo_angle) self.btn_get_mode.clicked.connect(self.get_mode) self.btn_servo_attach.clicked.connect(self.set_servo_attach) self.btn_servo_detach.clicked.connect(self.set_servo_detach) self.btn_set_mode.clicked.connect(self.set_mode) self.btn_reset.clicked.connect(self.reset) self.btn_pump_on.clicked.connect(self.pump_on) self.btn_pump_off.clicked.connect(self.pump_off) self.btn_gripper_catch.clicked.connect(self.gripper_catch) self.btn_gripper_release.clicked.connect(self.gripper_release) def reset_flag(self): self.cartesian_ui.reset_flag() self.axis_ui.reset_flag() def update_connect_status(self, item): try: img = QImage() if item and self.status != 1: self.status = 1 logger.info('connect to {} success'.format(self.handler.port)) if img.load(connect_icon_path): self.label_connected.setPixmap(QPixmap.fromImage(img)) self.btn_connect.setText(i18n[self.lang]['Disconnect']) self.btn_connect.setStyleSheet('''color: red;font:bold;''') self.set_disable(False) elif not item and self.status != 0: self.status = 0 logger.info('disconnect from {0} or failed connect {0}'.format(self.handler.port)) self.handler.cmd_que.queue.clear() if img.load(disconnect_icon_path): self.label_connected.setPixmap(QPixmap.fromImage(img)) self.btn_connect.setText(i18n[self.lang]['Connect']) self.btn_connect.setStyleSheet('''color: green;font:bold;''') self.set_disable(True) except Exception as e: print(e) def connect(self): try: if str(self.btn_connect.text()) == i18n[self.lang]['Connect']: addr = self.lnt_addr.text().strip() if addr == 'auto': addr = None self.btn_connect.setText('Connecting') self.status = 2 self.btn_connect.setStyleSheet('''color: orange;font:bold;''') self.handler.connect(addr) # if self.window.connect(addr, report_type=report_type): # self.btn_connect.setText(self.disconnect_label) # self.btn_connect.setStyleSheet('''color: red;font:bold;''') elif str(self.btn_connect.text()) == i18n[self.lang]['Disconnect']: self.handler.disconnect() self.btn_connect.setText(i18n[self.lang]['Connect']) self.btn_connect.setStyleSheet('''color: green;font:bold;''') except Exception as e: print(e) def get_device_info(self, event): try: item = { 'cmd': 'get_device_info', } self.handler.put_cmd_que(item) except Exception as e: print(e) def get_mode(self, event): try: item = { 'cmd': 'get_mode', } self.handler.put_cmd_que(item) except Exception as e: print(e) def get_position(self, event): try: item = { 'cmd': 'get_position', } self.handler.put_cmd_que(item) except Exception as e: print(e) def get_polar(self, event): try: item = { 'cmd': 'get_polar', } self.handler.put_cmd_que(item) except Exception as e: print(e) def get_servo_angle(self, event): try: item = { 'cmd': 'get_servo_angle', } self.handler.put_cmd_que(item) except Exception as e: print(e) def set_servo_attach(self, event): try: text = self.combobox_servo.currentText() if text == 'axis-all': servo_id = None else: servo_id = int(self.combobox_servo.currentIndex()) item = { 'cmd': 'set_servo_attach', 'kwargs': { 'servo_id': servo_id } } self.handler.put_cmd_que(item) except Exception as e: print(e) def set_servo_detach(self, event): try: text = self.combobox_servo.currentText() if text == 'axis-all': servo_id = None else: servo_id = int(self.combobox_servo.currentIndex()) item = { 'cmd': 'set_servo_detach', 'kwargs': { 'servo_id': servo_id } } self.handler.put_cmd_que(item) except Exception as e: print(e) def pump_on(self, event): try: item = { 'cmd': 'set_pump', 'kwargs': { 'on': True } } self.handler.put_cmd_que(item) except Exception as e: print(e) def pump_off(self, event): try: item = { 'cmd': 'set_pump', 'kwargs': { 'on': False } } self.handler.put_cmd_que(item) except Exception as e: print(e) def gripper_catch(self, event): try: item = { 'cmd': 'set_gripper', 'kwargs': { 'catch': True } } self.handler.put_cmd_que(item) except Exception as e: print(e) def gripper_release(self, event): try: item = { 'cmd': 'set_gripper', 'kwargs': { 'catch': False } } self.handler.put_cmd_que(item) except Exception as e: print(e) def set_mode(self, event): try: mode = self.combobox_mode.currentIndex() item = { 'cmd': 'set_mode', 'kwargs': { 'mode': int(mode) } } self.handler.put_cmd_que(item) except Exception as e: print(e) def reset(self, event): try: self.handler.cmd_que.queue.clear() item = { 'cmd': 'reset', 'kwargs': { 'speed': self.spinbox_speed.value(), 'wait': False, } } self.handler.put_cmd_que(item) self.reset_flag() except Exception as e: print(e) @staticmethod def slider_spinbox_related(value, master=None, slave=None, scale=1): try: slave.setValue(value * scale) except Exception as e: print(e) def _set_tab(self): tab_widget = QTabWidget() tab_widget.setMaximumHeight(self.main_ui.window.geometry().height() // 2) self.layout.addWidget(tab_widget) toolBox1 = QToolBox() toolBox2 = QToolBox() groupBox1 = QGroupBox() groupBox2 = QGroupBox() toolBox1.addItem(groupBox1, "") toolBox2.addItem(groupBox2, "") tab_widget.addTab(toolBox1, i18n[self.lang]['Angle']) tab_widget.addTab(toolBox2, i18n[self.lang]['Coordinate']) joint_layout = QVBoxLayout(groupBox1) cartesian_layout = QVBoxLayout(groupBox2) self.cartesian_ui = CoordinateUI(self, cartesian_layout) self.axis_ui = AngleUI(self, joint_layout) def update_device_info(self, device_info): if device_info is not None: self.label_type.setText(str(device_info.get('device_type', None))) self.label_hard_version.setText(str(device_info.get('hardware_version', None))) self.label_firm_version.setText(str(device_info.get('firmware_version', None))) def update_mode(self, mode): if mode is not None: if mode == 0: mode_str = 'Normal' elif mode == 1: mode_str = 'Laser' elif mode == 2: mode_str = '3D' elif mode == 3: mode_str = 'Pen' else: mode_str = self.label_mode.text() self.label_mode.setText(mode_str) def set_disable(self, disable): try: self.btn_get_device_info.setDisabled(disable) self.btn_get_position.setDisabled(disable) self.btn_get_polar.setDisabled(disable) self.btn_get_servo_angle.setDisabled(disable) self.btn_get_mode.setDisabled(disable) self.combobox_servo.setDisabled(disable) self.combobox_mode.setDisabled(disable) self.btn_servo_attach.setDisabled(disable) self.btn_servo_detach.setDisabled(disable) self.btn_reset.setDisabled(disable) self.btn_pump_on.setDisabled(disable) self.btn_pump_off.setDisabled(disable) self.btn_gripper_catch.setDisabled(disable) self.btn_gripper_release.setDisabled(disable) self.slider_speed.setDisabled(disable) self.spinbox_speed.setDisabled(disable) # self.slider_acc.setDisabled(disable) # self.spinbox_acc.setDisabled(disable) self.axis_ui.set_disable(disable) self.cartesian_ui.set_disable(disable) except Exception as e: print(e)
class XArmUI(object): def __init__(self, ui, layout): self.main_ui = ui self.layout = layout super(XArmUI, self).__init__() self.handler = XArmHandler(self) self.lang = self.main_ui.lang self.set_ui() self.set_disable(True) def set_ui(self): self._set_common_top_ui() self._set_tab() self._set_common_down_ui() self.connect_slot() def _set_common_top_ui(self): top_frame = QFrame() top_frame.setMaximumHeight(60) top_layout = QVBoxLayout(top_frame) self.layout.addWidget(top_frame) common_top_frame = QFrame() common_top_frame.setMinimumHeight(50) common_top_layout = QHBoxLayout(common_top_frame) top_layout.addWidget(common_top_frame) label_1 = QLabel(i18n[self.lang]['Connected'] + ':') self.label_connected = QLabel() img = QImage() self.label_connected.setMaximumHeight(20) self.label_connected.setMaximumWidth(20) self.label_connected.setScaledContents(True) if img.load(disconnect_icon_path): self.label_connected.setPixmap(QPixmap.fromImage(img)) label_2 = QLabel(i18n[self.lang]['Reported'] + ':') self.label_reported = QLabel() img = QImage() self.label_reported.setMaximumHeight(20) self.label_reported.setMaximumWidth(20) self.label_reported.setScaledContents(True) if img.load(disconnect_icon_path): self.label_reported.setPixmap(QPixmap.fromImage(img)) self.lnt_addr = QLineEdit('192.168.1.182') self.lnt_addr.setMaximumWidth(100) self.lnt_addr.setMinimumWidth(60) self.btn_connect = QPushButton(i18n[self.lang]['Connect']) # self.btn_connect.setMaximumWidth(50) # common_top_layout.addStretch(0) common_top_layout.setSpacing(10) common_top_layout.addWidget(label_1) common_top_layout.addWidget(self.label_connected) common_top_layout.addWidget(label_2) common_top_layout.addWidget(self.label_reported) common_top_layout.addWidget(self.lnt_addr) common_top_layout.addWidget(self.btn_connect) # common_down_frame = QFrame() # common_down_layout = QHBoxLayout(common_down_frame) # common_down_layout.setSpacing(0) # top_layout.addWidget(common_down_frame) common_down_layout = common_top_layout label = QLabel(i18n[self.lang]['WarnCode'] + ':') self.label_warn_code = QLabel('0') self.label_warn_code.setStyleSheet('''color: gray;font:bold;''') common_down_layout.addWidget(label) common_down_layout.addWidget(self.label_warn_code) label = QLabel(i18n[self.lang]['ErrorCode'] + ':') self.label_error_code = QLabel('0') self.label_error_code.setStyleSheet('''color: gray;font:bold;''') common_down_layout.addWidget(label) common_down_layout.addWidget(self.label_error_code) label = QLabel(i18n[self.lang]['CmdCount'] + ':') self.label_cmd_count = QLabel('0') self.label_cmd_count.setStyleSheet('''color: gray;font:bold;''') common_down_layout.addWidget(label) common_down_layout.addWidget(self.label_cmd_count) label = QLabel(i18n[self.lang]['State'] + ':') self.label_state = QLabel('4') self.label_state.setStyleSheet('''color: gray;font:bold;''') common_down_layout.addWidget(label) common_down_layout.addWidget(self.label_state) label = QLabel(i18n[self.lang]['Maable'] + ':') self.label_maable = QLabel('128') self.label_maable.setStyleSheet('''color: gray;font:bold;''') common_down_layout.addWidget(label) common_down_layout.addWidget(self.label_maable) label = QLabel(i18n[self.lang]['Mtbrake'] + ':') self.label_mtbrake = QLabel('128') self.label_mtbrake.setStyleSheet('''color: gray;font:bold;''') common_down_layout.addWidget(label) common_down_layout.addWidget(self.label_mtbrake) def _set_tab(self): tab_widget = QTabWidget() tab_widget.setMaximumHeight(self.main_ui.window.geometry().height() // 2) self.layout.addWidget(tab_widget) toolBox1 = QToolBox() toolBox2 = QToolBox() groupBox1 = QGroupBox() groupBox2 = QGroupBox() toolBox1.addItem(groupBox1, "") toolBox2.addItem(groupBox2, "") tab_widget.addTab(toolBox1, i18n[self.lang]['Joint']) tab_widget.addTab(toolBox2, i18n[self.lang]['Cartesian']) joint_layout = QVBoxLayout(groupBox1) cartesian_layout = QVBoxLayout(groupBox2) self.cartesian_ui = CartesianUI(self, cartesian_layout) self.axis_ui = JointUI(self, joint_layout) def _set_common_down_ui(self): slider_frame = QFrame() slider_layout = QGridLayout(slider_frame) self.layout.addWidget(slider_frame) label = QLabel(i18n[self.lang]['Speed'] + ':') self.slider_speed = QSlider(Qt.Horizontal) self.spinbox_speed = QSpinBox() self.slider_speed.setMinimum(1) self.slider_speed.setMaximum(1000) self.slider_speed.setValue(50) self.spinbox_speed.setSingleStep(1) self.spinbox_speed.setMinimum(1) self.spinbox_speed.setMaximum(1000) self.spinbox_speed.setValue(50) slider_layout.addWidget(label, 0, 0) slider_layout.addWidget(self.slider_speed, 0, 1) slider_layout.addWidget(self.spinbox_speed, 0, 2) label = QLabel(i18n[self.lang]['Acc'] + ':') self.slider_acc = QSlider(Qt.Horizontal) self.spinbox_acc = QSpinBox() self.slider_acc.setMinimum(1) self.slider_acc.setMaximum(100000) self.slider_acc.setValue(5000) self.spinbox_acc.setSingleStep(1) self.spinbox_acc.setMinimum(1) self.spinbox_acc.setMaximum(100000) self.spinbox_acc.setValue(5000) slider_layout.addWidget(label, 0, 3) slider_layout.addWidget(self.slider_acc, 0, 4) slider_layout.addWidget(self.spinbox_acc, 0, 5) common_frame = QFrame() common_layout = QGridLayout(common_frame) self.layout.addWidget(common_frame) self.btn_stop = QPushButton(i18n[self.lang]['Stop']) self.btn_clean = QPushButton(i18n[self.lang]['CleanErrorWarn']) self.btn_reset = QPushButton(i18n[self.lang]['Reset']) self.btn_get_servo_dbmsg = QPushButton( i18n[self.lang]['GetServoDebugMsg']) common_layout.addWidget(self.btn_stop, 0, 0) common_layout.addWidget(self.btn_clean, 0, 2) common_layout.addWidget(self.btn_reset, 0, 3) common_layout.addWidget(self.btn_get_servo_dbmsg, 0, 4) btn_frame = QFrame() btn_layout = QGridLayout(btn_frame) self.layout.addWidget(btn_frame) self.combobox_servo = QComboBox() self.combobox_servo.setStyleSheet('''color: blue;''') for item in [ 'axis-all', 'axis-1', 'axis-2', 'axis-3', 'axis-4', 'axis-5', 'axis-6', 'axis-7' ]: self.combobox_servo.addItem(item) self.combobox_servo.setCurrentIndex(1) btn_layout.addWidget(self.combobox_servo, 0, 0) self.btn_motion_enable = QPushButton(i18n[self.lang]['MotionEnable']) self.btn_motion_disable = QPushButton(i18n[self.lang]['MotionDisable']) self.btn_servo_attach = QPushButton(i18n[self.lang]['ServoAttach']) self.btn_servo_detach = QPushButton(i18n[self.lang]['ServoDetach']) self.combobox_state = QComboBox() self.combobox_state.setStyleSheet('''color: blue;''') for item in ['sport', 'pause', 'stop']: self.combobox_state.addItem(item) self.combobox_state.setCurrentIndex(0) self.btn_set_state = QPushButton(i18n[self.lang]['SetState']) btn_layout.addWidget(self.btn_motion_enable, 0, 1) btn_layout.addWidget(self.btn_motion_disable, 0, 2) btn_layout.addWidget(self.btn_servo_attach, 0, 3) btn_layout.addWidget(self.btn_servo_detach, 0, 4) btn_layout.addWidget(self.combobox_state, 0, 5) btn_layout.addWidget(self.btn_set_state, 0, 6) self.lnt_servo_addr = QLineEdit('servo_addr') self.lnt_servo_addr_value = QLineEdit('value') self.btn_get_servo_addr16 = QPushButton( i18n[self.lang]['GetServoAddr16']) self.btn_set_servo_addr16 = QPushButton( i18n[self.lang]['SetServoAddr16']) self.btn_get_servo_addr32 = QPushButton( i18n[self.lang]['GetServoAddr32']) self.btn_set_servo_addr32 = QPushButton( i18n[self.lang]['SetServoAddr32']) self.btn_set_servo_zero = QPushButton(i18n[self.lang]['SetServoZero']) btn_layout.addWidget(self.lnt_servo_addr, 1, 0) btn_layout.addWidget(self.lnt_servo_addr_value, 1, 1) btn_layout.addWidget(self.btn_get_servo_addr16, 1, 2) btn_layout.addWidget(self.btn_set_servo_addr16, 1, 3) btn_layout.addWidget(self.btn_get_servo_addr32, 1, 4) btn_layout.addWidget(self.btn_set_servo_addr32, 1, 5) btn_layout.addWidget(self.btn_set_servo_zero, 1, 6) def connect_slot(self): self.btn_connect.clicked.connect(self.connect) self.slider_speed.valueChanged.connect( functools.partial(self.slider_spinbox_related, slave=self.spinbox_speed, scale=1)) self.spinbox_speed.valueChanged.connect( functools.partial(self.slider_spinbox_related, slave=self.slider_speed, scale=1)) self.slider_acc.valueChanged.connect( functools.partial(self.slider_spinbox_related, slave=self.spinbox_acc, scale=1)) self.spinbox_acc.valueChanged.connect( functools.partial(self.slider_spinbox_related, slave=self.slider_acc, scale=1)) self.btn_stop.clicked.connect(self.stop) self.btn_clean.clicked.connect(self.clean) self.btn_reset.clicked.connect(self.reset) self.btn_get_servo_dbmsg.clicked.connect( functools.partial(self.handler.get_servo_debug_msg, only_log_error_servo=False)) self.btn_motion_enable.clicked.connect(self.motion_enable) self.btn_motion_disable.clicked.connect(self.motion_disable) self.btn_servo_attach.clicked.connect(self.set_servo_attach) self.btn_servo_detach.clicked.connect(self.set_servo_detach) self.btn_set_state.clicked.connect(self.set_state) self.btn_get_servo_addr16.clicked.connect(self.get_servo_addr_16) self.btn_set_servo_addr16.clicked.connect(self.set_servo_addr_16) self.btn_get_servo_addr32.clicked.connect(self.get_servo_addr_32) self.btn_set_servo_addr32.clicked.connect(self.set_servo_addr_32) self.btn_set_servo_zero.clicked.connect(self.set_servo_zero) @staticmethod def slider_spinbox_related(value, master=None, slave=None, scale=1): try: slave.setValue(value * scale) except Exception as e: print(e) def reset_flag(self): self.cartesian_ui.reset_flag() self.axis_ui.reset_flag() def update_maable_mtbrake(self, maable, mtbrake): try: self.label_maable.setText(str(maable)) self.label_mtbrake.setText(str(mtbrake)) self.label_maable.setStyleSheet('''color: green;font:bold;''') self.label_mtbrake.setStyleSheet('''color: green;font:bold;''') except Exception as e: print(e) def update_cmd_count(self, cmdnum): try: self.label_cmd_count.setText(str(cmdnum)) self.label_cmd_count.setStyleSheet('''color: green;font:bold;''') except Exception as e: print(e) def update_state(self, state): try: if state == 1: state_str = 'sport' self.label_state.setText(state_str) self.label_state.setStyleSheet('''color: green;font:bold;''') elif state == 2: state_str = 'sleep' self.label_state.setText(state_str) self.label_state.setStyleSheet('''color: gray;font:bold;''') elif state == 3: state_str = 'pause' self.label_state.setText(state_str) self.label_state.setStyleSheet('''color: orange;font:bold;''') elif state == 4: state_str = 'stop' self.label_state.setText(state_str) self.label_state.setStyleSheet('''color: red;font:bold;''') except Exception as e: print(e) # getattr(self, 'label_state').setText(state_str) # getattr(self, 'label_state').setText(str(state)) if state != 1: self.reset_flag() def update_warn_error(self, item): try: warn, error = item self.label_warn_code.setText(str(warn)) self.label_error_code.setText(str(error)) if warn != 0: self.label_warn_code.setStyleSheet('''color: red;font:bold;''') else: self.label_warn_code.setStyleSheet( '''color: green;font:bold;''') if error != 0: self.label_error_code.setStyleSheet( '''color: red;font:bold;''') else: self.label_error_code.setStyleSheet( '''color: green;font:bold;''') except Exception as e: print(e) def update_connect_status(self, item): try: img = QImage() if item[0]: logger.info('connect to {} success, report: {}'.format( self.handler.addr, self.handler.report_type)) if img.load(connect_icon_path): self.label_connected.setPixmap(QPixmap.fromImage(img)) self.btn_connect.setText(i18n[self.lang]['Disconnect']) self.btn_connect.setStyleSheet('''color: red;font:bold;''') self.set_disable(False) else: logger.info('disconnect from or failed connect {}'.format( self.handler.addr)) self.handler.cmd_que.queue.clear() if img.load(disconnect_icon_path): self.label_connected.setPixmap(QPixmap.fromImage(img)) self.btn_connect.setText(i18n[self.lang]['Connect']) self.btn_connect.setStyleSheet( '''color: green;font:bold;''') self.set_disable(True) if item[1]: if img.load(connect_icon_path): self.label_reported.setPixmap(QPixmap.fromImage(img)) else: if img.load(disconnect_icon_path): self.label_reported.setPixmap(QPixmap.fromImage(img)) except Exception as e: print(e) def connect(self, event): try: if str(self.btn_connect.text()) == i18n[self.lang]['Connect']: addr = self.lnt_addr.text().strip() if addr == '192.168.1.': addr = 'localhost' report_type = 'normal' else: tmp = addr.split(':') addr = tmp[0] report_type = tmp[1] if len(tmp) > 1 else 'normal' self.btn_connect.setText('Connecting') self.btn_connect.setStyleSheet('''color: orange;font:bold;''') self.handler.connect(addr, report_type=report_type) # if self.window.connect(addr, report_type=report_type): # self.btn_connect.setText(self.disconnect_label) # self.btn_connect.setStyleSheet('''color: red;font:bold;''') elif str(self.btn_connect.text()) == i18n[self.lang]['Disconnect']: self.handler.disconnect() self.btn_connect.setText(i18n[self.lang]['Connect']) self.btn_connect.setStyleSheet('''color: green;font:bold;''') except Exception as e: print(e) def stop(self, event): try: self.handler.cmd_que.queue.clear() if self.handler.xarm and self.handler.xarm.warn_code != 0: item = { 'cmd': 'clean_warn', } self.handler.put_cmd_que(item) if self.handler.xarm and self.handler.xarm.error_code != 0: item = { 'cmd': 'clean_error', } self.handler.put_cmd_que(item) item = { 'cmd': 'motion_enable', 'kwargs': { 'servo_id': 0, 'enable': True } } self.handler.put_cmd_que(item) item = { 'cmd': 'urgent_stop', } self.handler.put_cmd_que(item) self.reset_flag() except Exception as e: print(e) def clean(self, event): try: self.handler.cmd_que.queue.clear() if self.handler.xarm and self.handler.xarm.warn_code != 0: item = { 'cmd': 'clean_warn', } self.handler.put_cmd_que(item) if self.handler.xarm and self.handler.xarm.error_code != 0: item = { 'cmd': 'clean_error', } self.handler.put_cmd_que(item) except Exception as e: print(e) def reset(self, event): try: self.handler.cmd_que.queue.clear() if self.handler.xarm and self.handler.xarm.warn_code != 0: item = { 'cmd': 'clean_warn', } self.handler.put_cmd_que(item) if self.handler.xarm and self.handler.xarm.error_code != 0: item = { 'cmd': 'clean_error', } self.handler.put_cmd_que(item) item = { 'cmd': 'motion_enable', 'kwargs': { 'servo_id': 0, 'enable': True } } self.handler.put_cmd_que(item) item = {'cmd': 'set_state', 'kwargs': {'state': 0}} self.handler.put_cmd_que(item) item = { 'cmd': 'move_gohome', 'kwargs': { 'speed': 30, 'mvacc': 5000, } } self.handler.put_cmd_que(item) self.reset_flag() except Exception as e: print(e) def get_servo_addr_16(self, event): try: addr = self.lnt_servo_addr.text().strip() try: if addr.lower().startswith('0x'): addr = int(addr, base=16) else: addr = int(addr) except: QMessageBox.warning(self.main_ui.window, '错误', '请输入正确的地址, 地址必须是u16类型') return text = self.combobox_servo.currentText() if text == 'axis-all': QMessageBox.warning(self.main_ui.window, '警告', '请选择其中一个电机,不能选择所有电机') return else: servo_id = int(text.split('-')[-1]) tmp = '你确定要获取电机{}的地址{}的值吗?'.format(servo_id, addr) if QMessageBox.question(self.main_ui.window, '警告', tmp) == QMessageBox.Yes: item = { 'cmd': 'get_servo_addr_16', 'kwargs': { 'servo_id': servo_id, 'addr': addr } } self.handler.put_cmd_que(item) except Exception as e: print(e) def get_servo_addr_32(self, event): try: addr = self.lnt_servo_addr.text().strip() try: if addr.lower().startswith('0x'): addr = int(addr, base=16) else: addr = int(addr) except: QMessageBox.warning(self.main_ui.window, '错误', '请输入正确的地址, 地址必须是u16类型') return text = self.combobox_servo.currentText() if text == 'axis-all': QMessageBox.warning(self.main_ui.window, '警告', '请选择其中一个电机,不能选择所有电机') return else: servo_id = int(text.split('-')[-1]) tmp = '你确定要获取电机{}的地址{}的值吗?'.format(servo_id, addr) if QMessageBox.question(self.main_ui.window, '警告', tmp) == QMessageBox.Yes: item = { 'cmd': 'get_servo_addr_32', 'kwargs': { 'servo_id': servo_id, 'addr': addr } } self.handler.put_cmd_que(item) except Exception as e: print(e) def set_servo_addr_16(self, event): try: addr = self.lnt_servo_addr.text().strip() try: if addr.lower().startswith('0x'): addr = int(addr, base=16) else: addr = int(addr) except: QMessageBox.warning(self.main_ui.window, '错误', '请输入正确的地址, 地址必须是u16类型') return value = self.lnt_servo_addr_value.text().strip() try: value = float(value) except: QMessageBox.warning(self.main_ui.window, '错误', '请输入正确的值, 值必须是float32类型') return text = self.combobox_servo.currentText() if text == 'axis-all': QMessageBox.warning(self.main_ui.window, '警告', '请选择其中一个电机,不能选择所有电机') return else: servo_id = int(text.split('-')[-1]) tmp = '你确定要设置电机{}的地址{}的值为{}吗?'.format(servo_id, addr, value) if QMessageBox.question(self.main_ui.window, '警告', tmp) == QMessageBox.Yes: item = { 'cmd': 'set_servo_addr_16', 'kwargs': { 'servo_id': servo_id, 'addr': addr, 'value': value } } self.handler.put_cmd_que(item) except Exception as e: print(e) def set_servo_addr_32(self, event): try: addr = self.lnt_servo_addr.text().strip() try: if addr.lower().startswith('0x'): addr = int(addr, base=16) else: addr = int(addr) except: QMessageBox.warning(self.main_ui.window, '错误', '请输入正确的地址, 地址必须是u16类型') return value = self.lnt_servo_addr_value.text().strip() try: value = float(value) except: QMessageBox.warning(self.main_ui.window, '错误', '请输入正确的值, 值必须是float32类型') return text = self.combobox_servo.currentText() if text == 'axis-all': QMessageBox.warning(self.main_ui.window, '警告', '请选择其中一个电机,不能选择所有电机') return else: servo_id = int(text.split('-')[-1]) tmp = '你确定要设置电机{}的地址{}的值为{}吗?'.format(servo_id, addr, value) if QMessageBox.question(self.main_ui.window, '警告', tmp) == QMessageBox.Yes: item = { 'cmd': 'set_servo_addr_32', 'kwargs': { 'servo_id': servo_id, 'addr': addr, 'value': value } } self.handler.put_cmd_que(item) except Exception as e: print(e) def set_servo_zero(self, event): try: text = self.combobox_servo.currentText() if text == 'axis-all': servo_id = 8 else: servo_id = int(text.split('-')[-1]) if servo_id == 8: tmp = '你确定要设置所有电机的零点吗?' else: tmp = '你确定要设置电机{}的零点吗?'.format(servo_id) if QMessageBox.question(self.main_ui.window, '警告', tmp) == QMessageBox.Yes: item = { 'cmd': 'set_servo_zero', 'kwargs': { 'servo_id': servo_id } } self.handler.put_cmd_que(item) except Exception as e: print(e) def set_servo_attach(self, event): try: text = self.combobox_servo.currentText() if text == 'axis-all': servo_id = 8 else: servo_id = int(text.split('-')[-1]) item = { 'cmd': 'set_servo_attach', 'kwargs': { 'servo_id': servo_id } } self.handler.put_cmd_que(item) item = { 'cmd': 'motion_enable', 'kwargs': { 'servo_id': servo_id, 'enable': True } } self.handler.put_cmd_que(item) item = {'cmd': 'set_state', 'kwargs': {'state': 0}} self.handler.put_cmd_que(item) except Exception as e: print(e) def set_servo_detach(self, event): try: text = self.combobox_servo.currentText() if text == 'axis-all': servo_id = 8 else: servo_id = int(text.split('-')[-1]) if servo_id == 8: tmp = '你确定要解锁所有电机吗?' else: tmp = '你确定要解锁电机{}吗?'.format(servo_id) if QMessageBox.question(self.main_ui.window, '警告', tmp) == QMessageBox.Yes: item = { 'cmd': 'set_servo_detach', 'kwargs': { 'servo_id': servo_id } } self.handler.put_cmd_que(item) except Exception as e: print(e) def motion_enable(self, event): self._motion_enable(True) def motion_disable(self, event): self._motion_enable(False) def _motion_enable(self, enable=True): try: text = self.combobox_servo.currentText() if text == 'axis-all': servo_id = 8 else: servo_id = int(text.split('-')[-1]) item = { 'cmd': 'motion_enable', 'kwargs': { 'servo_id': servo_id, 'enable': enable } } self.handler.put_cmd_que(item) except Exception as e: print(e) def set_state(self, event): try: text = self.combobox_state.currentText() if text == 'sport': state = 0 elif text == 'pause': state = 3 elif text == 'stop': state = 4 else: return if state in [0, 3, 4]: item = {'cmd': 'set_state', 'kwargs': {'state': state}} self.handler.put_cmd_que(item) except Exception as e: print(e) def set_disable(self, disable): try: self.btn_stop.setDisabled(disable) self.btn_clean.setDisabled(disable) self.btn_reset.setDisabled(disable) self.btn_get_servo_dbmsg.setDisabled(disable) self.combobox_servo.setDisabled(disable) self.combobox_state.setDisabled(disable) self.btn_motion_enable.setDisabled(disable) self.btn_motion_disable.setDisabled(disable) self.btn_servo_attach.setDisabled(disable) self.btn_servo_detach.setDisabled(disable) self.btn_set_state.setDisabled(disable) self.lnt_servo_addr.setDisabled(disable) self.lnt_servo_addr_value.setDisabled(disable) self.btn_get_servo_addr16.setDisabled(disable) self.btn_set_servo_addr16.setDisabled(disable) self.btn_get_servo_addr32.setDisabled(disable) self.btn_set_servo_addr32.setDisabled(disable) self.btn_set_servo_zero.setDisabled(disable) self.slider_speed.setDisabled(disable) self.spinbox_speed.setDisabled(disable) self.slider_acc.setDisabled(disable) self.spinbox_acc.setDisabled(disable) self.axis_ui.set_disable(disable) self.cartesian_ui.set_disable(disable) except Exception as e: print(e)
class ConfigWidget(QWidget): """Configuration widget for managing calibre-comicvine config.""" def __init__(self): QWidget.__init__(self) self.layout = QGridLayout() self.layout.setSpacing(10) self.setLayout(self.layout) self.index = 0 self.api_key = QLineEdit(self) self.api_key.setText(PREFS['api_key']) self.add_labeled_widget('&API key:', self.api_key) # Worker threads is the maximum number of worker threads to spawn. # Restricted to 1+ self.worker_threads = QSpinBox(self) self.worker_threads.setMinimum(1) self.worker_threads.setValue(PREFS['worker_threads']) self.add_labeled_widget('&Worker threads:', self.worker_threads) # Request interval represents wait time between batches of requests. self.request_interval = QSpinBox(self) self.request_interval.setMinimum(0) self.request_interval.setValue(PREFS['request_interval']) self.add_labeled_widget('&Request interval (seconds):', self.request_interval) # Request batch is the maximum number of requests to run at a time. # Restricted to 1+ self.request_batch_size = QSpinBox(self) self.request_batch_size.setMinimum(1) self.request_batch_size.setValue(PREFS['request_batch_size']) self.add_labeled_widget('&Request batch size:', self.request_batch_size) # Retries is the number of times to retry if we get any error # from comicvine besides a rate limit error. self.retries = QSpinBox(self) self.retries.setMinimum(0) self.retries.setValue(PREFS['retries']) self.add_labeled_widget('&Retries:', self.retries) # Search volume limit is the max number of volumes to return from # a volume search. self.search_volume_limit = QSpinBox(self) self.search_volume_limit.setMinimum(10) self.search_volume_limit.setMaximum(10000) self.search_volume_limit.setSingleStep(10) self.search_volume_limit.setValue(PREFS['search_volume_limit']) self.add_labeled_widget('&search_volume_limit:', self.search_volume_limit) def add_labeled_widget(self, label_text, widget): """ Add a configuration widget, incrementing the index for the next widget. """ self.index += 1 label = QLabel(label_text) label.setBuddy(widget) self.layout.addWidget(label, self.index, 0) self.layout.addWidget(widget, self.index, 1) def save_settings(self): """Apply new settings value.""" PREFS['api_key'] = unicode(self.api_key.text()) PREFS['worker_threads'] = self.worker_threads.value() PREFS['request_interval'] = self.request_interval.value() PREFS['request_batch_size'] = self.request_batch_size.value() PREFS['retries'] = self.retries.value() PREFS['search_volume_limit'] = self.search_volume_limit.value()
class TwoPlots(QWidget): ''' Widget to plot 2 curves x(t) & y(t), or Vx(t) & Vy(t)''' Ylabels = { "position": ("X [pixel]", "Y [pixel]"), "velocity": ("VX [pixel/s]", "VY [pixel/s]"), "position_mm": ("X [mm]", "Y [mm]"), "velocity_mm": ("VX [mm/s]", "VY [mm/s]") } CurveLabels = { "position": ("X(t) [{}]", "Y(t) [{}]"), "velocity": ("VX(t) {}", "VY(t) {}") } def __init__(self, mainWindow, quantity): # appel du constructeur de la classe de base : QWidget.__init__(self, mainWindow) self.mw = mainWindow # la fenêtre de l'application principale # Attributs (objets persistants) self.__quantity = quantity # "position" or "velocity" self.__data1 = None # data for the first plot self.__data2 = None # data for tthe second self.__figure = None # figure tracé self.__axes1 = None # système d'axes tracé 1 self.__axes2 = None # système d'axes tracé 2 self.__canvas = None # pour le tracé matplot.lib self.__toolbar = None # barre d'outils tracé self.__time = None # abcissa values for plot self.__xlim = None # xmin, xmay tracé self.__xlabel = None # étiquette axe X (n° image ou temps [s]) self.__ylim1 = None # ymin, ymax tracé x(t) self.__ylim2 = None # ymin, ymax tracé y(t) self.btn_imageSize = QRadioButton("ImageSize", self) self.btn_autoSize = QRadioButton("AutoSize", self) self.btn_smooth_Vx = QCheckBox("Lissage Vx", self) self.btn_smooth_Vy = QCheckBox("Lissage Vy", self) self.x_mav_nb_pts = QSpinBox(parent=self) # X velocity moving average self.y_mav_nb_pts = QSpinBox(parent=self) # Y velocity moving average self.__initUI() # Initialisation de l'interface utilisateur def __initUI(self): if self.__quantity == "position": for w in (self.btn_smooth_Vx, self.btn_smooth_Vy, self.x_mav_nb_pts, self.y_mav_nb_pts): w.setVisible(False) w.setEnabled(False) group = QButtonGroup(self) group.addButton(self.btn_imageSize) group.addButton(self.btn_autoSize) self.btn_imageSize.toggled.connect(self.__ImageSizePlotXYLim) self.btn_imageSize.setEnabled(False) texte = "Tracé avec les bornes min et max de l'image" self.btn_imageSize.setStatusTip(texte) self.btn_imageSize.setChecked(True) self.btn_autoSize.toggled.connect(self.__AutoSizePlotXYLim) self.btn_autoSize.setEnabled(False) texte = "Tracé avec les bornes min et max de la " texte += "trajectoire calculée" self.btn_autoSize.setStatusTip(texte) elif self.__quantity == "velocity": for w in (self.btn_imageSize, self.btn_autoSize): w.setVisible(False) w.setEnabled(False) self.btn_smooth_Vx.stateChanged.connect(self.__smooth_Vx_wanted) self.btn_smooth_Vy.stateChanged.connect(self.__smooth_Vy_wanted) self.x_mav_nb_pts.setEnabled(False) self.y_mav_nb_pts.setEnabled(False) self.x_mav_nb_pts.setRange(3, 100) self.y_mav_nb_pts.setRange(3, 100) self.x_mav_nb_pts.setSingleStep(2) self.y_mav_nb_pts.setSingleStep(2) self.x_mav_nb_pts.valueChanged.connect(self.__smooth_vX) self.y_mav_nb_pts.valueChanged.connect(self.__smooth_vY) vbox = QVBoxLayout() self.setLayout(vbox) # Ligne 1 : tracé de l'image self.setLayout(vbox) self.__figure = Figure() self.__axes1 = self.__figure.add_subplot(211) self.__axes2 = self.__figure.add_subplot(212) self.__figure.subplots_adjust(left=0.120, right=0.99, bottom=0.11, top=0.98) self.__canvas = FigureCanvas(self.__figure) self.__toolbar = NavigationToolbar(self.__canvas, self) #self.__toolbar.setMinimumWidth(450) vbox.addWidget(self.__canvas) hbox = QHBoxLayout() hbox.addWidget(self.__toolbar) hbox.addStretch() if self.__quantity == "position": hbox.addWidget(self.btn_imageSize) hbox.addWidget(self.btn_autoSize) elif self.__quantity == "velocity": vb = QVBoxLayout() hb = QHBoxLayout() hb.addWidget(self.btn_smooth_Vx) hb.addWidget(self.x_mav_nb_pts) vb.addLayout(hb) hb = QHBoxLayout() hb.addWidget(self.btn_smooth_Vy) hb.addWidget(self.y_mav_nb_pts) vb.addLayout(hb) hbox.addLayout(vb) vbox.addLayout(hbox) def reset(self): if self.__quantity == "velocity": for w in (self.btn_smooth_Vx, self.btn_smooth_Vy, self.x_mav_nb_pts, self.y_mav_nb_pts): w.setVisible(True) w.setEnabled(True) self.x_mav_nb_pts.setValue(self.x_mav_nb_pts.minimum()) self.y_mav_nb_pts.setValue(self.y_mav_nb_pts.minimum()) self.btn_smooth_Vx.setCheckState(Qt.Unchecked) self.btn_smooth_Vy.setCheckState(Qt.Unchecked) def __smooth_Vx_wanted(self, checked): if checked: self.x_mav_nb_pts.setEnabled(True) else: self.x_mav_nb_pts.setEnabled(False) self.Plot() def __smooth_Vy_wanted(self, checked): if checked: self.y_mav_nb_pts.setEnabled(True) else: self.y_mav_nb_pts.setEnabled(False) self.Plot() def __smooth_vX(self, nb_pts): if self.btn_smooth_Vx.isChecked(): self.Plot() else: pass def __smooth_vY(self, nb_pts): if self.btn_smooth_Vy.isChecked(): self.Plot() else: pass def __compute_velocity(self, U, plot_id): """Computes the velocity with the centered finite difference of order 1 : V[i] = (U[i+1] - U[i-1])/(T[i+1] - T[i-1]) for i in [1,N-1] V[0] = (U[1] - U[0])/(T[1] - T[0]) V[-1] = (U[-1] - U[-2])/(T[-1] - T[-2]) """ V = U.copy() T = self.__time V[0] = (U[1] - U[0]) / (T[1] - T[0]) V[-1] = (U[-1] - U[-2]) / (T[-1] - T[-2]) V[1:-1] = (U[2:] - U[:-2]) / (T[2:] - T[:-2]) if plot_id == "x": if self.btn_smooth_Vx.isChecked(): N = self.x_mav_nb_pts.value() V = self.__smooth_data(V, N) elif plot_id == "y": if self.btn_smooth_Vy.isChecked(): N = self.y_mav_nb_pts.value() V = self.__smooth_data(V, N) return V def __smooth_data(self, U, nb_pts): """Computes the nb_pts moving average on U.""" N = nb_pts V = U.copy() V.fill(np.nan) mav = deque(maxlen=N) # initialize the mav (moving average) for e in U[:N]: mav.append(e) # move! index, count = N // 2, 0 while count < V.shape[0] - N: V[index] = np.mean(mav) mav.append(U[N + count]) count += 1 index += 1 return V def Plot(self): target_pos = self.mw.target_pos if target_pos is None: return else: self.__data1, self.__data2, I = target_pos scale = self.mw.imageTab.pix_to_mm_coeff if self.__quantity == "position": self.btn_imageSize.setEnabled(True) self.btn_autoSize.setEnabled(True) elif self.__quantity == "velocity": pass # Effacement automatiqe si demandé à chaque nouveau tracé : if self.mw.flags["autoClearTraj"]: if self.__axes1 is not None: self.__axes1.clear() if self.__axes2 is not None: self.__axes2.clear() # Récupération du nom de l'alagorithme de traitement : algo = self.mw.imageTab.btn_algo.currentText() # Récupération de la valeur de FP (Frame per seconde) pour calcul # du pas de temps et des abscisses : deltaT = None if self.mw.imageTab.video_FPS is not None: deltaT = 1. / self.mw.imageTab.video_FPS self.__time = np.array(I) * deltaT self.__xlabel = "temps [s]" else: self.__time = np.array(I) self.__xlabel = "image #" if self.__quantity == "velocity": if deltaT is not None: self.__data1 = self.__compute_velocity(self.__data1, "x") self.__data2 = self.__compute_velocity(self.__data2, "y") if self.btn_smooth_Vx.isChecked(): N = self.x_mav_nb_pts.value() if self.btn_smooth_Vy.isChecked(): N = self.y_mav_nb_pts.value() self.__AutoSizePlotXYLim() else: self.__data1, self.__data2 = None, None self.mw.target_veloc = np.array([self.__data1, self.__data2]) else: self.__ImageSizePlotXYLim() if self.__data1 is None or self.__data2 is None: return curveLabelX, curveLabelY = TwoPlots.CurveLabels[self.__quantity] if self.__quantity == "position": Xlabel, Ylabel = curveLabelX.format(algo), curveLabelY.format(algo) else: Xlabel, Ylabel = curveLabelX.format(""), curveLabelY.format("") color = 'b' if self.mw.target_RGB is None else self.mw.target_RGB / 255 # tracé de courbe x(t) self.__axes1.plot(self.__time, self.__data1 * scale, color=color, marker='o', markersize=2, linewidth=.4, label=Xlabel) self.__axes1.grid(True) #self.__axes1.legend(fontsize=9, framealpha=0.7, # bbox_to_anchor=(-0.1, 1.1), loc='upper left') self.__axes1.legend(loc='best', fontsize=10) # tracé de courbe y(t) self.__axes2.plot(self.__time, self.__data2 * scale, color=color, marker='o', markersize=2, linewidth=.4, label=Ylabel) self.__axes2.grid(True) #self.__axes2.legend(fontsize=9, framealpha=0.7, # bbox_to_anchor=(1.1, 1.1), loc='upper right') self.__axes2.legend(loc='best', fontsize=10) self.__canvas.draw() def __AutoSizePlotXYLim(self): if self.mw.target_pos is None: return y1label, y2label = TwoPlots.Ylabels[self.__quantity] self.__xlim = np.array( [np.nanmin(self.__time), np.nanmax(self.__time)]) scale = self.mw.imageTab.pix_to_mm_coeff if not self.btn_smooth_Vx.isChecked(): self.__ylim1 = np.array( [np.nanmin(self.__data1), np.nanmax(self.__data1)]) * scale offset = (self.__ylim1[1] - self.__ylim1[0]) / 10 self.__ylim1 += np.array([-offset, offset]) else: #self.__ylim1 = self.__axes1.get_ylim() pass if not self.btn_smooth_Vy.isChecked(): self.__ylim2 = np.array( [np.nanmin(self.__data2), np.nanmax(self.__data2)]) * scale offset = (self.__ylim2[1] - self.__ylim2[0]) / 10 self.__ylim2 += np.array([-offset, offset]) else: #self.__ylim2 = self.__axes2.get_ylim() pass if self.mw.imageTab.valid_scale: y1label, y2label = TwoPlots.Ylabels[self.__quantity + "_mm"] self.__axes1.set_xlim(*self.__xlim) self.__axes2.set_xlim(*self.__xlim) self.__axes1.set_ylim(*self.__ylim1) self.__axes2.set_ylim(*self.__ylim2) self.__axes1.set_ylabel(y1label) self.__axes2.set_ylabel(y2label) self.__axes2.set_xlabel(self.__xlabel) self.__canvas.draw() def __ImageSizePlotXYLim(self): if self.mw.target_pos is None: return scale = self.mw.imageTab.pix_to_mm_coeff y1label, y2label = TwoPlots.Ylabels[self.__quantity] w, h = self.mw.imageTab.video_size self.__xlim = np.array( [np.nanmin(self.__time), np.nanmax(self.__time)]) self.__ylim1 = np.array([0, w - 1], dtype=float) * scale self.__ylim2 = np.array([0, h - 1], dtype=float) * scale if self.mw.imageTab.valid_scale: y1label, y2label = TwoPlots.Ylabels[self.__quantity + "_mm"] self.__axes1.set_xlim(*self.__xlim) self.__axes2.set_xlim(*self.__xlim) self.__axes1.set_ylim(*self.__ylim1) self.__axes2.set_ylim(*self.__ylim2) self.__axes1.set_ylabel(y1label) self.__axes2.set_ylabel(y2label) self.__axes2.set_xlabel(self.__xlabel) self.__canvas.draw() def ClearAxes(self): if self.__axes1 is not None: self.__axes1.clear() if self.__axes2 is not None: self.__axes2.clear() self.__canvas.draw()
class ImageDisplay(QWidget): video_infos = ['vidéo : {}','nb frames : {}','taille : {}','FPS : {}','durée : {:.2f} sec'] video_keys = ['videoname','nframes','size','fps','duration'] algo_traj = ['barycentre','minmax'] def __init__(self, mainWindow): # acall the base class constructor: super().__init__(mainWindow) self.mw = mainWindow # Attributs (objets persistants) self.img_lbl = QLabel(self) # to display the current image self.img_lbl.installEventFilter(self) # filter to catch evenements self.selectTargetRect = None # display a rectangle to show teh target color selection self.rubberBand = QRubberBand(QRubberBand.Line, self) # Boutons pour avancer/reculer self.btn_prev = QPushButton(QIcon("icones/go-prev.png"), "", self) self.btn_next = QPushButton(QIcon("icones/go-next.png"), "", self) self.btn_first = QPushButton(QIcon("icones/go-first.png"), "", self) self.btn_last = QPushButton(QIcon("icones/go-last.png"), "", self) self.btn_traj = QPushButton(QIcon("icones/extract.png"), "Extraire...", self) self.btn_clear = QPushButton(QIcon("icones/clear.png"), "Effacer courbes...", self) self.btn_exportCSV = QPushButton(QIcon("icones/exportCSV.png"), "Export CSV", self) self.btn_algo = QComboBox(self) self.image_index = QLabel(self) # widget QSpinBox self.images_step = QSpinBox(parent=self) self.images_firstRank = QSpinBox(parent=self) self.images_lastRank = QSpinBox(parent=self) # QLabel to display the target color self.target_color_label = QLabel("target color",parent=self) self.picked_color = QLabel(self) self.video_path = None # Chemin de la dernière vidéo self.images_dir = None # Dossier contenant les images self.__img_idx = None # Rang de l'image affichée self.img_path = None # nom du chemin de l'image courante self.nb_img = None # nombre d'images self.video_name = None # nom de la video ("aaaaaa.mp4") self.video_nframes = None # nombre d'images dans la video self.video_size = None # taille (width, height) des images self.video_FPS = None # nombre d'images par seconde self.video_duration = None # durée de la video en secondes self.videoLabels = [] # liste de QLabel contenant les infos vidéo self.dico_video = {} # dictionnaire des méta-données self.dico_unit = {} # dictionary "pixels", "mm" self.scale_pixel = None # nombre de pixels pour conversion en mm self.scale_mm = None # nbre de mm pour scale_pixel self.valid_scale = False # données de l'échelle valides ou pas self.pix_to_mm_coeff= 1. # le coefficient de converion pixels -> mm self.dicoScale = {} # dictionnaire des QWidget d'info scale self.lbl_epsilon = None # label epsilon self.epsi_spin = None # boite de choix de epsilon # créer l'onglet de visualisation image """ self.__initUI() self.scaleInfoVisible(False) self.__epsilonVisible(False) def __initUI(self): # Onglet "Visualisation images" vbox = QVBoxLayout() # Ligne 1 : extraction trajec self.picked_color.setFrameStyle(QFrame.StyledPanel | QFrame.Plain); line1 = QHBoxLayout() line1.addStretch(1) line1.addWidget(self.btn_algo) line1.addWidget(self.btn_traj) line1.addWidget(self.target_color_label) line1.addWidget(self.picked_color) line1.addWidget(self.btn_clear) line1.addWidget(self.btn_exportCSV) line1.addStretch(1) # Ligne 2 : infos video + visu image line2 = QHBoxLayout() # boîte d'infos sur la vidéo infoVBox = QVBoxLayout() for _ in ImageDisplay.video_infos: label = QLabel(self) label.setFrameStyle(QFrame.StyledPanel | QFrame.Plain); infoVBox.addWidget(label) self.videoLabels.append(label) infoVBox.addStretch() widget = QLabel("Conversion pixels -> mm", self) self.dicoScale['Pixels-mm'] = widget infoVBox.addWidget(widget) grid = QGridLayout() infoVBox.addLayout(grid) widget = QLabel("pixels ",self) self.dicoScale['pixels'] = widget grid.addWidget(widget,1,1) self.scale_pixel = QLineEdit(self) self.dicoScale['pixelsForMM'] = self.scale_pixel grid.addWidget(self.scale_pixel,1,2) widget = QLabel("millimètres ",self) self.dicoScale['millimeters'] = widget grid.addWidget(widget,2,1) self.scale_mm = QLineEdit(self) self.dicoScale['mmForPixels'] = self.scale_mm grid.addWidget(self.scale_mm,2,2) self.lbl_epsilon = QLabel("Epsilon ",self) grid.addWidget(self.lbl_epsilon,5,1) self.epsi_spin = QSpinBox(self) self.epsi_spin.setRange(1,50) self.epsi_spin.setSingleStep(1) self.epsi_spin.setValue(10) grid.addWidget(self.epsi_spin,5,2) infoVBox.addStretch() line2.addLayout(infoVBox) line2.addStretch(1) line2.addWidget(self.img_lbl) # le QLabel por afficher l'image line2.addStretch(1) # line 3 : navigation boutons self.image_index.setFrameStyle(QFrame.Panel | QFrame.Sunken) self.image_index.setText(" ") line3 = QHBoxLayout() line3.addStretch(1) line3.addWidget(self.btn_first) line3.addWidget(self.btn_prev) line3.addWidget(self.image_index) line3.addWidget(self.btn_next) line3.addWidget(self.btn_last) line3.addStretch(1) # line 4 : first , step, last image selection line4 = QHBoxLayout() line4.addStretch(1) line4.addWidget(self.images_firstRank) line4.addWidget(self.images_step) line4.addWidget(self.images_lastRank) line4.addStretch(1) vbox.addLayout(line1) vbox.addStretch(1) vbox.addLayout(line2) vbox.addStretch(1) vbox.addLayout(line3) vbox.addLayout(line4) self.setLayout(vbox) self.buttonsState() self.__buttonsConnect() self.__setVideoLabelVisible(False) def __setVideoLabelVisible(self, state): for label in self.videoLabels: label.setVisible(state) def __buttonsConnect(self): self.btn_traj.clicked.connect(self.extract_trajectoire) self.btn_clear.clicked.connect(self.mw.clearPlots) self.btn_exportCSV.clicked.connect(self.mw.ExportCSV) self.btn_prev.clicked.connect(self.prev_image) self.btn_next.clicked.connect(self.next_image) self.btn_first.clicked.connect(self.first_image) self.btn_last.clicked.connect(self.last_image) self.images_step.valueChanged.connect(self.__step_changed) self.images_firstRank.valueChanged.connect(self.__first_rank_changed) self.images_lastRank.valueChanged.connect(self.__last_rank_changed) def buttonsState(self, importCSV=False): self.btn_traj.setEnabled(False) self.picked_color.setText("X") self.picked_color.setEnabled(False) self.btn_traj.setStatusTip('Extrait la trajectoire de la cible'+ 'dont la couleur a été choisie') self.target_color_label.setEnabled(False) self.picked_color.setStyleSheet('background-color : rgb(255, 255, 255)') self.btn_clear.setEnabled(False) self.btn_clear.setStatusTip('Nettoye tous les tracés des onglets'+ '<trajectoire> et <X(t), Y(t)>') self.btn_exportCSV.setEnabled(False) texte = "Export des données dans un fichier CSV" self.btn_exportCSV.setStatusTip(texte) if not importCSV: self.btn_algo.addItems(ImageDisplay.algo_traj) self.btn_algo.setEnabled(False) self.btn_prev.setEnabled(False) self.btn_prev.setStatusTip("affiche l'image précédente") self.btn_next.setEnabled(False) self.btn_next.setStatusTip("affiche l'image suivante") self.btn_first.setEnabled(False) self.btn_first.setStatusTip("affiche la première image à traiter") self.btn_last.setEnabled(False) self.btn_last.setStatusTip("affiche la dernière image à traiter") # SpinBoxes parameters: self.images_step.setRange(1,1000) self.images_step.setSingleStep(1) self.images_step.setValue(1) self.images_step.setPrefix("step: ") self.images_step.setEnabled(False) self.images_step.setStatusTip("Fixe le pas pour passer d'une image à l'autre") self.images_firstRank.setRange(1,1000) self.images_firstRank.setSingleStep(1) self.images_firstRank.setValue(1) self.images_firstRank.setPrefix("first: ") self.images_firstRank.setEnabled(False) self.images_firstRank.setStatusTip("Fixe le rang de la première image à traiter") self.images_lastRank.setRange(1,10000) self.images_lastRank.setSingleStep(1) self.images_lastRank.setValue(1) self.images_lastRank.setPrefix("last: ") self.images_lastRank.setEnabled(False) self.images_lastRank.setStatusTip("Fixe le rang de la dernière image à traiter") def __first_rank_changed(self, val): if self.img_idx is None: return if self.img_idx < val: self.img_idx = val self.show_image() def __last_rank_changed(self, val): if self.img_idx is None: return if self.img_idx > val: self.img_idx = val self.show_image() def __step_changed(self, val): if self.img_idx is None: return def setTextInfoVideoGrid(self): for field, name, key in zip(self.videoLabels, ImageDisplay.video_infos, ImageDisplay.video_keys): mess = name.format(self.dico_video.get(key,'?')) field.setText(mess) self.__setVideoLabelVisible(True) def scaleInfoVisible(self, state): for widget in self.dicoScale.values(): widget.setVisible(state) def __epsilonVisible(self, state): self.lbl_epsilon.setVisible(state) self.epsi_spin.setVisible(state) def open_video(self): '''Lance un sélecteur de fichier pour choisir la vidéo à traiter.''' fname = QFileDialog.getOpenFileName(None, 'Choisir une vidéo', self.mw.cur_dir, 'Fichier vidéo (*.mp4)') if fname[0] != '' : # un fichier vidéo a été chosi : vp = fname[0] if self.video_path == vp : name = os.path.basename(vp) rep = QMessageBox.question(self, # widget parent de QMessageBox 'Question', # texte du bandeau de la fenêtre 'Voulez-vous recharger le fichier video {} ?'.format(name), QMessageBox.Yes | QMessageBox.No, # afficher les boutons Yes et No QMessageBox.No) # bouton No sélectionné par défaut if rep == QMessageBox.No: return # fichier vidéo à traiter => faire le split des images : self.video_path = vp self.extract_images_from_video() def load_images_from_directory(self): '''Charge les images '*.png' contenue dans le répertoire des images choisi avec un sélecteur graphique.''' # Choix du répertoire avec un sélecteur de fichier : dname = QFileDialog.getExistingDirectory(None, 'Choisir un dossier images', self.mw.image_dir) if dname != '' : # un répertoire valide a été choisi : self.video_path = None self.images_dir = dname + "/" try: # Lecture du fichier ascii des méta-données with open(self.images_dir + "metaData.txt", "r") as F: data = F.read() exec('self.dico_video = '+data) except: rep = QMessageBox.critical( None, # widget parent de QMessageBox 'Erreur', # bandeau de la fenêtre 'Pas de fichier de méta-données dans le répertoire'+\ ' <{}>'.format(os.path.basename(dname)), QMessageBox.Ok) return print("méta données :", self.dico_video) self.parse_meta_data() self.setTextInfoVideoGrid() # Mettre à jour l'application avec les nouvelles images chargées : self.update_images() def extract_trajectoire(self): '''Méthode utilisée pour extraire la trajectoire du centre de la cible pour toutes les images de la vidéo.''' # Récupérer l'algorithme de calcul du centre de la cible : algo = self.btn_algo.currentText() # Définition de la liste dans laquelle on va récupérer les coordonnées # du centre cible pour toutes les images : target_pos = [] # Création d'un objet ProgressBar qui va lancer le travail # d'extraction de la cible dans les images tout en affichant une # barre d'avancement : first = self.images_firstRank.value() last = self.images_lastRank.value() step = self.images_step.value() last = last - (last - first) % step first_last_step = (first, last, step) pg = ProgressBar(self.images_dir, self) pg.configure_for_target_extraction(self.mw.target_RGB, algo, self.epsi_spin.value(), target_pos, first_last_step) ret = pg.exec_() # lance la barre et le travail d'extraction... print("retour de pg.exec_() :",ret) if ret != 0: self.mw.target_pos = None return target_pos = np.array(target_pos) width, height = self.video_size # l'axe verticale est retourné et decalé: target_pos[1] = height - target_pos[1] self.scale_XY() self.mw.target_pos = target_pos self.display_plots() # remettre le bouton extraire_trajectoire disabled: self.btn_exportCSV.setEnabled(True) def display_plots(self): self.mw.clearPlots() # Plot trajectory (X(t), Y(t)) : self.mw.onePlot.setEnabled(True) self.mw.onePlot.Plot() # Plot curves X(t) and Y(t) self.mw.twoPlots_xy.setEnabled(True) self.mw.tabs.setCurrentWidget(self.mw.twoPlots_xy) self.mw.twoPlots_xy.Plot() # Plot curves VX(t) and VY(t) self.mw.twoPlots_VxVy.setEnabled(True) self.mw.tabs.setCurrentWidget(self.mw.twoPlots_xy) self.mw.twoPlots_VxVy.Plot() def extract_images_from_video(self) : # name of the video file without path and suffix: videoname = os.path.basename(self.video_path)[:-4] # directory where to put extracted iamges: self.images_dir = self.mw.image_dir + videoname + "/" if os.path.isdir(self.images_dir) : print("Effacement de tous les fichiers de '{}'"\ .format(self.images_dir)) for fn in os.listdir(self.images_dir) : os.remove(self.images_dir+fn) else : os.mkdir(self.images_dir) video = cv2.VideoCapture(self.video_path) self.dico_video['nframes'] = int(video.get(cv2.CAP_PROP_FRAME_COUNT)) self.dico_video['size'] = (int(video.get(cv2.CAP_PROP_FRAME_WIDTH)), int(video.get(cv2.CAP_PROP_FRAME_HEIGHT))) self.dico_video['fps'] = int(video.get(cv2.CAP_PROP_FPS)) self.dico_video['duration'] = video.get(cv2.CAP_PROP_FRAME_COUNT)/video.get(cv2.CAP_PROP_FPS) self.dico_video['videoname'] = os.path.basename(self.video_path) self.parse_meta_data() self.dico_video["videoname"] = videoname+".mp4" self.setTextInfoVideoGrid() # Création d'un objet ProgressBar qui va lancer le travail # d'extraction des images tout en affichant une barre d'avancement : pg = ProgressBar(self.images_dir, self) pg.configure_for_video_extraction(video, self.mw.image_fmt) ret = pg.exec_() print("retour de pg.exec_() :", ret) if ret != 0: return # MAJ de la liste des fichiers images : self.update_images() # écriture des méta-data dans le fichier 'nom_video'.info with open(self.mw.image_dir+videoname+"/metaData.txt", "w") as F: F.write(str(self.dico_video)) def computeTargetColor(self, draw_selection=False): col_min,row_min,col_max,row_max = self.selection.getCoords() print("Pixels selectionnés : lignes [{},{}] colonnes [{},{}]".\ format(row_min, row_max, col_min, col_max)) tab = imread(self.img_path) self.target_pix = tab[row_min:row_max+1, col_min:col_max+1, :] R = round(self.target_pix[:,:,0].mean()) G = round(self.target_pix[:,:,1].mean()) B = round(self.target_pix[:,:,2].mean()) self.mw.target_RGB = np.array([R, G, B], dtype=int) print("RGB sélection dans <{}> :".format(os.path.basename(self.img_path)), self.mw.target_RGB) draw_selection = self.mw.flags["drawTargetSelection"] if draw_selection: self.show_image() print("drawTargetSelection") #if self.selectTargetRect is not None : del self.selectTargetRect # create painter instance with pixmap self.selectTargetRect = QPainter(self.img_lbl.pixmap()) # set rectangle color and thickness self.penRectangle = QPen(QColor(0,0,0)) self.penRectangle.setWidth(2) # draw rectangle on painter self.selectTargetRect.begin(self) self.selectTargetRect.setPen(self.penRectangle) self.selectTargetRect.drawRect(col_min,row_min, col_max-col_min,row_max-row_min) self.selectTargetRect.setOpacity(0.1) self.selectTargetRect.end() #self.show_image() self.btn_traj.setEnabled(True) self.btn_algo.setEnabled(True) self.btn_clear.setEnabled(True) self.target_color_label.setEnabled(True) self.picked_color.setStyleSheet('background-color : rgb({},{},{})'.format(R, G, B)) @property def img_idx(self): return self.__img_idx @img_idx.setter def img_idx(self, index): self.__img_idx = index self.image_index.setText(str(index)) def update_images(self) : '''Méthode à exécuter quand de nouvelles images sont apparues après une extraction d'images depuis une vidéo par exemple. Cette méthode : - met à jour des attributs qui dépendant de la liste des images, - met à jour l'état de certains boutons - fait afficher la première image et un message d'information.''' if self.images_dir is None : self.__img_idx = None #self.btn_prev.setEnabled(False) self.btn_prev.setStatusTip("") #self.btn_next.setEnabled(False) self.btn_next.setStatusTip("") self.images_step.setEnabled(False) self.images_firstRank.setEnabled(False) self.images_lastRank.setEnabled(False) else : self.buttonsState() self.mw.clearPlots() self.mw.twoPlots_VxVy.reset() # liste des noms des fichiers image pour avoir leur nombre : file_names = [ f for f in os.listdir(self.images_dir) \ if f.endswith('.png')] file_names.sort() self.nb_img = len(file_names) # Update spinBox: self.images_step.setEnabled(True) self.images_firstRank.setEnabled(True) self.images_lastRank.setEnabled(True) self.images_firstRank.setValue(1) self.images_step.setValue(1) self.images_lastRank.setValue(self.nb_img) self.images_firstRank.setMaximum(self.nb_img) self.images_lastRank.setMaximum(self.nb_img) self.images_step.setMaximum(self.nb_img) # MAJ des boutons prev et next self.img_idx = self.images_firstRank.value() self.btn_prev.setEnabled(True) self.btn_first.setEnabled(True) self.btn_prev.setStatusTip("charge l'image précédente") self.btn_next.setEnabled(True) self.btn_last.setEnabled(True) self.btn_next.setStatusTip("afficher "+self.mw.image_fmt.format(\ self.img_idx+self.images_step.value())) self.show_image() self.scaleInfoVisible(True) self.__epsilonVisible(True) self.mw.tabs.setCurrentWidget(self) self.scale_mm.clear() self.scale_mm.setText("???") self.scale_pixel.clear() try: text = str(self.video_size[1]) except: text = "" self.scale_pixel.setText(text) self.mw.twoPlots_VxVy.reset() if self.mw.flags["displayInfo"]: rep = QMessageBox.information( None, # widget parent de QMessageBox 'Information', # bandeau de la fenêtre 'Vous pouvez maintenant sélectionner une couleur de cible'+\ 'avec la souris...', QMessageBox.Ok) def show_image(self): '''Affiche l'image dont le numéro est donné par l'attribut 'img_idx'.''' if self.img_idx is None : self.img_path = '' else : self.img_path = self.images_dir+self.mw.image_fmt.format(self.img_idx) pixmap = QPixmap(self.img_path) self.img_lbl.setPixmap(pixmap) self.img_lbl.setStatusTip(os.path.basename(self.img_path)) def first_image(self) : if self.img_idx == None : return self.img_idx = self.images_firstRank.value() self.mw.statusBar().showMessage("") self.show_image() def prev_image(self) : if self.img_idx == None : return if self.img_idx >= self.images_firstRank.value() + self.images_step.value(): self.img_idx -= self.images_step.value() self.mw.statusBar().showMessage("") self.show_image() def last_image(self) : if self.img_idx == None : return self.img_idx = self.images_lastRank.value() # rank of last image to process self.mw.statusBar().showMessage("") self.show_image() def next_image(self) : if self.img_idx == None : return if self.img_idx <= self.images_lastRank.value()-self.images_step.value(): self.img_idx += self.images_step.value() self.mw.statusBar().showMessage("") self.show_image() def parse_meta_data(self): self.video_nframes = self.dico_video.get('nframes', None) self.video_size = self.dico_video.get('size', None) self.video_FPS = self.dico_video.get('fps', None) self.video_duration = self.dico_video.get('duration', None) self.video_name = self.dico_video.get('videoname',"none.mp4") if self.mw.flags["debug"]: info= " nb images : {},\n taille image : {},\n FPS : {} image/sec,\n durée : {.2f} sec." info = info.format(self.video_nframes, self.video_size, self.video_FPS, self.video_duration) QMessageBox.about(None, # widget parent de QMessageBox 'Informations video {}'.format(self.video_name), info) def eventFilter(self, object, event): if object == self.img_lbl : if event.type() == QEvent.MouseButtonPress: self.mousePressInLabel(event) return True elif event.type() == QEvent.MouseButtonRelease: self.mouseReleaseInLabel(event) return True elif event.type() == QEvent.MouseMove: self.mouseMoveInLabel(event) return True return False def mousePressInLabel(self, event): if event.button() == Qt.LeftButton: self.pt1 = event.pos() self.pt1_rect = self.img_lbl.mapTo(self, self.pt1) print("\nCoord. pt1 image :",self.pt1) self.rubberBand.setGeometry(QRect(self.pt1_rect, QSize())) self.rubberBand.show() self.mw.statusBar().showMessage('sélection en cours....') def mouseMoveInLabel(self, event): if not self.pt1.isNull(): pt = self.img_lbl.mapTo(self,event.pos()) self.rubberBand.setGeometry(QRect(self.pt1_rect, pt).normalized()) def mouseReleaseInLabel(self, event): if event.button() == Qt.LeftButton: self.pt2 = event.pos() print("Coord. pt2 image :", self.pt2) self.rubberBand.hide() self.selection = QRect(self.pt1, self.pt2).normalized() print(self.selection) self.computeTargetColor() def scale_XY(self): self.valid_scale = False self.pix_to_mm_coeff = 1. try : pixels = float(self.scale_pixel.text()) mm = float(self.scale_mm.text()) except : if self.mw.flags["displayInfo"]: info = 'Les données de conversion Pixels -> mm n\'ont pas été ' info += 'complétées.. les ordonnées des tracés seront en pixels.' rep = QMessageBox.information(None, # parent de QMessageBox 'Info', # bandeau de la fenêtre info, QMessageBox.Ok) return self.valid_scale = True self.pix_to_mm_coeff = mm/pixels print("valid scale : ", self.pix_to_mm_coeff)
class GUI(QtWidgets.QMainWindow): def __init__(self): '''Asetetaan muuttujille alkuarvoja ohjelman suorittamiseksi''' super().__init__() self.title = "Lujuusanalysaattori" self.left = 200 self.top = 200 self.width = 1300 self.height = 700 self.palkin_default_pituus = 5 self.square_size = 10 self.ikkuna() self.button_height = 75 self.button_width = 150 self.button_separation = 25 self.x = 0 self.y = 0 self.palkin_leveys = 700 self.palkin_korkeus = 75 self.palkin_keskipiste = 650 self.palkin_paatypiste = 1000 self.yksikko_arvo = 0 self.voima = 20 self.maks_jannitys = "-" self.asteikko_teksti = QGraphicsSimpleTextItem() '''Lisää QGraphicsScenen ruudukon piirtämistä varten''' self.scene = QtWidgets.QGraphicsScene() self.scene.setSceneRect(0, -20, self.width - 200, self.height - 100) '''Suoritetaan lukuisia metodeja, jolla ohjelma "alustetaan"''' self.aloita_simulaatio() self.simulaatioikkuna() self.simulaatio_nappi() self.materiaali_valikko() self.uusi_palkki_nappi() self.lisaa_tuki_nappi() self.lisaa_ulkoinen_voima_nappi() self.poista_ulkoinen_voima_nappi() self.vaihda_tuki_nappi() Ominaisuudet.alkuarvot(self) self.lisaa_palkki() self.palkin_pituus_valikko() self.yksikko_pituus() self.asteikko() self.lisaa_asteikko_arvo() self.asteikko_teksti.hide() self.tulos_teksti() self.lisaa_seina_tuki() self.lisaa_tuki_alhaalta() self.ulkoinen_voima_valikko() self.ulkoinen_voima_nuoli_alatuki() self.ulkoinen_voima_nuoli_seinatuki() Ominaisuudet.alkuarvot(self) '''Asetetaan tietyille napeille tietty näkyvyys''' self.lisaa_tuki.setEnabled(False) self.simuloi.setEnabled(False) self.show() def ikkuna(self): '''Tekee ohjelman pääikkunan''' self.setGeometry(self.left, self.top, self.width, self.height) self.setWindowTitle('Lujuusanalysaattori') self.horizontal = QtWidgets.QHBoxLayout() '''Luo menubarin''' self.uusiAction = QAction("Uusi simulaatio", self) self.uusiAction.setStatusTip("Luo uusi rakenne") self.uusiAction.triggered.connect(self.uusi_rakenne) self.uusiAction.setEnabled(True) self.uusiAction.setShortcut("Ctrl+N") self.tallennaAction = QAction("Tallenna simulaatio", self) self.tallennaAction.setStatusTip("Tallenna simulaatio") self.tallennaAction.triggered.connect(self.tallenna_rakenne) self.tallennaAction.setEnabled(False) self.tallennaAction.setShortcut("Ctrl+S") self.avaaAction = QAction("Lataa simulaatio", self) self.avaaAction.setStatusTip("Lataa simulaatio tiedostosta") self.avaaAction.triggered.connect(self.lataa_tallennettu_rakenne) self.avaaAction.setShortcut("Ctrl+O") self.exitAction = QAction("Exit", self) self.exitAction.setToolTip("Lopeta ohjelma") self.exitAction.triggered.connect(self.close_application) self.exitAction.setShortcut("Ctrl+E") self.statusBar() mainMenu = self.menuBar() fileMenu = mainMenu.addMenu('&File') aboutMenu = mainMenu.addMenu('&About') fileMenu.addAction(self.uusiAction) fileMenu.addAction(self.avaaAction) fileMenu.addAction(self.tallennaAction) fileMenu.addAction(self.exitAction) def tallenna_rakenne(self): '''Hoitaa rakenteen tallentamisen''' tallennus = Tallennin.tallenin(self) if tallennus == True: '''Kerrotaan käyttäjälle, että tallennus onnistui''' msgBox = QMessageBox() msgBox.setText("Tallennus onnistui!") msgBox.setWindowTitle("Onnistunut Tallennus") msgBox.setMinimumWidth(50) msgBox.addButton(QPushButton('OK'), QMessageBox.NoRole) msgBox.exec_() def lataa_tallennettu_rakenne(self): '''Metodi avaa QFileDialog ikkunan, josta käyttäjä valitsee tiedoston, jossa aiemmin tallennettu rakenne sijaitsee. Vain .txt -tiedostot ovat ladattavissa ''' options = QFileDialog.Options() options |= QFileDialog.DontUseNativeDialog tiedosto, _ = QFileDialog.getOpenFileName(self, "Valitse tiedosto", "", "txt Files (*.txt)", options=options) lataus = Lataaja.lataaja(self, tiedosto) if lataus == False: return if lataus == True: self.uusi_rakenne() Lataaja.lataaja(self, tiedosto) tuen_tyyppi = Ominaisuudet.palauta_tuen_tyyppi(self) '''Jos tuki on seinästä, piirretään sitä vastaava grafiikka''' if tuen_tyyppi == 0: self.nayta_seina_tuki() self.gradient_seina_tuki() '''Jos tuki on alhaalta, piirretään sitä vastaava grafiikka''' if tuen_tyyppi == 1: self.nayta_tuki_alhaalta() self.gradient_alatuki() if tuen_tyyppi != 2: self.vaihda_tuki.show() self.lisaa_tuki.hide() '''Jos ulkoinen voima on asetettu, piirretään se''' ulkoinen_voima = int( Ominaisuudet.onko_ulkoinen_voima_asetettu(self)) if ulkoinen_voima == 1: self.nayta_ulkoinen_voima() self.nayta_palkki() Laskin.laskin(self) self.paivita_tulos_teksti() self.tulos.show() self.sp.setValue(float(Ominaisuudet.palauta_palkin_pituus(self))) self.uusiAction.setEnabled(True) self.simuloi.setEnabled(True) '''Kerrotaan käyttäjälle, että kaikki onnistui''' msgBox = QMessageBox() msgBox.setText("Lataus onnistui!") msgBox.setWindowTitle("Onnistunut lataus") msgBox.addButton(QPushButton('OK'), QMessageBox.NoRole) msgBox.exec_() def aloita_simulaatio(self): '''Aloittaa simulaation''' self.setCentralWidget(QtWidgets.QWidget()) self.horizontal = QtWidgets.QHBoxLayout() self.centralWidget().setLayout(self.horizontal) def simulaatioikkuna(self): '''lisää view näyttämistä varten''' self.view = QtWidgets.QGraphicsView(self.scene, self) self.view.adjustSize() self.view.show() self.horizontal.addWidget(self.view) def uusi_palkki_nappi(self): '''Luo Uusi palkki -napin''' self.uusi_palkki = QPushButton('Uusi palkki') self.uusi_palkki.setToolTip("Lisää uusi palkki") self.uusi_palkki.move(0, 0) self.uusi_palkki.resize(self.button_width, self.button_height) self.uusi_palkki.font = QtGui.QFont() self.uusi_palkki.font.setPointSize(12) self.uusi_palkki.setFont(self.uusi_palkki.font) self.uusi_palkki.setEnabled(True) self.scene.addWidget(self.uusi_palkki) self.uusi_palkki.clicked.connect(self.nayta_palkki) def nayta_palkki(self): '''Näyttää kaikki palkkiin liittyvät komponentit sekä asettaa uusi palkki -napin toimimattomaksi''' self.rect.show() self.palkin_pituus.show() self.sp.show() self.yksikko.show() self.asteikko_teksti.show() self.line.show() self.nuoli_1.show() self.nuoli_2.show() self.uusi_palkki.setEnabled(False) self.lisaa_tuki.setEnabled(True) self.materiaali_valinta.setEnabled(True) def lisaa_palkki(self): '''lisää palkin''' self.rect = QGraphicsRectItem(300, 200, self.palkin_leveys, self.palkin_korkeus) self.rect.setBrush(QBrush(4)) self.scene.addItem(self.rect) self.rect.hide() self.lisaa_tuki.setEnabled(True) '''Aina kun on uusi palkki luotu, voidaan aloittaa simulaatio alusta''' self.uusiAction.setEnabled(True) def lisaa_tuki_nappi(self): '''Luo Lisää tuki -napin''' self.lisaa_tuki = QPushButton("Lisää tuki") self.lisaa_tuki.setToolTip("Lisää tuki") self.lisaa_tuki.move(0, self.button_height + self.button_separation) self.lisaa_tuki.resize(self.button_width, self.button_height) self.lisaa_tuki.font = QtGui.QFont() self.lisaa_tuki.font.setPointSize(12) self.lisaa_tuki.setFont(self.lisaa_tuki.font) self.lisaa_tuki.setEnabled(False) self.lisaa_tuki.clicked.connect(self.valitse_tuki) self.scene.addWidget(self.lisaa_tuki) def vaihda_tuki_nappi(self): '''Luo vaihda tuki -napin''' self.vaihda_tuki = QPushButton("Vaihda tuki") self.vaihda_tuki.setToolTip("Vaihda tuki") self.vaihda_tuki.move(0, self.button_height + self.button_separation) self.vaihda_tuki.resize(self.button_width, self.button_height) self.vaihda_tuki.setFont(self.lisaa_tuki.font) self.vaihda_tuki.clicked.connect(self.valitse_tuki) self.scene.addWidget(self.vaihda_tuki) self.vaihda_tuki.hide() def valitse_tuki(self): '''Tuen valinta. Jos tuki on seinästä (tyyppi = 0), kysytään halutaanko vaihtaa. Jos haluaa muutetaan tuen grafiikka ja arvo''' if Ominaisuudet.palauta_tuen_tyyppi(self) == 0: msgBox = QMessageBox() msgBox.setText("Haluatko vaihtaa tuen tyyppiä?") msgBox.addButton(QPushButton('En'), QMessageBox.NoRole) msgBox.addButton(QPushButton('Kyllä'), QMessageBox.YesRole) vastaus = msgBox.exec_() self.rect.setBrush(QBrush(4)) if vastaus == 1: self.viiva_1.hide() self.viiva_2.hide() self.viiva_3.hide() self.viiva_4.hide() self.nayta_tuki_alhaalta() if int(Ominaisuudet.onko_ulkoinen_voima_asetettu(self)) == 1: self.viiva.hide() self.nuoli_3.hide() self.viiva_5.show() self.nuoli_6.show() Ominaisuudet.tuki(self, 1) return '''Jos tuki on alhaalta (tyyppi = 1), kysytään halutaanko vaihtaa. Jos haluaa muutetaan tuen grafiikka ja arvo''' if Ominaisuudet.palauta_tuen_tyyppi(self) == 1: msgBox = QMessageBox() msgBox.setText("Haluatko vaihtaa tuen tyyppiä?") msgBox.addButton(QPushButton('Kyllä'), QMessageBox.YesRole) msgBox.addButton(QPushButton('En'), QMessageBox.NoRole) vastaus = msgBox.exec_() self.rect.setBrush(QBrush(4)) if vastaus == 0: Ominaisuudet.tuki(self, 0) self.nuoli_4.hide() self.nuoli_5.hide() self.nayta_seina_tuki() if int(Ominaisuudet.onko_ulkoinen_voima_asetettu(self)) == 1: self.viiva.show() self.nuoli_3.show() self.viiva_5.hide() self.nuoli_6.hide() if vastaus == 1: pass '''Jos tukea ei ole (tyyppi = 2). Tuen tyypin valinta''' if Ominaisuudet.palauta_tuen_tyyppi(self) == 2: msgBox = QMessageBox() msgBox.setText("Valitse tuen tyyppi") msgBox.addButton(QPushButton('Seinätuki'), QMessageBox.YesRole) msgBox.addButton(QPushButton('Tuki alhaalta'), QMessageBox.NoRole) vastaus = msgBox.exec_() self.vaihda_tuki.show() self.lisaa_tuki.hide() if vastaus == 0: self.nayta_seina_tuki() Ominaisuudet.tuki(self, 0) if vastaus == 1: self.nayta_tuki_alhaalta() Ominaisuudet.tuki(self, 1) '''Joka tapauksessa asetetaan ulkoisen voiman lisääminen mahdolliseksi sekä maalataan palkki normaaliksi''' self.lisaa_ulkoinen_voima.setEnabled(True) self.simuloi.setEnabled(True) def nayta_seina_tuki(self): '''Näytetään seinätukea kuvaavat grafiikat''' self.viiva_1.show() self.viiva_2.show() self.viiva_3.show() self.viiva_4.show() def nayta_tuki_alhaalta(self): '''Näytetään alatukea kuvaavat grafiikat''' self.nuoli_4.show() self.nuoli_5.show() def paivita_tuen_tyyppi(self, tyyppi): '''Päivittää tuen tyypin arvon Ominaisuudet luokassa''' Ominaisuudet.tuki(self, tyyppi) def lisaa_seina_tuki(self): '''Piirtää seinätukea kuvaavat viivat sekä asettaa self.tuen_tyyppi arvoksi Asettaa SIMULOI-napin painettavaksi''' viiva = QtGui.QPen(QtCore.Qt.black, 2) viiva.setStyle(QtCore.Qt.SolidLine) self.viiva_1 = QGraphicsLineItem(QtCore.QLineF(300, 202, 275, 225)) self.viiva_2 = QGraphicsLineItem(QtCore.QLineF(300, 222, 275, 245)) self.viiva_3 = QGraphicsLineItem(QtCore.QLineF(300, 242, 275, 265)) self.viiva_4 = QGraphicsLineItem(QtCore.QLineF(300, 262, 275, 285)) self.scene.addItem(self.viiva_1) self.scene.addItem(self.viiva_2) self.scene.addItem(self.viiva_3) self.scene.addItem(self.viiva_4) self.viiva_1.hide() self.viiva_2.hide() self.viiva_3.hide() self.viiva_4.hide() tyyppi = 0 Ominaisuudet.tuki(self, tyyppi) self.simuloi.setEnabled(True) def lisaa_tuki_alhaalta(self): '''Piirtää alhaalta tukemista kuvaavat grafiikat sekä asettaa self.tuen_tyyppi arvoksi 1''' leveys = 15 #nuolen leveus pikseleissä korkeus = 30 #nuuolen korkeus pikseleissä '''Nuolen kärkien koordinaatit''' nuoli_piste_1 = QtCore.QPointF(305, 275) nuoli_piste_2 = QtCore.QPointF(305 - leveys, 275 + korkeus) nuoli_piste_3 = QtCore.QPointF(305 + leveys, 275 + korkeus) nuoli_piste_4 = QtCore.QPointF(995, 275) nuoli_piste_5 = QtCore.QPointF(995 - leveys, 275 + korkeus) nuoli_piste_6 = QtCore.QPointF(995 + leveys, 275 + korkeus) '''Luodaan nuolia kuvaavat QPolygonF oliot''' self.nuoli_4 = QGraphicsPolygonItem( QtGui.QPolygonF([nuoli_piste_1, nuoli_piste_2, nuoli_piste_3])) self.nuoli_5 = QGraphicsPolygonItem( QtGui.QPolygonF([nuoli_piste_4, nuoli_piste_5, nuoli_piste_6])) self.nuoli_brush = QtGui.QBrush(1) self.nuoli_pencil = QtGui.QPen(QtCore.Qt.black, 2) self.nuoli_pencil.setStyle(QtCore.Qt.SolidLine) '''Lisätään nuolet sceneen''' self.scene.addItem(self.nuoli_4) self.scene.addItem(self.nuoli_5) self.nuoli_4.hide() self.nuoli_5.hide() tyyppi = 1 Ominaisuudet.tuki(self, tyyppi) self.simuloi.setEnabled(True) def lisaa_ulkoinen_voima_nappi(self): '''Luo Lisää ulkoinen voima -napin''' self.lisaa_ulkoinen_voima = QPushButton("Lisää ulkoinen voima") self.lisaa_ulkoinen_voima.setToolTip("Lisää ulkoinen voima") self.lisaa_ulkoinen_voima.move( 0, 2 * self.button_height + 2 * self.button_separation) self.lisaa_ulkoinen_voima.resize(self.button_width, self.button_height) self.lisaa_ulkoinen_voima.font = QtGui.QFont() self.lisaa_ulkoinen_voima.font.setPointSize(8) self.lisaa_ulkoinen_voima.setFont(self.lisaa_ulkoinen_voima.font) self.lisaa_ulkoinen_voima.clicked.connect(self.nayta_ulkoinen_voima) self.lisaa_ulkoinen_voima.clicked.connect(self.nollaa_gradientti) self.lisaa_ulkoinen_voima.setEnabled(False) self.scene.addWidget(self.lisaa_ulkoinen_voima) def poista_ulkoinen_voima_nappi(self): '''Luo poista ulkoinen voima -napin''' self.poista_ulkoinen_voima = QPushButton("Poista ulkoinen voima") self.poista_ulkoinen_voima.setToolTip("Poista ulkoinen voima") self.poista_ulkoinen_voima.move( 0, 2 * self.button_height + 2 * self.button_separation) self.poista_ulkoinen_voima.resize(self.button_width, self.button_height) self.poista_ulkoinen_voima.setFont(self.lisaa_ulkoinen_voima.font) self.poista_ulkoinen_voima.clicked.connect(self.piilota_ulkoinen_voima) self.poista_ulkoinen_voima.clicked.connect(self.nollaa_gradientti) self.scene.addWidget(self.poista_ulkoinen_voima) self.poista_ulkoinen_voima.hide() def piilota_ulkoinen_voima(self): '''Piilotaa kaiken ulkoiseen voimaan liittyvän''' self.sp_voima.hide() self.yksikko_voima.hide() self.ulkoinen_voima.hide() self.lisaa_ulkoinen_voima.show() self.lisaa_ulkoinen_voima.setEnabled(True) self.viiva.hide() self.nuoli_3.hide() self.viiva_5.hide() self.nuoli_6.hide() self.poista_ulkoinen_voima.hide() self.lisaa_ulkoinen_voima.show() self.tulos.hide() Ominaisuudet.ulkoinen_voima(self, 0) def nayta_ulkoinen_voima(self): '''Näytetään ulkoinen voima riippuen tuen tyypistä''' self.sp_voima.show() self.yksikko_voima.show() self.ulkoinen_voima.show() self.lisaa_ulkoinen_voima.hide() self.poista_ulkoinen_voima.show() if int(Ominaisuudet.palauta_tuen_tyyppi(self)) == 0: self.viiva.show() self.nuoli_3.show() if int(Ominaisuudet.palauta_tuen_tyyppi(self)) == 1: self.viiva_5.show() self.nuoli_6.show() Ominaisuudet.ulkoinen_voima(self, 1) def ulkoinen_voima_valikko(self): '''Luo voiman suuruus -tekstin''' self.ulkoinen_voima = QGraphicsSimpleTextItem("Voiman suuruus") self.ulkoinen_voima.setPos(600, 5) self.ulkoinen_voima.font = QtGui.QFont() self.ulkoinen_voima.font.setPointSize(12) self.ulkoinen_voima.setFont(self.ulkoinen_voima.font) self.lisaa_ulkoinen_voima.setEnabled(False) self.scene.addItem(self.ulkoinen_voima) self.ulkoinen_voima.hide() '''Luo voiman arvon QSpinBoxin''' self.sp_voima = QSpinBox() self.sp_voima.move(750, 5) self.sp_voima.setRange(0, 10000) self.sp_voima.setSingleStep(1) self.sp_voima.setMinimumHeight(30) self.sp_voima.setValue(int(Ominaisuudet.palauta_voima(self))) self.sp_voima.valueChanged.connect(self.paivita_voima) self.scene.addWidget(self.sp_voima) self.sp_voima.hide() '''Luo yksikönvalinta QComboBOxin''' self.yksikko_voima = QComboBox() self.yksikko_voima.addItem("kN", 0) self.yksikko_voima.addItem("N", 1) self.yksikko_voima.move(820, 5) self.yksikko_voima.setMinimumHeight(30) self.yksikko_voima.setCurrentIndex( int(Ominaisuudet.palauta_voiman_yksikko(self))) self.yksikko_voima.setEditable(True) self.yksikko_voima.lineEdit().setAlignment(QtCore.Qt.AlignCenter) self.scene.addWidget(self.yksikko_voima) self.yksikko_voima.currentIndexChanged.connect( self.paivita_yksikko_voima) self.yksikko_voima.hide() def ulkoinen_voima_nuoli_seinatuki(self): '''Luo nuolen osoittamaan ulkoisen voiman paikkaa''' voima_viiva = QtGui.QPen(QtCore.Qt.black, 2) voima_viiva.setStyle(QtCore.Qt.SolidLine) '''Nuolen kärkien koordinaatit seinätuelle''' nuoli_piste_1 = QtCore.QPointF(self.palkin_paatypiste - 7, 185) nuoli_piste_2 = QtCore.QPointF(self.palkin_paatypiste, 200) nuoli_piste_3 = QtCore.QPointF(self.palkin_paatypiste + 7, 185) viiva_x = self.palkin_paatypiste self.viiva = QGraphicsLineItem( QtCore.QLineF(viiva_x, 100, viiva_x, 200)) '''Luodaan nuoli QPolygonItem olio''' self.nuoli_3 = QGraphicsPolygonItem( QtGui.QPolygonF([nuoli_piste_1, nuoli_piste_2, nuoli_piste_3])) self.nuoli_brush = QtGui.QBrush(1) self.nuoli_pencil = QtGui.QPen(QtCore.Qt.black, 2) self.nuoli_pencil.setStyle(QtCore.Qt.SolidLine) '''Lisätään viiva sekä päiden nuolet sceneen''' self.scene.addItem(self.viiva) self.scene.addItem(self.nuoli_3) self.viiva.hide() self.nuoli_3.hide() '''Lisätään tieto, että voima on asetettu''' Ominaisuudet.ulkoinen_voima(self, 1) def ulkoinen_voima_nuoli_alatuki(self): '''Nuolen kärkien koordinaatit alhaalta tuetulle palkille''' nuoli_piste_1 = QtCore.QPointF(self.palkin_keskipiste - 7, 185) nuoli_piste_2 = QtCore.QPointF(self.palkin_keskipiste, 200) nuoli_piste_3 = QtCore.QPointF(self.palkin_keskipiste + 7, 185) viiva_x = self.palkin_keskipiste '''Luodaan nuoli QPolygonItem olio''' self.nuoli_6 = QGraphicsPolygonItem( QtGui.QPolygonF([nuoli_piste_1, nuoli_piste_2, nuoli_piste_3])) self.nuoli_brush = QtGui.QBrush(1) self.nuoli_pencil = QtGui.QPen(QtCore.Qt.black, 2) self.nuoli_pencil.setStyle(QtCore.Qt.SolidLine) self.viiva_5 = QGraphicsLineItem( QtCore.QLineF(viiva_x, 100, viiva_x, 200)) '''Lisätään viiva sekä päiden nuolet sceneen''' self.scene.addItem(self.viiva_5) self.scene.addItem(self.nuoli_6) self.viiva_5.hide() self.nuoli_6.hide() '''Lisätään tieto, että voima on asetettu''' Ominaisuudet.ulkoinen_voima(self, 1) def paivita_voima(self): '''Lukee voiman arvon ja kutsuu Ominaisuudet luoka metodia voima''' voima = self.sp_voima.value() Ominaisuudet.voima(self, voima) def paivita_yksikko_voima(self): '''Lukee ykiskön arvon ja kutsuu Ominaisuudet-luokan metodia yksikko_voima''' self.yksikko_voima_arvo = self.yksikko_voima.currentData() Ominaisuudet.yksikko_voima(self, self.yksikko_voima_arvo) def materiaali_valikko(self): ''' Luo Materiaali-otsikon''' self.materiaali = QGraphicsSimpleTextItem("Materiaali") self.materiaali.setPos( 0, 3 * self.button_height + 3 * self.button_separation) self.materiaali.font = QtGui.QFont() self.materiaali.font.setPointSize(12) self.materiaali.setFont(self.materiaali.font) self.scene.addItem(self.materiaali) '''Luo drop down valikon materiaalivalinnalle''' self.materiaali_valinta = QComboBox() self.materiaali_valinta.addItem("Teräs", 0) self.materiaali_valinta.addItem("Alumiini", 1) self.materiaali_valinta.addItem("Muovi", 2) self.materiaali_valinta.move( 0, 3 * self.button_height + 3 * self.button_separation + 25) self.materiaali_valinta.resize(self.button_width, self.button_height - 25) self.materiaali_valinta.setEditable(True) self.materiaali_valinta.lineEdit().setAlignment(QtCore.Qt.AlignCenter) self.materiaali_valinta.setCurrentIndex(0) self.scene.addWidget(self.materiaali_valinta) self.materiaali_valinta.setEnabled(False) self.materiaali_valinta.currentIndexChanged.connect( self.paivita_materiaali) def paivita_materiaali(self): '''Lukee materiaalin arvon ja kutsuu Ominaisuudet-luokan metodia materiaali''' materiaali = self.materiaali_valinta.currentData() Ominaisuudet.materiaali(self, materiaali) def simulaatio_nappi(self): '''Luo SIMULOI-napin''' self.simuloi = QPushButton('SIMULOI') self.simuloi.setToolTip('Simuloi valittu rakenne') self.simuloi.move(0, 4 * self.button_height + 4 * self.button_separation) self.simuloi.setStyleSheet("background-color:rgb(122, 201, 255)") self.simuloi.resize(self.button_width, self.button_height) self.simuloi.font = QtGui.QFont() self.simuloi.font.setPointSize(12) self.simuloi.setFont(self.simuloi.font) self.simuloi.setEnabled(False) self.simuloi.clicked.connect(self.simulaatio) self.scene.addWidget(self.simuloi) def simulaatio(self): '''Kutsuu laskentaa suorittavaa metodia ja tallentaa tuloksen. Tämän jälkeen kutsuu lopputuloksen esittävän tekstin päivittävää metodia sekä palkin visualisoivaa gradient-metodia''' Laskin.laskin(self) Ominaisuudet.palauta_tulos(self) self.paivita_tulos_teksti() self.tallennaAction.setEnabled(True) if Ominaisuudet.palauta_tuen_tyyppi(self) == 0: if Ominaisuudet.onko_ulkoinen_voima_asetettu(self) == 1: self.gradient_seina_tuki() if Ominaisuudet.onko_ulkoinen_voima_asetettu(self) == 0: self.gradient_seina_tuki_ei_voimaa() if Ominaisuudet.palauta_tuen_tyyppi(self) == 1: self.gradient_alatuki() def tulos_teksti(self): '''Lisää tekstin, joka kertoo maksimijänintyksen arvon''' teksti = "Maksimijännitys " + str(self.maks_jannitys) + " MPa" self.tulos = QGraphicsSimpleTextItem(teksti) self.tulos.setPos(550, 500) self.tulos.font = QtGui.QFont() self.tulos.font.setPointSize(12) self.tulos.setFont(self.tulos.font) self.scene.addItem(self.tulos) self.tulos.hide() def paivita_tulos_teksti(self): '''Päivittää maksimijännityksen arvoa kuvaavan tekstin''' maks_jannitys = Ominaisuudet.palauta_tulos(self) self.tulos.setText("Maksimijännitys " + str(maks_jannitys) + " MPa") self.tulos.show() def palkin_pituus_valikko(self): '''Luo palkin pituus tekstin sekä spinbox-valitsimen pituuden asettamista varten Päivittää palkin pituuden Ominaisuudet luokan avulla''' self.palkin_pituus = QGraphicsSimpleTextItem("Palkin pituus") self.palkin_pituus.setPos(300, 5) self.palkin_pituus.font = QtGui.QFont() self.palkin_pituus.font.setPointSize(12) self.palkin_pituus.setFont(self.palkin_pituus.font) self.scene.addItem(self.palkin_pituus) self.palkin_pituus.hide() self.sp = QSpinBox() self.scene.addWidget(self.sp) self.sp.hide() self.sp.move(450, 5) self.sp.setRange(0, 100) self.sp.setSingleStep(1) self.sp.setMinimumHeight(30) self.sp.setValue(int(Ominaisuudet.palauta_palkin_pituus(self))) self.paivita_pituus() self.sp.valueChanged.connect(self.paivita_pituus) def paivita_pituus(self): '''Lukee palkin pituuden ja aktivoi Ominaisuudet luokan meodin palkin pituus''' self.palkin_pituus_arvo = self.sp.value() Ominaisuudet.palkin_pituus(self, self.palkin_pituus_arvo) self.paivita_asteikon_arvot() def yksikko_pituus(self): '''Luo yksikönvalinta dropdown-menun ja arvon muuttuessa päivittää yksikön Ominaisuudet-luokassa''' self.yksikko = QComboBox() self.yksikko.addItem("m", 0) self.yksikko.addItem("cm", 1) self.yksikko.addItem("mm", 2) self.yksikko.move(500, 5) self.yksikko.setMinimumHeight(30) self.yksikko.setEditable(True) self.yksikko.lineEdit().setAlignment(QtCore.Qt.AlignCenter) self.yksikko.setCurrentIndex( Ominaisuudet.palauta_pituuden_yksikko(self)) self.scene.addWidget(self.yksikko) self.yksikko.hide() self.yksikko_arvo = self.yksikko.currentData() self.yksikko.currentIndexChanged.connect(self.paivita_yksikko) def paivita_yksikko(self): '''Lukee yksikön arvon ja kutsuu Ominaisuudet-luokan metodia yksikko''' self.yksikko_arvo = self.yksikko.currentData() Ominaisuudet.yksikko(self, self.yksikko_arvo) self.paivita_asteikon_arvot() def asteikko(self): ''''Luodaan viivaa kuvaava olio''' viiva = QtGui.QPen(QtCore.Qt.black, 2) viiva.setStyle(QtCore.Qt.SolidLine) '''Oikean puoleisen nuolen kärkien koordinaatit''' nuoli_1_piste_1 = QtCore.QPointF(990, 390) nuoli_1_piste_2 = QtCore.QPointF(1000, 400) nuoli_1_piste_3 = QtCore.QPointF(990, 410) '''Vasemman puoleisen nuolen kärkien koordinaatit''' nuoli_2_piste_1 = QtCore.QPointF(310, 390) nuoli_2_piste_2 = QtCore.QPointF(300, 400) nuoli_2_piste_3 = QtCore.QPointF(310, 410) '''Luodaan nuoli QPolygonF oliot''' self.nuoli_1 = QGraphicsPolygonItem( QtGui.QPolygonF( [nuoli_1_piste_1, nuoli_1_piste_2, nuoli_1_piste_3])) self.nuoli_2 = QGraphicsPolygonItem( QtGui.QPolygonF( [nuoli_2_piste_1, nuoli_2_piste_2, nuoli_2_piste_3])) self.nuoli_brush = QtGui.QBrush(1) self.nuoli_pencil = QtGui.QPen(QtCore.Qt.black, 2) self.nuoli_pencil.setStyle(QtCore.Qt.SolidLine) self.line = QGraphicsLineItem(QtCore.QLineF(300, 400, 1000, 400)) '''Lisätään viiva sekä päiden nuolet sceneen''' self.scene.addItem(self.line) self.scene.addItem(self.nuoli_1) self.scene.addItem(self.nuoli_2) self.line.hide() self.nuoli_1.hide() self.nuoli_2.hide() def lisaa_asteikko_arvo(self): '''Lisää tekstikentän pituuden arvolle sekä yksikölle''' teksti = (str(Ominaisuudet.palauta_palkin_pituus(self)) + " " + "m") self.asteikko_teksti = QGraphicsSimpleTextItem() self.asteikko_teksti.setText(teksti) self.asteikko_teksti.setPos(650, 425) self.asteikko_teksti.font = QtGui.QFont() self.asteikko_teksti.font.setPointSize(12) self.asteikko_teksti.setFont(self.asteikko_teksti.font) self.scene.addItem(self.asteikko_teksti) self.asteikko_teksti.hide() def paivita_asteikon_arvot(self): '''Päivittää palkin pituutta kuvaavan asteikon''' yksikko = Ominaisuudet.palauta_pituuden_yksikko(self) if yksikko == 0: self.yksikko_merkki = "m" if yksikko == 1: self.yksikko_merkki = "cm" if yksikko == 2: self.yksikko_merkki = "mm" pituus = float(Ominaisuudet.palauta_palkin_pituus(self)) teksti = str(str(pituus) + " " + self.yksikko_merkki) self.asteikko_teksti.setText(teksti) self.asteikko_teksti.show() def gradient_seina_tuki(self): '''Luo seinästä tuetun palkin rasitusta kuvaavan gradientin''' gradient = QLinearGradient(300, 200, 300 + self.palkin_leveys, 200) gradient.setColorAt(0, QColor(244, 72, 66)) gradient.setColorAt(1, QColor(65, 244, 83)) self.rect.setBrush(gradient) def gradient_seina_tuki_ei_voimaa(self): '''Luo ilman ulkoista voimaa olevan gradientin''' gradient = QLinearGradient(300, 200, 300 + (self.palkin_leveys / 2), 200) gradient.setColorAt(0, QColor(244, 72, 66)) gradient.setColorAt(1, QColor(65, 244, 83)) self.rect.setBrush(gradient) def gradient_alatuki(self): '''Luo kahdella alatuella olevan palkin rasitusta kuvaavan gradientin''' gradient = QLinearGradient(300, 200, 300 + self.palkin_leveys, 200) gradient.setColorAt(0, QColor(65, 244, 83)) gradient.setColorAt(0.5, QColor(244, 72, 66)) gradient.setColorAt(1, QColor(65, 244, 83)) self.rect.setBrush(gradient) def nollaa_gradientti(self): '''Asettaa palkin "normaaliksi"''' self.rect.setBrush(QBrush(4)) def uusi_rakenne(self): '''Muokkaa ikkunaa uuden simulaation luomista varten''' self.rect.hide() self.ulkoinen_voima.hide() self.sp_voima.hide() self.yksikko_voima.hide() self.nuoli_1.hide() self.nuoli_2.hide() self.nuoli_3.hide() self.nuoli_4.hide() self.nuoli_5.hide() self.nuoli_6.hide() self.viiva_1.hide() self.viiva_2.hide() self.viiva_3.hide() self.viiva_4.hide() self.viiva_5.hide() self.viiva.hide() self.palkin_pituus.hide() self.sp.hide() self.yksikko.hide() self.line.hide() self.asteikko_teksti.hide() self.tulos.hide() self.nollaa_gradientti() self.lisaa_tuki.show() self.vaihda_tuki.hide() self.poista_ulkoinen_voima.hide() self.lisaa_ulkoinen_voima.show() Ominaisuudet.alkuarvot(self) '''Asettaa napit''' self.uusi_palkki.setEnabled(True) self.lisaa_ulkoinen_voima.setEnabled(False) self.lisaa_tuki.setEnabled(False) self.simuloi.setEnabled(False) self.tallennaAction.setEnabled(False) '''Päivittää tuen tyypiksi arvon, joka vastaa, ettei tukea ole''' self.tuen_tyyppi = 2 def close_application(self): '''sulkee ohjelman''' sys.exit()