class CountSelectorWidget(QWidget): def __init__(self, parent, minimum, maximum, initial=0): QWidget.__init__(self, parent) self._slider = QScrollBar(Qt.Horizontal, self) self._slider.setMinimum(minimum) self._slider.setMaximum(maximum) self._slider.setSingleStep(1) # self._slider.setTickInterval(1) self._linedit = QLineEdit(self) self._linedit.setValidator(QIntValidator(minimum, maximum, self)) self._linedit.setMinimumWidth(20) self._layout = QHBoxLayout(self) self._layout.setContentsMargins(0, 0, 0, 0) self._layout.addWidget(self._slider, 4) self._layout.addWidget(self._linedit, 1) self._slider.valueChanged.connect(self._update_value) self._linedit.textChanged.connect(self._update_value) self._update_value(initial) valueChanged = pyqtSignal(int) def _update_value(self, value): value = int(value) if value else 0 self.value = value self.valueChanged.emit(value) @property def value(self): return self._slider.value() @value.setter def value(self, value): if self._slider.value() != value: self._slider.setValue(value) self._linedit.setText(str(value))
def __init__(self, window, plugin, keystore, device_id): title = _("{} Settings").format(plugin.device) super(SettingsDialog, self).__init__(window, title) self.setMaximumWidth(540) devmgr = plugin.device_manager() config = devmgr.config handler = keystore.handler thread = keystore.thread hs_rows, hs_cols = (64, 128) def invoke_client(method, *args, **kw_args): unpair_after = kw_args.pop('unpair_after', False) def task(): client = devmgr.client_by_id(device_id) if not client: raise RuntimeError("Device not connected") if method: getattr(client, method)(*args, **kw_args) if unpair_after: devmgr.unpair_id(device_id) return client.features thread.add(task, on_success=update) def update(features): self.features = features set_label_enabled() if features.bootloader_hash: bl_hash = bh2u(features.bootloader_hash) bl_hash = "\n".join([bl_hash[:32], bl_hash[32:]]) else: bl_hash = "N/A" noyes = [_("No"), _("Yes")] endis = [_("Enable Passphrases"), _("Disable Passphrases")] disen = [_("Disabled"), _("Enabled")] setchange = [_("Set a PIN"), _("Change PIN")] version = "%d.%d.%d" % (features.major_version, features.minor_version, features.patch_version) device_label.setText(features.label) pin_set_label.setText(noyes[features.pin_protection]) passphrases_label.setText(disen[features.passphrase_protection]) bl_hash_label.setText(bl_hash) label_edit.setText(features.label) device_id_label.setText(features.device_id) initialized_label.setText(noyes[features.initialized]) version_label.setText(version) clear_pin_button.setVisible(features.pin_protection) clear_pin_warning.setVisible(features.pin_protection) pin_button.setText(setchange[features.pin_protection]) pin_msg.setVisible(not features.pin_protection) passphrase_button.setText(endis[features.passphrase_protection]) language_label.setText(features.language) def set_label_enabled(): label_apply.setEnabled(label_edit.text() != self.features.label) def rename(): invoke_client('change_label', label_edit.text()) def toggle_passphrase(): title = _("Confirm Toggle Passphrase Protection") currently_enabled = self.features.passphrase_protection if currently_enabled: msg = _("After disabling passphrases, you can only pair this " "Electrum wallet if it had an empty passphrase. " "If its passphrase was not empty, you will need to " "create a new wallet with the install wizard. You " "can use this wallet again at any time by re-enabling " "passphrases and entering its passphrase.") else: msg = _("Your current Electrum wallet can only be used with " "an empty passphrase. You must create a separate " "wallet with the install wizard for other passphrases " "as each one generates a new set of addresses.") msg += "\n\n" + _("Are you sure you want to proceed?") if not self.question(msg, title=title): return invoke_client('toggle_passphrase', unpair_after=currently_enabled) def change_homescreen(): dialog = QFileDialog(self, _("Choose Homescreen")) filename, __ = dialog.getOpenFileName() if not filename: return # user cancelled if filename.endswith('.toif'): img = open(filename, 'rb').read() if img[:8] != b'TOIf\x90\x00\x90\x00': handler.show_error('File is not a TOIF file with size of \ 144x144') return else: from PIL import Image # FIXME im = Image.open(filename) if im.size != (128, 64): handler.show_error('Image must be 128 x 64 pixels') return im = im.convert('1') pix = im.load() img = bytearray(1024) for j in range(64): for i in range(128): if pix[i, j]: o = (i + j * 128) img[o // 8] |= (1 << (7 - o % 8)) img = bytes(img) invoke_client('change_homescreen', img) def clear_homescreen(): invoke_client('change_homescreen', b'\x00') def set_pin(): invoke_client('set_pin', remove=False) def clear_pin(): invoke_client('set_pin', remove=True) def wipe_device(): wallet = window.wallet if wallet and sum(wallet.get_balance()): title = _("Confirm Device Wipe") msg = _("Are you SURE you want to wipe the device?\n" "Your wallet still has bitcoins in it!") if not self.question( msg, title=title, icon=QMessageBox.Critical): return invoke_client('wipe_device', unpair_after=True) def slider_moved(): mins = timeout_slider.sliderPosition() timeout_minutes.setText(_("%2d minutes") % mins) def slider_released(): config.set_session_timeout(timeout_slider.sliderPosition() * 60) # Information tab info_tab = QWidget() info_layout = QVBoxLayout(info_tab) info_glayout = QGridLayout() info_glayout.setColumnStretch(2, 1) device_label = QLabel() pin_set_label = QLabel() passphrases_label = QLabel() version_label = QLabel() device_id_label = QLabel() bl_hash_label = QLabel() bl_hash_label.setWordWrap(True) language_label = QLabel() initialized_label = QLabel() rows = [ (_("Device Label"), device_label), (_("PIN set"), pin_set_label), (_("Passphrases"), passphrases_label), (_("Firmware Version"), version_label), (_("Device ID"), device_id_label), (_("Bootloader Hash"), bl_hash_label), (_("Language"), language_label), (_("Initialized"), initialized_label), ] for row_num, (label, widget) in enumerate(rows): info_glayout.addWidget(QLabel(label), row_num, 0) info_glayout.addWidget(widget, row_num, 1) info_layout.addLayout(info_glayout) # Settings tab settings_tab = QWidget() settings_layout = QVBoxLayout(settings_tab) settings_glayout = QGridLayout() # Settings tab - Label label_msg = QLabel( _("Name this {}. If you have multiple devices " "their labels help distinguish them.").format(plugin.device)) label_msg.setWordWrap(True) label_label = QLabel(_("Device Label")) label_edit = QLineEdit() label_edit.setMinimumWidth(150) label_edit.setMaxLength(plugin.MAX_LABEL_LEN) label_apply = QPushButton(_("Apply")) label_apply.clicked.connect(rename) label_edit.textChanged.connect(set_label_enabled) settings_glayout.addWidget(label_label, 0, 0) settings_glayout.addWidget(label_edit, 0, 1, 1, 2) settings_glayout.addWidget(label_apply, 0, 3) settings_glayout.addWidget(label_msg, 1, 1, 1, -1) pin_button = QPushButton() # Settings tab - PIN pin_label = QLabel(_("PIN Protection")) pin_button.clicked.connect(set_pin) settings_glayout.addWidget(pin_label, 2, 0) settings_glayout.addWidget(pin_button, 2, 1) pin_msg = QLabel( _("PIN protection is strongly recommended. " "A PIN is your only protection against someone " "stealing your bitcoins if they obtain physical " "access to your {}.").format(plugin.device)) pin_msg.setWordWrap(True) pin_msg.setStyleSheet("color: red") settings_glayout.addWidget(pin_msg, 3, 1, 1, -1) # Settings tab - Homescreen homescreen_label = QLabel(_("Homescreen")) homescreen_change_button = QPushButton(_("Change...")) homescreen_clear_button = QPushButton(_("Reset")) homescreen_change_button.clicked.connect(change_homescreen) try: import PIL except ImportError: homescreen_change_button.setDisabled(True) homescreen_change_button.setToolTip( _("Required package 'PIL' is not available - Please install \ it or use the OctoWallet website instead.")) homescreen_clear_button.clicked.connect(clear_homescreen) homescreen_msg = QLabel( _("You can set the homescreen on your " "device to personalize it. You must " "choose a {} x {} monochrome black and " "white image.").format(hs_rows, hs_cols)) homescreen_msg.setWordWrap(True) settings_glayout.addWidget(homescreen_label, 4, 0) settings_glayout.addWidget(homescreen_change_button, 4, 1) settings_glayout.addWidget(homescreen_clear_button, 4, 2) settings_glayout.addWidget(homescreen_msg, 5, 1, 1, -1) # Settings tab - Session Timeout timeout_label = QLabel(_("Session Timeout")) timeout_minutes = QLabel() timeout_slider = QSlider(Qt.Horizontal) timeout_slider.setRange(1, 60) timeout_slider.setSingleStep(1) timeout_slider.setTickInterval(5) timeout_slider.setTickPosition(QSlider.TicksBelow) timeout_slider.setTracking(True) timeout_msg = QLabel( _("Clear the session after the specified period " "of inactivity. Once a session has timed out, " "your PIN and passphrase (if enabled) must be " "re-entered to use the device.")) timeout_msg.setWordWrap(True) timeout_slider.setSliderPosition(config.get_session_timeout() // 60) slider_moved() timeout_slider.valueChanged.connect(slider_moved) timeout_slider.sliderReleased.connect(slider_released) settings_glayout.addWidget(timeout_label, 6, 0) settings_glayout.addWidget(timeout_slider, 6, 1, 1, 3) settings_glayout.addWidget(timeout_minutes, 6, 4) settings_glayout.addWidget(timeout_msg, 7, 1, 1, -1) settings_layout.addLayout(settings_glayout) settings_layout.addStretch(1) # Advanced tab advanced_tab = QWidget() advanced_layout = QVBoxLayout(advanced_tab) advanced_glayout = QGridLayout() # Advanced tab - clear PIN clear_pin_button = QPushButton(_("Disable PIN")) clear_pin_button.clicked.connect(clear_pin) clear_pin_warning = QLabel( _("If you disable your PIN, anyone with physical access to your " "{} device can spend your bitcoins.").format(plugin.device)) clear_pin_warning.setWordWrap(True) clear_pin_warning.setStyleSheet("color: red") advanced_glayout.addWidget(clear_pin_button, 0, 2) advanced_glayout.addWidget(clear_pin_warning, 1, 0, 1, 5) # Advanced tab - toggle passphrase protection passphrase_button = QPushButton() passphrase_button.clicked.connect(toggle_passphrase) passphrase_msg = WWLabel(PASSPHRASE_HELP) passphrase_warning = WWLabel(PASSPHRASE_NOT_PIN) passphrase_warning.setStyleSheet("color: red") advanced_glayout.addWidget(passphrase_button, 3, 2) advanced_glayout.addWidget(passphrase_msg, 4, 0, 1, 5) advanced_glayout.addWidget(passphrase_warning, 5, 0, 1, 5) # Advanced tab - wipe device wipe_device_button = QPushButton(_("Wipe Device")) wipe_device_button.clicked.connect(wipe_device) wipe_device_msg = QLabel( _("Wipe the device, removing all data from it. The firmware " "is left unchanged.")) wipe_device_msg.setWordWrap(True) wipe_device_warning = QLabel( _("Only wipe a device if you have the recovery seed written down " "and the device wallet(s) are empty, otherwise the bitcoins " "will be lost forever.")) wipe_device_warning.setWordWrap(True) wipe_device_warning.setStyleSheet("color: red") advanced_glayout.addWidget(wipe_device_button, 6, 2) advanced_glayout.addWidget(wipe_device_msg, 7, 0, 1, 5) advanced_glayout.addWidget(wipe_device_warning, 8, 0, 1, 5) advanced_layout.addLayout(advanced_glayout) advanced_layout.addStretch(1) tabs = QTabWidget(self) tabs.addTab(info_tab, _("Information")) tabs.addTab(settings_tab, _("Settings")) tabs.addTab(advanced_tab, _("Advanced")) dialog_vbox = QVBoxLayout(self) dialog_vbox.addWidget(tabs) dialog_vbox.addLayout(Buttons(CloseButton(self))) # Update information invoke_client(None)
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 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 LibraryCodesTab(QWidget): def __init__(self, mygui, myguidb, mymainprefs, myparam_dict, myuiexit, mysavedialoggeometry): super(LibraryCodesTab, self).__init__() #----------------------------------------------------- #----------------------------------------------------- self.gui = mygui #----------------------------------------------------- #----------------------------------------------------- self.guidb = myguidb #----------------------------------------------------- #----------------------------------------------------- self.lib_path = self.gui.library_view.model().db.library_path #----------------------------------------------------- #----------------------------------------------------- self.mytabprefs = mymainprefs #----------------------------------------------------- #----------------------------------------------------- self.param_dict = myparam_dict #----------------------------------------------------- #----------------------------------------------------- self.ui_exit = myuiexit #----------------------------------------------------- #----------------------------------------------------- self.save_dialog_geometry = mysavedialoggeometry #----------------------------------------------------- #----------------------------------------------------- font = QFont() font.setBold(False) font.setPointSize(10) #----------------------------------------------------- self.layout_top = QVBoxLayout() self.layout_top.setSpacing(0) self.layout_top.setAlignment(Qt.AlignLeft) self.setLayout(self.layout_top) #----------------------------------------------------- self.scroll_area_frame = QScrollArea() self.scroll_area_frame.setAlignment(Qt.AlignLeft) self.scroll_area_frame.setWidgetResizable(True) self.scroll_area_frame.ensureVisible(400, 400) self.layout_top.addWidget( self.scroll_area_frame ) # the scroll area is now the child of the parent of self.layout_top # NOTE: the self.scroll_area_frame.setWidget(self.scroll_widget) is at the end of the init() AFTER all children have been created and assigned to a layout... #----------------------------------------------------- self.scroll_widget = QWidget() self.layout_top.addWidget( self.scroll_widget ) # causes automatic reparenting of QWidget to the parent of self.layout_top, which is: self . #----------------------------------------------------- self.layout_frame = QVBoxLayout() self.layout_frame.setSpacing(0) self.layout_frame.setAlignment(Qt.AlignLeft) self.scroll_widget.setLayout( self.layout_frame ) # causes automatic reparenting of any widget later added to self.layout_frame to the parent of self.layout_frame, which is: QWidget . #----------------------------------------------------- self.lc_groupbox = QGroupBox('Settings:') self.lc_groupbox.setMaximumWidth(400) self.lc_groupbox.setToolTip( "<p style='white-space:wrap'>The settings that control 'Library Codes'. Using only ISBN or ISSN or Author/Title, Library Codes for selected books will be derived using the Current Settings." ) self.layout_frame.addWidget(self.lc_groupbox) self.lc_layout = QGridLayout() self.lc_groupbox.setLayout(self.lc_layout) #----------------------------------------------------- #----------------------------------------------------- #----------------------------------------------------- #----------------------------------------------------- self.spacing0 = QLabel() self.layout_frame.addWidget(self.spacing0) self.spacing0.setMaximumHeight(20) #----------------------------------------------------- #----------------------------------------------------- #----------------------------------------------------- self.button_box = QDialogButtonBox() self.button_box.setOrientation(Qt.Horizontal) self.button_box.setCenterButtons(True) self.layout_frame.addWidget(self.button_box) #----------------------------------------------------- #----------------------------------------------------- #----------------------------------------------------- #----------------------------------------------------- #----------------------------------------------------- #----------------------------------------------------- self.push_button_save_only = QPushButton("Save") self.push_button_save_only.clicked.connect(self.save_settings) self.push_button_save_only.setDefault(True) self.push_button_save_only.setFont(font) self.push_button_save_only.setToolTip( "<p style='white-space:wrap'>Save all user settings.") self.button_box.addButton(self.push_button_save_only, 0) self.push_button_exit_only = QPushButton("Exit") self.push_button_exit_only.clicked.connect(self.exit_only) self.push_button_exit_only.setDefault(False) self.push_button_exit_only.setFont(font) self.push_button_exit_only.setToolTip( "<p style='white-space:wrap'>Exit immediately without saving anything." ) self.button_box.addButton(self.push_button_exit_only, 0) #----------------------------------------------------- #----------------------------------------------------- #----------------------------------------------------- #----------------------------------------------------- r = 4 self.ddc_labelname = QLineEdit(self) self.ddc_labelname.setText(self.mytabprefs['DDC']) self.ddc_labelname.setFont(font) self.ddc_labelname.setToolTip( "<p style='white-space:wrap'>Custom Column Search/Lookup #name for DDC.<br><br>See: https://www.oclc.org/dewey/features/summaries.en.html" ) self.ddc_labelname.setMaximumWidth(100) self.lc_layout.addWidget(self.ddc_labelname, r, 0) self.ddc_activate_checkbox = QCheckBox( "Activate 'Dewey Decimal Code' Classification?") self.ddc_activate_checkbox.setToolTip( "<p style='white-space:wrap'>Do you want to derive DDC?") r = r + 1 self.lc_layout.addWidget(self.ddc_activate_checkbox, r, 0) if prefs['DDC_IS_ACTIVE'] == unicode_type(S_TRUE): self.ddc_activate_checkbox.setChecked(True) else: self.ddc_activate_checkbox.setChecked(False) #----------------------------------------------------- self.spacing1 = QLabel() r = r + 1 self.lc_layout.addWidget(self.spacing1, r, 0) self.spacing1.setMaximumHeight(10) #----------------------------------------------------- self.lcc_labelname = QLineEdit(self) self.lcc_labelname.setText(self.mytabprefs['LCC']) self.lcc_labelname.setFont(font) self.lcc_labelname.setToolTip( "<p style='white-space:wrap'>Custom Column Search/Lookup #name for LCC.<br><br>See: http://www.loc.gov/catdir/cpso/lcco/ " ) self.lcc_labelname.setMaximumWidth(100) r = r + 4 self.lc_layout.addWidget(self.lcc_labelname, r, 0) self.lcc_activate_checkbox = QCheckBox( "Activate 'Library of Congress Code' Classification?") self.lcc_activate_checkbox.setToolTip( "<p style='white-space:wrap'>Do you want to derive LCC?") r = r + 1 self.lc_layout.addWidget(self.lcc_activate_checkbox, r, 0) if prefs['LCC_IS_ACTIVE'] == unicode_type(S_TRUE): self.lcc_activate_checkbox.setChecked(True) else: self.lcc_activate_checkbox.setChecked(False) #----------------------------------------------------- self.spacing2 = QLabel("") r = r + 1 self.lc_layout.addWidget(self.spacing2, r, 0) self.spacing2.setMaximumHeight(10) #----------------------------------------------------- self.fast_labelname = QLineEdit(self) self.fast_labelname.setText(self.mytabprefs['FAST']) self.fast_labelname.setFont(font) self.fast_labelname.setToolTip( "<p style='white-space:wrap'>Custom Column Search/Lookup #name for FAST Tag Values. " ) self.fast_labelname.setMinimumWidth(100) self.fast_labelname.setMaximumWidth(100) r = r + 4 self.lc_layout.addWidget(self.fast_labelname, r, 0) self.fast_activate_checkbox = QCheckBox("Activate 'FAST' Tags?") self.fast_activate_checkbox.setToolTip( "<p style='white-space:wrap'>Do you want to derive FAST Tags?\ <br><br>Text. Behaves like Tags. Not Names.<br><br>" ) r = r + 1 self.lc_layout.addWidget(self.fast_activate_checkbox, r, 0) if prefs['FAST_IS_ACTIVE'] == unicode_type(S_TRUE): self.fast_activate_checkbox.setChecked(True) else: self.fast_activate_checkbox.setChecked(False) #----------------------------------------------------- self.spacing6 = QLabel("") r = r + 1 self.lc_layout.addWidget(self.spacing6, r, 0) self.spacing6.setMaximumHeight(10) #----------------------------------------------------- self.oclc_labelname = QLineEdit(self) self.oclc_labelname.setText(self.mytabprefs['OCLC']) self.oclc_labelname.setFont(font) self.oclc_labelname.setToolTip( "<p style='white-space:wrap'>Custom Column Search/Lookup #name for OCLC-OWI.<br><br>See: #http://classify.oclc.org/classify2/ " ) self.oclc_labelname.setMaximumWidth(100) r = r + 4 self.lc_layout.addWidget(self.oclc_labelname, r, 0) self.oclc_activate_checkbox = QCheckBox( "Activate 'Online Computer Library Center' Work ID Code?") self.oclc_activate_checkbox.setToolTip( "<p style='white-space:wrap'>Do you want to derive OCLC-OWI?") r = r + 1 self.lc_layout.addWidget(self.oclc_activate_checkbox, r, 0) if self.mytabprefs['OCLC_IS_ACTIVE'] == unicode_type(S_TRUE): self.oclc_activate_checkbox.setChecked(True) else: self.oclc_activate_checkbox.setChecked(False) #----------------------------------------------------- self.spacing5 = QLabel("") r = r + 1 self.lc_layout.addWidget(self.spacing5, r, 0) self.spacing5.setMaximumHeight(10) #----------------------------------------------------- self.lc_author_details_labelname = QLineEdit(self) self.lc_author_details_labelname.setText( self.mytabprefs['EXTRA_AUTHOR_DETAILS']) self.lc_author_details_labelname.setFont(font) self.lc_author_details_labelname.setToolTip( "<p style='white-space:wrap'>Custom Column Search/Lookup #name for 'LC Extra Author Details'.\ <br><br>Text. Behaves like Tags. Not Names.<br><br>" ) self.lc_author_details_labelname.setMaximumWidth(100) r = r + 4 self.lc_layout.addWidget(self.lc_author_details_labelname, r, 0) self.lc_author_details_checkbox = QCheckBox( "Activate 'Library Codes Extra Author Details'?") self.lc_author_details_checkbox.setToolTip( "<p style='white-space:wrap'>Do you want to add (never delete or replace) any available Tag-like values to this Custom Column if they are associated with the OCLC-OWI Identifier?" ) r = r + 1 self.lc_layout.addWidget(self.lc_author_details_checkbox, r, 0) if self.mytabprefs['EXTRA_AUTHOR_DETAILS_IS_ACTIVE'] == unicode_type( S_TRUE): self.lc_author_details_checkbox.setChecked(True) else: self.lc_author_details_checkbox.setChecked(False) #----------------------------------------------------- #----------------------------------------------------- self.spacing4 = QLabel() r = r + 1 self.lc_layout.addWidget(self.spacing4, r, 0) self.spacing4.setMaximumHeight(10) #----------------------------------------------------- font.setBold(False) font.setPointSize(7) #----------------------------------------------------- self.push_button_autoadd_custom_columns = QPushButton( "Automatically Add Activated Custom Columns?") self.push_button_autoadd_custom_columns.clicked.connect( self.autoadd_custom_columns) self.push_button_autoadd_custom_columns.setDefault(False) self.push_button_autoadd_custom_columns.setFont(font) self.push_button_autoadd_custom_columns.setToolTip( "<p style='white-space:wrap'>Do you want to automatically add the Custom Columns selected above?<br><br>If you have any issues, please add them manually." ) r = r + 4 self.lc_layout.addWidget(self.push_button_autoadd_custom_columns, r, 0) self.push_button_autoadd_custom_columns.setMaximumWidth(250) #----------------------------------------------------- self.lc_custom_columns_generation_label = QLabel() r = r + 1 self.lc_layout.addWidget(self.lc_custom_columns_generation_label, r, 0) self.lc_custom_columns_generation_label.setText( " ") self.lc_custom_columns_generation_label.setMaximumHeight(10) self.lc_custom_columns_generation_label.setFont(font) self.oclc_identifier_only_checkbox = QCheckBox( "Always Create OCLC-OWI as an 'Identifier' (à la ISBN)?") self.oclc_identifier_only_checkbox.setToolTip( "<p style='white-space:wrap'>Do you want to update Calibre's Identifiers for an Identifier of 'OCLC-OWI',\ regardless of whether you want its own Custom Column updated?\ <br><br>REQUIRED to derive DDC/LCC using Author/Title." ) r = r + 2 self.lc_layout.addWidget(self.oclc_identifier_only_checkbox, r, 0) if prefs['OCLC_IDENTIFIER'] == unicode_type(S_TRUE): self.oclc_identifier_only_checkbox.setChecked(True) else: self.oclc_identifier_only_checkbox.setChecked(False) #----------------------------------------------------- self.spacing3 = QLabel("") r = r + 1 self.lc_layout.addWidget(self.spacing3, r, 0) self.spacing3.setMaximumHeight(10) #----------------------------------------------------- font.setBold(False) font.setPointSize(10) #----------------------------------------------------- self.lc_genre_labelname = QLineEdit(self) self.lc_genre_labelname.setText(self.mytabprefs['GENRE']) self.lc_genre_labelname.setFont(font) self.lc_genre_labelname.setToolTip( "<p style='white-space:wrap'>Custom Column Search/Lookup #name for 'Genre'.\ <br><br>Text. Behaves like Tags.<br><br>" ) self.lc_genre_labelname.setMaximumWidth(100) r = r + 1 self.lc_layout.addWidget(self.lc_genre_labelname, r, 0) self.lc_checkbox_buttongroup = QButtonGroup() self.lc_checkbox_buttongroup.setExclusive(True) self.lc_genre_ddc_checkbox = QCheckBox( "Update 'Genre' using DDC-to-Genre Mappings?") self.lc_genre_ddc_checkbox.setToolTip( "<p style='white-space:wrap'>Do you want LC to update 'Genre' using the DDC-to-Genre mapping in Table _lc_genre_mapping?" ) r = r + 1 self.lc_layout.addWidget(self.lc_genre_ddc_checkbox, r, 0) self.lc_genre_lcc_checkbox = QCheckBox( "Update 'Genre' using LCC-to-Genre Mappings?") self.lc_genre_lcc_checkbox.setToolTip( "<p style='white-space:wrap'>Do you want LC to update 'Genre' using the LCC-to-Genre mapping in Table _lc_genre_mapping?" ) r = r + 1 self.lc_layout.addWidget(self.lc_genre_lcc_checkbox, r, 0) self.lc_genre_inactive_checkbox = QCheckBox( "Do not update 'Genre' at all") self.lc_genre_inactive_checkbox.setToolTip( "<p style='white-space:wrap'>Do no 'Genre' processing at all?") r = r + 1 self.lc_layout.addWidget(self.lc_genre_inactive_checkbox, r, 0) self.lc_checkbox_buttongroup.addButton(self.lc_genre_ddc_checkbox) self.lc_checkbox_buttongroup.addButton(self.lc_genre_lcc_checkbox) self.lc_checkbox_buttongroup.addButton(self.lc_genre_inactive_checkbox) if self.mytabprefs['GENRE_DDC_IS_ACTIVE'] == unicode_type(S_TRUE): self.lc_genre_ddc_checkbox.setChecked(True) elif self.mytabprefs['GENRE_LCC_IS_ACTIVE'] == unicode_type(S_TRUE): self.lc_genre_lcc_checkbox.setChecked(True) elif self.mytabprefs['GENRE_IS_INACTIVE'] == unicode_type(S_TRUE): self.lc_genre_inactive_checkbox.setChecked(True) self.lc_exact_match_checkbox = QCheckBox( "DDC: Require an 'Exact Match', not a 'Best Match'?") self.lc_exact_match_checkbox.setToolTip( "<p style='white-space:wrap'>Check this checkbox if you want an exact DDC match to be required in Table _lc_genre_mapping. Otherwise, a 'best match' will be used via progressive shortening from right to left, but not past any decimal point. If there is no decimal point in a book's DDC, then no progressive shortening will be performed at all." ) r = r + 1 self.lc_layout.addWidget(self.lc_exact_match_checkbox, r, 0) if self.mytabprefs['GENRE_EXACT_MATCH'] == unicode_type(S_TRUE): self.lc_exact_match_checkbox.setChecked(True) self.spin_lcc = QSpinBox(self) self.spin_lcc.setMinimum(1) self.spin_lcc.setMaximum(50) self.spin_lcc.setProperty('value', prefs['GENRE_LCC_MATCH_LENGTH']) self.spin_lcc.setMaximumWidth(250) self.spin_lcc.setSuffix(" LCC: Maximum Length to Match") self.spin_lcc.setToolTip( "<p style='white-space:nowrap'>Maximum number of characters in the LCC that should be used to map to the 'Genre', starting from the left. A maximum of 1 guarantees a (broad) match.\ <br><br>LCCs are structured with either 1 or 2 beginning letters, so 2-character LCCs have special matching logic.\ <br><br>Example: Assume maximum = 2 for a LCC of 'Q1': Q1 would be attempted. If it failed, because the 2nd digit is a number, 'Q' would be attempted.\ <br><br>Example: Assume maximum = 2 for a LCC of 'PN1969.C65': PN would be attempted. If it failed, nothing else would be attempted.\ <br><br>Example: Assume maximum = 4 for a LCC of 'PN1969.C65': PN19 would be attempted. If it failed, nothing else would be attempted.\ <br><br>Example: Assume maximum = 4 for a LCC of 'Q1': Q1 would be attempted. If it failed, because the 2nd digit is a number, 'Q' would be attempted.\ <br><br>Example: Assume maximum = 4 for a LCC of 'Q389': Q389 would be attempted. If it failed, nothing else would be attempted." ) r = r + 2 self.lc_layout.addWidget(self.spin_lcc, r, 0) #----------------------------------------------------- #----------------------------------------------------- #----------------------------------------------------- #----------------------------------------------------- #----------------------------------------------------- #----------------------------------------------------- self.scroll_widget.resize(self.sizeHint()) #----------------------------------------------------- #----------------------------------------------------- self.scroll_area_frame.setWidget( self.scroll_widget ) # now that all widgets have been created and assigned to a layout... #----------------------------------------------------- #----------------------------------------------------- self.scroll_area_frame.resize(self.sizeHint()) #----------------------------------------------------- #----------------------------------------------------- self.resize(self.sizeHint()) #----------------------------------------------------------------------------------------- def validate(self): return True #----------------------------------------------------------------------------------------- def autoadd_custom_columns(self): number_active = self.save_settings() if number_active == 0: return error_dialog( self.gui, _('Automatically Add Custom Columns'), _('No Activated Library Codes Custom Columns Found. Nothing to Add.' ), show=True) self.cli_param_list = self.create_cli_parameters() is_valid, restart_required = self.create_new_lc_custom_columns( self.cli_param_list) if is_valid: if restart_required: self.lc_custom_columns_generation_label.setText( "Addition of Custom Columns Complete. Restart Calibre Now." ) self.repaint() info_dialog( self.gui, 'Automatically Add Custom Columns', 'All Selected Custom Customs Were Added If They Did Not Already Exist. Please Restart Calibre now.' ).show() else: self.lc_custom_columns_generation_label.setText( "Selected Custom Columns Already Exist. Nothing Done.") self.repaint() else: self.lc_custom_columns_generation_label.setText( "Not Completed. Please Restart Calibre, then Add Manually.") self.repaint() msg = "Fatal error experienced in adding new Custom Columns." error_dialog(self.gui, _('Automatically Add Custom Columns'), _(msg), show=True) #----------------------------------------------------------------------------------------- def create_cli_parameters(self): try: del self.cli_param_list except: pass self.cli_param_list = [] temp_list = [] cc_taglike_list = [] cc_fast_name = "" if self.mytabprefs['DDC_IS_ACTIVE'] == unicode_type(S_TRUE): cc = self.mytabprefs['DDC'] if cc > '#': cc = cc.replace('#', "").strip() cc = unicode_type(cc) temp_list.append(cc) else: error_dialog(self.gui, _('Automatically Add Custom Columns'), _('Illogical DDC Settings. Please Correct.'), show=True) return self.cli_param_list if self.mytabprefs['LCC_IS_ACTIVE'] == unicode_type(S_TRUE): cc = self.mytabprefs['LCC'] if cc > '#': cc = cc.replace('#', "").strip() cc = unicode_type(cc) temp_list.append(cc) else: error_dialog(self.gui, _('Automatically Add Custom Columns'), _('Illogical LCC Settings. Please Correct.'), show=True) return self.cli_param_list if self.mytabprefs['FAST_IS_ACTIVE'] == unicode_type(S_TRUE): cc = self.mytabprefs['FAST'] if cc > '#': cc = cc.replace('#', "").strip() cc = unicode_type(cc) temp_list.append(cc) cc_taglike_list.append(cc) cc_fast_name = cc else: error_dialog(self.gui, _('Automatically Add Custom Columns'), _('Illogical FAST Settings. Please Correct.'), show=True) return self.cli_param_list if self.mytabprefs['OCLC_IS_ACTIVE'] == unicode_type(S_TRUE): cc = self.mytabprefs['OCLC'] if cc > '#': cc = cc.replace('#', "").strip() cc = unicode_type(cc) temp_list.append(cc) else: error_dialog(self.gui, _('Automatically Add Custom Columns'), _('Illogical OCLC Settings. Please Correct.'), show=True) return self.cli_param_list if self.mytabprefs['EXTRA_AUTHOR_DETAILS_IS_ACTIVE'] == unicode_type( S_TRUE): cc = self.mytabprefs['EXTRA_AUTHOR_DETAILS'] if cc > '#': cc = cc.replace('#', "").strip() cc = unicode_type(cc) temp_list.append(cc) cc_taglike_list.append(cc) else: error_dialog( self.gui, _('Automatically Add Custom Columns'), _('Illogical LC Extra Author Details Settings. Please Correct.' ), show=True) return self.cli_param_list else: pass if len(temp_list) == 0: del temp_list error_dialog(self.gui, _('Automatically Add Custom Columns'), _('Nothing to do. Please Review Settings.'), show=True) return self.cli_param_list cc_to_add_list = [] # for each cc currently set to active, create a parameter...but only if the cc does NOT already exist... my_db, my_cursor, is_valid = self.apsw_connect_to_library() if not is_valid: error_dialog(self.gui, _('Automatically Add Custom Columns'), _('Database Connection Error. Restart Calibre.'), show=True) return self.lc_custom_columns_generation_label.setText( "...Adding Custom Columns...") self.repaint() mysql = "SELECT label,name FROM custom_columns" my_cursor.execute(mysql) tmp_rows = my_cursor.fetchall() if not tmp_rows: for cc in temp_list: cc_to_add_list.append(cc) #END FOR else: if len(tmp_rows) == 0: for cc in temp_list: cc_to_add_list.append(cc) #END FOR else: for cc in temp_list: label_already_exists = False for row in tmp_rows: label, name = row if unicode_type(label) == unicode_type(cc): label_already_exists = True break else: continue #END FOR if not label_already_exists: cc_to_add_list.append(cc) #END FOR del tmp_rows del temp_list if len(cc_to_add_list) == 0: return self.cli_param_list cc_to_add_list.sort() for label in cc_to_add_list: label = unicodedata.normalize('NFKD', label).encode('ascii', 'ignore') label = unicode_type(label) label = label.lower() name = label.upper() datatype = 'text' if label in cc_taglike_list: is_multiple = "--is-multiple" if label == cc_fast_name: name = "FAST Tags" else: name = '"LC Extra Author Details"' param = is_multiple + '|||' + label + '|||' + name + '|||' + datatype else: param = label + '|||' + name + '|||' + datatype param = param.replace("[LIBRARY]", self.lib_path) self.cli_param_list.append(param) #END FOR del cc_to_add_list return self.cli_param_list #----------------------------------------------------------------------------------------- #----------------------------------------------------------------------------------------- def apsw_connect_to_library(self): my_db = self.gui.library_view.model().db self.lib_path = my_db.library_path self.lib_path = self.lib_path.replace(os.sep, '/') if isbytestring(self.lib_path): self.lib_path = self.lib_path.decode(filesystem_encoding) path = my_db.library_path if isbytestring(path): path = path.decode(filesystem_encoding) path = path.replace(os.sep, '/') path = os.path.join(path, 'metadata.db') path = path.replace(os.sep, '/') if isbytestring(path): path = path.decode(filesystem_encoding) if path.endswith("/"): path = path[0:-1] if path.count("metadata.db") == 0: path = path + "/metadata.db" try: my_db = apsw.Connection(path) is_valid = True except Exception as e: if DEBUG: print("path to metadata.db is: ", path) if DEBUG: print("error: ", as_unicode(e)) is_valid = False return None, None, is_valid my_cursor = my_db.cursor() mysql = "PRAGMA main.busy_timeout = 5000;" #PRAGMA busy_timeout = milliseconds; my_cursor.execute(mysql) return my_db, my_cursor, is_valid #----------------------------------------------------------------------------------------- def exit_only(self): self.save_dialog_geometry() # inherited from SizePersistedDialog self.ui_exit() #----------------------------------------------------------------------------------------- def save_settings(self): self.save_dialog_geometry() # inherited from SizePersistedDialog self.mytabprefs['DDC'] = self.ddc_labelname.text() self.mytabprefs['LCC'] = self.lcc_labelname.text() self.mytabprefs['FAST'] = self.fast_labelname.text() self.mytabprefs['OCLC'] = self.oclc_labelname.text() self.mytabprefs['DDC_IS_ACTIVE'] = unicode_type( self.ddc_activate_checkbox.isChecked()) self.mytabprefs['LCC_IS_ACTIVE'] = unicode_type( self.lcc_activate_checkbox.isChecked()) self.mytabprefs['FAST_IS_ACTIVE'] = unicode_type( self.fast_activate_checkbox.isChecked()) self.mytabprefs['OCLC_IS_ACTIVE'] = unicode_type( self.oclc_activate_checkbox.isChecked()) self.mytabprefs['OCLC_IDENTIFIER'] = unicode_type( self.oclc_identifier_only_checkbox.isChecked()) label = self.mytabprefs['DDC'] label = unicode_type(label) label = unicodedata.normalize('NFKD', label).encode('ascii', 'ignore') label = label.lower().strip() if not label.startswith("#"): label = "#" + label if label == "#": label = "" self.ddc_activate_checkbox.setChecked(False) self.mytabprefs['DDC'] = unicode_type(label) label = self.mytabprefs['LCC'] label = unicode_type(label) label = unicodedata.normalize('NFKD', label).encode('ascii', 'ignore') label = label.lower().strip() if not label.startswith("#"): label = "#" + label if label == "#": label = "" self.lcc_activate_checkbox.setChecked(False) self.mytabprefs['LCC'] = unicode_type(label) label = self.mytabprefs['FAST'] label = unicode_type(label) label = unicodedata.normalize('NFKD', label).encode('ascii', 'ignore') label = label.lower().strip() if not label.startswith("#"): label = "#" + label if label == "#": label = "" self.fast_activate_checkbox.setChecked(False) self.mytabprefs['FAST'] = unicode_type(label) label = self.mytabprefs['OCLC'] label = unicode_type(label) label = unicodedata.normalize('NFKD', label).encode('ascii', 'ignore') label = label.lower().strip() if not label.startswith("#"): label = "#" + label if label == "#": label = "" self.oclc_activate_checkbox.setChecked(False) self.mytabprefs['OCLC'] = unicode_type(label) if self.mytabprefs['DDC'] == unicode_type( "") and self.mytabprefs['LCC'] == unicode_type( "") and self.mytabprefs['FAST'] == unicode_type( "") and self.mytabprefs['OCLC'] == unicode_type(""): self.mytabprefs['DDC'] = unicode_type("#ddc") self.mytabprefs['LCC'] = unicode_type("#lcc") self.mytabprefs['FAST'] = unicode_type("#fast") self.mytabprefs['OCLC'] = unicode_type("#oclc_owi") else: if self.mytabprefs['DDC'] == unicode_type( "") and self.mytabprefs['LCC'] == unicode_type(""): self.oclc_identifier_only_checkbox.setChecked(False) #--------------------------------------- s = unicode_type(self.lc_genre_labelname.text()) s = s.strip() if s.startswith("#") and len(s) > 1: self.mytabprefs['GENRE'] = unicode_type(s) self.mytabprefs['GENRE_DDC_IS_ACTIVE'] = unicode_type( self.lc_genre_ddc_checkbox.isChecked()) self.mytabprefs['GENRE_LCC_IS_ACTIVE'] = unicode_type( self.lc_genre_lcc_checkbox.isChecked()) self.mytabprefs['GENRE_IS_INACTIVE'] = unicode_type( self.lc_genre_inactive_checkbox.isChecked()) self.mytabprefs['GENRE_EXACT_MATCH'] = unicode_type( self.lc_exact_match_checkbox.isChecked()) self.mytabprefs['GENRE_LCC_MATCH_LENGTH'] = self.spin_lcc.value() else: self.mytabprefs['GENRE'] = unicode_type("#genre") self.lc_genre_labelname.setText(unicode_type("#genre")) self.lc_genre_ddc_checkbox.setChecked(False) self.lc_genre_lcc_checkbox.setChecked(False) self.lc_genre_inactive_checkbox.setChecked(True) self.mytabprefs['GENRE_DDC_IS_ACTIVE'] = unicode_type(S_FALSE) self.mytabprefs['GENRE_LCC_IS_ACTIVE'] = unicode_type(S_FALSE) self.mytabprefs['GENRE_IS_INACTIVE'] = unicode_type(S_TRUE) self.mytabprefs['GENRE_EXACT_MATCH'] = unicode_type(S_TRUE) self.mytabprefs['GENRE_LCC_MATCH_LENGTH'] = 2 self.repaint() sleep(2) #--------------------------------------- #~ for k,v in self.mytabprefs.iteritems(): for k, v in iteritems(self.mytabprefs): v = unicode_type(v) v = v.strip() prefs[k] = v #END FOR prefs #--------------------------------------- self.ddc_labelname.setText(self.mytabprefs['DDC']) self.lcc_labelname.setText(self.mytabprefs['LCC']) self.fast_labelname.setText(self.mytabprefs['FAST']) self.oclc_labelname.setText(self.mytabprefs['OCLC']) self.repaint() sleep(0) #~ for k,v in self.mytabprefs.iteritems(): for k, v in iteritems(self.mytabprefs): self.param_dict[k] = v #END FOR number_active = 0 if self.mytabprefs['DDC_IS_ACTIVE'] == unicode_type(S_TRUE): number_active = number_active + 1 if self.mytabprefs['LCC_IS_ACTIVE'] == unicode_type(S_TRUE): number_active = number_active + 1 if self.mytabprefs['FAST_IS_ACTIVE'] == unicode_type(S_TRUE): number_active = number_active + 1 if self.mytabprefs['OCLC_IS_ACTIVE'] == unicode_type(S_TRUE): number_active = number_active + 1 self.ddc_name = self.mytabprefs['DDC'].replace("#", "").strip() self.lcc_name = self.mytabprefs['LCC'].replace("#", "").strip() self.fast_name = self.mytabprefs['FAST'].replace("#", "").strip() self.oclc_name = self.mytabprefs['OCLC'].replace("#", "").strip() if self.oclc_identifier_only_checkbox.isChecked(): self.oclc_identifier_is_desired = True else: self.oclc_identifier_is_desired = False return number_active #----------------------------------------------------------------------------------------- #----------------------------------------------------------------------------------------- def create_new_lc_custom_columns(self, execution_param_list): if len(self.cli_param_list) == 0: return True, False # successful since the labels already exist; no restart is required. dbpath = self.lib_path was_successful = True restart_required = True for param in execution_param_list: try: lc_cli_add_custom_column(self.guidb, param, dbpath) except Exception as e: if DEBUG: print("Exception: ", as_unicode(e)) was_successful = False break #END FOR return was_successful, restart_required #----------------------------------------------------------------------------------------- #----------------------------------------------------------------------------------------- #----------------------------------------------------------------------------------------- #----------------------------------------------------------------------------------------- #----------------------------------------------------------------------------------------- #----------------------------------------------------------------------------------------- #END of library_codes_dialog.py
class EbookScramble(QDialog): ''' Read an EPUB/KEPUB/AZW3 de-DRM'd ebook file and scramble various contents ''' def __init__(self, pathtoebook, book_id=None, from_calibre=False, dsettings={}, calibre_libpaths=[], parent=None): QDialog.__init__(self, parent=parent) self.gui = parent self.pathtoebook = pathtoebook self.book_id = book_id self.from_calibre = from_calibre self.calibre_libpaths = calibre_libpaths self.dsettings = MR_SETTINGS.copy() self.dsettings.update(dsettings) self.ebook = None self.eborig = None self.cleanup_dirs = [] self.cleanup_files = [] self.log = [] self.rename_file_map = {} self.meta, self.errors = {}, {} self.is_scrambled = False self.dummyimg = None self.dummysvg = '' self.setWindowTitle(CAPTION) self.setWindowIcon(get_icons('images/plugin_icon.png')) # create widgets self.buttonBox = QDialogButtonBox(QDialogButtonBox.Save | QDialogButtonBox.Cancel) self.buttonBox.button( QDialogButtonBox.Save).setText('Save scrambled ebook && Exit') self.browser = QTextBrowser() self.browser.setText('') self.browser.setLineWrapMode(QTextBrowser.NoWrap) self.browser.setMinimumWidth(600) self.browser.setMinimumHeight(150) self.browser.setReadOnly(True) self.savefile = QLineEdit() self.savefile.setReadOnly(True) self.sourcefile = QLineEdit() self.sourcefile.setMinimumWidth(100) self.sourcefile.setReadOnly(True) self.browsesource = QPushButton('...') self.browsesource.setMaximumWidth(30) about_button = QPushButton('About', self) self.runButton = QPushButton('Scramble now') previewButton = QPushButton('Preview content') if Webview is None: previewButton.setEnabled(False) previewButton.setToolTip( 'Preview not currently available for this book') configButton = QPushButton('Change rules *') configButton.setToolTip( 'Only available in standalone version, not calibre plugin') metadataButton = QPushButton('View metadata *') metadataButton.setToolTip( 'Only available in standalone version, not calibre plugin') errorsButton = QPushButton('View errors *') errorsButton.setToolTip( 'Only available in standalone version, not calibre plugin') # layout widgets gpsource = QGroupBox('Source ebook:') laysource = QGridLayout() gpsource.setLayout(laysource) laysource.addWidget(self.sourcefile, 0, 0) laysource.addWidget(self.browsesource, 0, 1) gptarget = QGroupBox('Scrambled ebook:') laytarget = QGridLayout() gptarget.setLayout(laytarget) laytarget.addWidget(self.savefile, 0, 0) gpaction = QGroupBox('Actions:') layaction = QVBoxLayout() gpaction.setLayout(layaction) layaction.addWidget(self.runButton) layaction.addStretch() layaction.addWidget(previewButton) layaction.addStretch() gpextras = QGroupBox('Extras:') layaction2 = QVBoxLayout() gpextras.setLayout(layaction2) layaction2.addWidget(configButton) layaction2.addWidget(metadataButton) layaction2.addWidget(errorsButton) layaction3 = QVBoxLayout() layaction3.addWidget(about_button) layaction3.addStretch() layaction3.addWidget(gpextras) grid = QGridLayout() grid.addWidget(self.browser, 0, 0) grid.addLayout(layaction3, 0, 1) grid.addWidget(gpsource, 2, 0) grid.addWidget(gptarget, 3, 0) grid.addWidget(gpaction, 2, 1, 2, 1) grid.addWidget(self.buttonBox, 5, 0, 1, 2) self.setLayout(grid) # create connect signals/slots about_button.clicked.connect(self.about_button_clicked) self.runButton.clicked.connect(self.create_scramble_book) previewButton.clicked.connect(self.preview_ebook) configButton.clicked.connect(self.change_rules) metadataButton.clicked.connect(self.view_metadata) errorsButton.clicked.connect(self.view_errors) self.browsesource.clicked.connect(self.choose_source_ebook) self.buttonBox.accepted.connect(self.accept) self.buttonBox.rejected.connect(self.reject) if self.from_calibre: gpextras.setVisible( False) # Extras not available in calibre plugin self.browsesource.setVisible( False) # ebook file selection done by calibre self.initialise_new_file(self.pathtoebook) def initialise_new_file(self, pathtoebook): self.meta, self.errors = {}, {} self.rename_file_map = {} self.is_scrambled = False self.dummyimg = None self.dummysvg = '' self.runButton.setEnabled(True) self.buttonBox.button(QDialogButtonBox.Save).setEnabled(False) fileok = True if not os.path.isfile(pathtoebook): fileok = False else: try: self.ebook = get_container(pathtoebook) except: fileok = False msg = "Source ebook must be de-DRM'd and in one of these formats:" \ "\n- azw3\n- epub\n- kepub\n- kepub.epub.\n\nPlease select another." error_dialog(self, CAPTION, msg, show=True, show_copy_button=True) if not fileok: self.log.append('No ebook selected yet') else: self.cleanup_dirs.append(self.ebook.root) tdir = PersistentTemporaryDirectory('_scramble_clone_orig') self.cleanup_dirs.append(tdir) self.eborig = clone_container(self.ebook, tdir) dirn, fname, ext, is_kepub_epub = get_fileparts( self.ebook.path_to_ebook) ext = ext.lower() format = 'kepub' if is_kepub_epub else ext if self.book_id is not None: # calibre library book self.cleanup_files.append(self.ebook.path_to_ebook) sourcepath = self.ebook.path_to_ebook self.dummyimg = get_resources('images/' + format + '.png') self.dummysvg = get_resources('images/' + format + '.svg') if self.from_calibre: # calibre plugin self.dirout = '' else: # standalone version self.dirout = dirn self.log.append('\n--- New ebook: %s' % sourcepath) fn = fname + '_scrambled.' fn += 'kepub.' + ext if is_kepub_epub else ext self.fname_scrambled_ebook = ascii_text(fn) self.sourcefile.setText(sourcepath) self.savefile.setText(self.fname_scrambled_ebook) self.meta['orig'] = get_metadata(self.ebook) self.errors['orig'] = get_run_check_error(self.ebook) self.viewlog() def accept(self): # Any accept actions which need to be done before returning to caller savedir = self.choose_save_dir(self.dirout) if savedir is not None: self.buttonBox.button(QDialogButtonBox.Save).setText('Saving ...') self.buttonBox.button(QDialogButtonBox.Save).setEnabled(False) msg = '' if self.ebook.book_type.lower() == 'azw3': msg = '\n ... please note, rebuilding an AZW3 may take a little longer ...' self.log.append('\nSaving now ... %s' % msg) self.viewlog() path_to_scrambled_ebook = os.path.join(savedir, self.fname_scrambled_ebook) self.ebook.commit(path_to_scrambled_ebook) self.cleanup() QDialog.accept(self) def reject(self): self.cleanup() QDialog.reject(self) def cleanup(self): # delete calibre plugin temp files if self.book_id: for f in self.cleanup_files: try: os.remove(f) except: pass if self.from_calibre: for d in self.cleanup_dirs: try: shutil.rmtree(d) except: pass def choose_save_dir(self, default_dir): savedir = None askagain = True no_save_dir = False if default_dir: no_save_dir = True title = _('Choose destination directory for scrambled ebook') while askagain: savedir = choose_dir(window=self, name='', title=title, default_dir=default_dir, no_save_dir=no_save_dir) askagain = False if savedir is not None: savedir = os.path.normpath(savedir) if savedir.startswith(tuple(self.calibre_libpaths)): askagain = True msg = [] msg.append( 'You have selected a destination inside your Calibre library.' ) msg.append(savedir) msg.append('\nThis is NOT recommended. Try again.') msg.append('\nPlease avoid the following:') [ msg.append(path) for path in sorted(self.calibre_libpaths) ] warning_dialog(self, 'Calibre library chosen', '\n'.join(msg), show=True, show_copy_button=True) return savedir def choose_source_ebook(self): sf = self.sourcefile.text() seldir = get_fileparts(sf)[0] if sf else '' title = _('Select source ebook') selfiles = choose_files(self, name='', title=title, filters=[('Ebooks', ['epub', 'kepub', 'azw3'])], select_only_single_file=True, default_dir=seldir) if selfiles: self.pathtoebook = os.path.normpath(selfiles[0]) self.initialise_new_file(self.pathtoebook) def create_scramble_book(self): if self.ebook is None: return sf = self.sourcefile.text() self.log.append('\nScrambling %s ...' % sf) self.viewlog() scrambler = EbookScrambleAction(self.ebook, self.dsettings, self.dummyimg, self.dummysvg) self.rename_file_map = { k: v for (k, v) in iteritems(scrambler.file_map) } self.meta['scramb'] = get_metadata(self.ebook) self.errors['scramb'] = get_run_check_error(self.ebook) self.buttonBox.button(QDialogButtonBox.Save).setEnabled(True) self.runButton.setEnabled(False) self.is_scrambled = True self.log.append(scrambler.results) self.log.append('\n... finished') self.viewlog() def change_rules(self): dlg = EbookScrambleRulesDlg(self.dsettings, parent=self.gui) if dlg.exec_(): self.dsettings.update(dlg.dsettings) self.log.append('\n--- Scrambling rules updated ---') self.viewlog() def preview_ebook(self): if self.ebook is None: return dlg = EbookScramblePreviewDlg(self.ebook, self.eborig, self.is_scrambled, self.rename_file_map, parent=self.gui) dlg.exec_() dlg.raise_() def view_metadata(self): if self.ebook is None: return dlg = EbookScrambleMetadataDlg(self.meta, parent=self.gui) dlg.exec_() dlg.raise_() def view_errors(self): if self.ebook is None: return dlg = EbookScrambleErrorsDlg(self.errors, parent=self.gui) dlg.exec_() dlg.raise_() def display_settings(self): self.log.append('\nCurrent Scramble rules:') [ self.log.append('%s: %s' % (k, v)) for (k, v) in sorted(iteritems(self.dsettings)) ] def viewlog(self): self.browser.setText('\n'.join(self.log)) self.browser.moveCursor(QTextCursor.End) QApplication.instance().processEvents() def about_button_clicked(self): # Get the about text from a file inside the plugin zip file # The get_resources function is a builtin function defined for all your # plugin code. It loads files from the plugin zip file. It returns # the bytes from the specified file. # # Note that if you are loading more than one file, for performance, you # should pass a list of names to get_resources. In this case, # get_resources will return a dictionary mapping names to bytes. Names that # are not found in the zip file will not be in the returned dictionary. source = 'calibre plugin' if self.from_calibre else 'standalone' text = get_resources('about.txt') QMessageBox.about(self, 'About %s %s' % (CAPTION, source), text)