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))
Exemple #2
0
    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)
Exemple #3
0
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)
Exemple #4
0
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)
Exemple #5
0
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)