Example #1
0
    def __exit__(self, *exc):
        ret = super().__exit__(*exc)

        # build table here
        layout = QGridLayout()
        for y, row in enumerate(self.rows):
            x = 0
            for col in row.cols:
                layout.addLayout(col.layout, y, x, 1, col.colspan)
                x += col.colspan

        qDlgStackTop().addChild(layout)
        return ret
Example #2
0
    def __init__(self, parent=None):
        super().__init__(parent)

        self.lineEditPPScannos = QLineEdit()
        self.lineEditPPScannos.setReadOnly(True)
        self.comboScannoFiles = QComboBox()
        
        labelPPScannosLoc = QLabel('PPScannos File')
        labelPPScannosLoc.setBuddy(self.lineEditPPScannos)
        labelScannoLoc = QLabel('Default Scanno File')
        labelScannoLoc.setBuddy(self.comboScannoFiles)

        self.buttonPPScannos = QPushButton('Change')
        self.buttonPPScannos.pressed.connect(self.openFileDlg)
        
        hbox = QHBoxLayout()
        hbox.addWidget(labelPPScannosLoc)
        hbox.addWidget(self.lineEditPPScannos)
        hbox.addWidget(self.buttonPPScannos)
        
        self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)

        self.buttonBox.accepted.connect(self.accept)
        self.buttonBox.rejected.connect(self.reject)

        mainLayout = QGridLayout()
        mainLayout.addLayout(hbox, 0, 0, 2, 0)
        mainLayout.addWidget(labelScannoLoc, 1, 0)
        mainLayout.addWidget(self.comboScannoFiles, 1, 1)
        mainLayout.addWidget(self.buttonBox, 5, 0, 1, 2)
        self.setLayout(mainLayout)

        self.populate()
        
        self.lineEditPPScannos.textChanged.connect(self.ppscannosChanged)

        self.setWindowTitle("Configure Gui Scannos")
        self.resize(650, 400)
Example #3
0
class Config(QDialog):
    '''
    Configuration dialog for single book conversion. If accepted, has the
    following important attributes

    output_format - Output format (without a leading .)
    input_format  - Input format (without a leading .)
    opf_path - Path to OPF file with user specified metadata
    cover_path - Path to user specified cover (can be None)
    recommendations - A pickled list of 3 tuples in the same format as the
    recommendations member of the Input/Output plugins.
    '''
    def __init__(self,
                 parent,
                 db,
                 book_id,
                 preferred_input_format=None,
                 preferred_output_format=None):
        QDialog.__init__(self, parent)
        self.setupUi()
        self.opt_individual_saved_settings.setVisible(False)
        self.db, self.book_id = db, book_id

        self.setup_input_output_formats(self.db, self.book_id,
                                        preferred_input_format,
                                        preferred_output_format)
        self.setup_pipeline()

        self.input_formats.currentIndexChanged[native_string_type].connect(
            self.setup_pipeline)
        self.output_formats.currentIndexChanged[native_string_type].connect(
            self.setup_pipeline)
        self.groups.setSpacing(5)
        self.groups.activated[(QModelIndex)].connect(self.show_pane)
        self.groups.clicked[(QModelIndex)].connect(self.show_pane)
        self.groups.entered[(QModelIndex)].connect(self.show_group_help)
        rb = self.buttonBox.button(self.buttonBox.RestoreDefaults)
        rb.setText(_('Restore &defaults'))
        rb.clicked.connect(self.restore_defaults)
        self.groups.setMouseTracking(True)
        geom = gprefs.get('convert_single_dialog_geom', None)
        if geom:
            self.restoreGeometry(geom)
        else:
            self.resize(self.sizeHint())

    def setupUi(self):
        self.setObjectName("Dialog")
        self.resize(1024, 700)
        self.setWindowIcon(QIcon(I('convert.png')))
        self.gridLayout = QGridLayout(self)
        self.gridLayout.setObjectName("gridLayout")
        self.horizontalLayout = QHBoxLayout()
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.input_label = QLabel(self)
        self.input_label.setObjectName("input_label")
        self.horizontalLayout.addWidget(self.input_label)
        self.input_formats = QComboBox(self)
        self.input_formats.setSizeAdjustPolicy(
            QComboBox.AdjustToMinimumContentsLengthWithIcon)
        self.input_formats.setMinimumContentsLength(5)
        self.input_formats.setObjectName("input_formats")
        self.horizontalLayout.addWidget(self.input_formats)
        self.opt_individual_saved_settings = QCheckBox(self)
        self.opt_individual_saved_settings.setObjectName(
            "opt_individual_saved_settings")
        self.horizontalLayout.addWidget(self.opt_individual_saved_settings)
        spacerItem = QSpacerItem(40, 20, QSizePolicy.Expanding,
                                 QSizePolicy.Minimum)
        self.horizontalLayout.addItem(spacerItem)
        self.label_2 = QLabel(self)
        self.label_2.setObjectName("label_2")
        self.horizontalLayout.addWidget(self.label_2)
        self.output_formats = QComboBox(self)
        self.output_formats.setSizeAdjustPolicy(
            QComboBox.AdjustToMinimumContentsLengthWithIcon)
        self.output_formats.setMinimumContentsLength(5)
        self.output_formats.setObjectName("output_formats")
        self.horizontalLayout.addWidget(self.output_formats)
        self.gridLayout.addLayout(self.horizontalLayout, 0, 0, 1, 2)
        self.groups = QListView(self)
        sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        sizePolicy.setHorizontalStretch(1)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(
            self.groups.sizePolicy().hasHeightForWidth())
        self.groups.setSizePolicy(sizePolicy)
        self.groups.setTabKeyNavigation(True)
        self.groups.setIconSize(QSize(48, 48))
        self.groups.setWordWrap(True)
        self.groups.setObjectName("groups")
        self.gridLayout.addWidget(self.groups, 1, 0, 3, 1)
        self.scrollArea = QScrollArea(self)
        sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        sizePolicy.setHorizontalStretch(4)
        sizePolicy.setVerticalStretch(10)
        sizePolicy.setHeightForWidth(
            self.scrollArea.sizePolicy().hasHeightForWidth())
        self.scrollArea.setSizePolicy(sizePolicy)
        self.scrollArea.setFrameShape(QFrame.NoFrame)
        self.scrollArea.setLineWidth(0)
        self.scrollArea.setWidgetResizable(True)
        self.scrollArea.setObjectName("scrollArea")
        self.scrollAreaWidgetContents = QWidget()
        self.scrollAreaWidgetContents.setGeometry(QRect(0, 0, 810, 494))
        self.scrollAreaWidgetContents.setObjectName("scrollAreaWidgetContents")
        self.verticalLayout_3 = QVBoxLayout(self.scrollAreaWidgetContents)
        self.verticalLayout_3.setContentsMargins(0, 0, 0, 0)
        self.verticalLayout_3.setObjectName("verticalLayout_3")
        self.stack = QStackedWidget(self.scrollAreaWidgetContents)
        sizePolicy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(
            self.stack.sizePolicy().hasHeightForWidth())
        self.stack.setSizePolicy(sizePolicy)
        self.stack.setObjectName("stack")
        self.page = QWidget()
        self.page.setObjectName("page")
        self.stack.addWidget(self.page)
        self.page_2 = QWidget()
        self.page_2.setObjectName("page_2")
        self.stack.addWidget(self.page_2)
        self.verticalLayout_3.addWidget(self.stack)
        self.scrollArea.setWidget(self.scrollAreaWidgetContents)
        self.gridLayout.addWidget(self.scrollArea, 1, 1, 1, 1)
        self.buttonBox = QDialogButtonBox(self)
        self.buttonBox.setOrientation(Qt.Horizontal)
        self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel
                                          | QDialogButtonBox.Ok
                                          | QDialogButtonBox.RestoreDefaults)
        self.buttonBox.setObjectName("buttonBox")
        self.gridLayout.addWidget(self.buttonBox, 3, 1, 1, 1)
        self.help = QTextEdit(self)
        sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(
            self.help.sizePolicy().hasHeightForWidth())
        self.help.setSizePolicy(sizePolicy)
        self.help.setMaximumHeight(80)
        self.help.setObjectName("help")
        self.gridLayout.addWidget(self.help, 2, 1, 1, 1)
        self.input_label.setBuddy(self.input_formats)
        self.label_2.setBuddy(self.output_formats)
        self.input_label.setText(_("&Input format:"))
        self.opt_individual_saved_settings.setText(
            _("Use &saved conversion settings for individual books"))
        self.label_2.setText(_("&Output format:"))

        self.buttonBox.accepted.connect(self.accept)
        self.buttonBox.rejected.connect(self.reject)

    def sizeHint(self):
        desktop = QCoreApplication.instance().desktop()
        geom = desktop.availableGeometry(self)
        nh, nw = max(300, geom.height() - 100), max(400, geom.width() - 70)
        return QSize(nw, nh)

    def restore_defaults(self):
        delete_specifics(self.db, self.book_id)
        self.setup_pipeline()

    @property
    def input_format(self):
        return unicode_type(self.input_formats.currentText()).lower()

    @property
    def output_format(self):
        return unicode_type(self.output_formats.currentText()).lower()

    @property
    def manually_fine_tune_toc(self):
        for i in range(self.stack.count()):
            w = self.stack.widget(i)
            if hasattr(w, 'manually_fine_tune_toc'):
                return w.manually_fine_tune_toc.isChecked()

    def setup_pipeline(self, *args):
        oidx = self.groups.currentIndex().row()
        input_format = self.input_format
        output_format = self.output_format
        self.plumber = create_dummy_plumber(input_format, output_format)

        def widget_factory(cls):
            return cls(self.stack, self.plumber.get_option_by_name,
                       self.plumber.get_option_help, self.db, self.book_id)

        self.mw = widget_factory(MetadataWidget)
        self.setWindowTitle(
            _('Convert') + ' ' + unicode_type(self.mw.title.text()))
        lf = widget_factory(LookAndFeelWidget)
        hw = widget_factory(HeuristicsWidget)
        sr = widget_factory(SearchAndReplaceWidget)
        ps = widget_factory(PageSetupWidget)
        sd = widget_factory(StructureDetectionWidget)
        toc = widget_factory(TOCWidget)
        from calibre.gui2.actions.toc_edit import SUPPORTED
        toc.manually_fine_tune_toc.setVisible(
            output_format.upper() in SUPPORTED)
        debug = widget_factory(DebugWidget)

        output_widget = self.plumber.output_plugin.gui_configuration_widget(
            self.stack, self.plumber.get_option_by_name,
            self.plumber.get_option_help, self.db, self.book_id)
        input_widget = self.plumber.input_plugin.gui_configuration_widget(
            self.stack, self.plumber.get_option_by_name,
            self.plumber.get_option_help, self.db, self.book_id)
        while True:
            c = self.stack.currentWidget()
            if not c:
                break
            self.stack.removeWidget(c)

        widgets = [self.mw, lf, hw, ps, sd, toc, sr]
        if input_widget is not None:
            widgets.append(input_widget)
        if output_widget is not None:
            widgets.append(output_widget)
        widgets.append(debug)
        for w in widgets:
            self.stack.addWidget(w)
            w.set_help_signal.connect(self.help.setPlainText)

        self._groups_model = GroupModel(widgets)
        self.groups.setModel(self._groups_model)

        idx = oidx if -1 < oidx < self._groups_model.rowCount() else 0
        self.groups.setCurrentIndex(self._groups_model.index(idx))
        self.stack.setCurrentIndex(idx)
        try:
            shutil.rmtree(self.plumber.archive_input_tdir, ignore_errors=True)
        except:
            pass

    def setup_input_output_formats(self, db, book_id, preferred_input_format,
                                   preferred_output_format):
        if preferred_output_format:
            preferred_output_format = preferred_output_format.upper()
        output_formats = get_output_formats(preferred_output_format)
        input_format, input_formats = get_input_format_for_book(
            db, book_id, preferred_input_format)
        preferred_output_format = preferred_output_format if \
            preferred_output_format in output_formats else \
            sort_formats_by_preference(output_formats,
                    [prefs['output_format']])[0]
        self.input_formats.addItems(
            (unicode_type(x.upper()) for x in input_formats))
        self.output_formats.addItems(
            (unicode_type(x.upper()) for x in output_formats))
        self.input_formats.setCurrentIndex(input_formats.index(input_format))
        self.output_formats.setCurrentIndex(
            output_formats.index(preferred_output_format))

    def show_pane(self, index):
        self.stack.setCurrentIndex(index.row())

    def accept(self):
        recs = GuiRecommendations()
        for w in self._groups_model.widgets:
            if not w.pre_commit_check():
                return
            x = w.commit(save_defaults=False)
            recs.update(x)
        self.opf_file, self.cover_file = self.mw.opf_file, self.mw.cover_file
        self._recommendations = recs
        if self.db is not None:
            recs['gui_preferred_input_format'] = self.input_format
            save_specifics(self.db, self.book_id, recs)
        self.break_cycles()
        QDialog.accept(self)

    def reject(self):
        self.break_cycles()
        QDialog.reject(self)

    def done(self, r):
        if self.isVisible():
            gprefs['convert_single_dialog_geom'] = \
                bytearray(self.saveGeometry())
        return QDialog.done(self, r)

    def break_cycles(self):
        for i in range(self.stack.count()):
            w = self.stack.widget(i)
            w.break_cycles()

    @property
    def recommendations(self):
        recs = [(k, v, OptionRecommendation.HIGH)
                for k, v in self._recommendations.items()]
        return recs

    def show_group_help(self, index):
        widget = self._groups_model.widgets[index.row()]
        self.help.setPlainText(widget.HELP)
Example #4
0
    def createWindow(self):
        self.status = True
        self.cateStatus = ''
        self.sizes = ''
        self.setWindowTitle(self.title)
        self.item = ''
        self.color = ''

        #Button
        self.button1 = QPushButton('Start Running', self)
        self.button1.clicked.connect(
            lambda: search.run(self, self.cateStatus, self.status, self.sizes,
                               self.color, self.item))
        self.button2 = QPushButton('Purchase', self)
        self.button2.clicked.connect(lambda: search.purchase(self))
        self.button3 = QPushButton('Close', self)
        self.button3.clicked.connect(lambda: self.close())
        self.button4 = QPushButton('Add item', self)

        #Dropdown menu
        Options = ['', 'True', 'False']
        self.TF = QComboBox(self)
        self.TF.addItems(Options)
        self.TF.activated[str].connect(self.changeOption)

        CateOptions = [
            '', 'jackets', 'shirts', 'tops/sweaters', 'sweatshirts', 'pants',
            'shorts', 't-shirts', 'hats', 'bags', 'accessories'
        ]
        self.cateBox = QComboBox(self)
        self.cateBox.addItems(CateOptions)
        self.cateBox.activated[str].connect(self.changeCateOption)

        sizeOption = ['', 'Small', 'Medium', 'Large']
        self.sizeBox = QComboBox(self)
        self.sizeBox.addItems(sizeOption)
        self.sizeBox.activated[str].connect(self.changeSize)

        itemList = self.item_list
        self.itemBox = QComboBox(self)
        self.itemBox.addItems(itemList)
        self.itemBox.activated[str].connect(self.changeitem)

        #Textbox
        self.EnterColor = QLineEdit(self)
        self.EnterColor.textChanged[str].connect(self.changeColor)

        #Label
        self.Name = QLabel('Name: ', self)
        self.Name.setFont(QtGui.QFont('Microsoft YaHei', 9))

        self.Category = QLabel('Category: ', self)
        self.Category.setFont(QtGui.QFont('Microsoft YaHei', 9))

        self.Sizes = QLabel('Size: ', self)
        self.Sizes.setFont(QtGui.QFont('Microsoft YaHei', 9))

        self.color = QLabel('Color(True/False): ', self)
        self.color.setFont(QtGui.QFont('Microsoft YaHei', 9))

        self.whatColor = QLabel('Color: ', self)
        self.whatColor.setFont(QtGui.QFont('Microsoft YaHei', 9))

        #Vbox
        Operation = QVBoxLayout()
        Operation.addWidget(self.button4)
        Operation.addWidget(self.button1)
        Operation.addWidget(self.button2)
        Operation.addWidget(self.button3)
        Structure = QGridLayout()
        Structure.addLayout(Operation, 4, 0)
        Structure.addWidget(self.Name, 0, 0)
        Structure.addWidget(self.itemBox, 0, 1)
        Structure.addWidget(self.Category, 1, 0)
        Structure.addWidget(self.cateBox, 1, 1)
        Structure.addWidget(self.Sizes, 2, 0)
        Structure.addWidget(self.sizeBox, 2, 1)
        Structure.addWidget(self.color, 3, 0)
        Structure.addWidget(self.TF, 3, 1)
        Structure.addWidget(self.whatColor, 3, 2)
        Structure.addWidget(self.EnterColor, 3, 3)

        #Display
        self.setLayout(Structure)
        self.show()
Example #5
0
class Window(QWidget):
    def __init__(self, cc):
        super().__init__()

        self.camCtr = cc
        self.initUI()

    def initUI(self):
        self.setWindowTitle('Camera Control')
        self.setMinimumWidth(260)
        self.grid = QGridLayout()
        self.grid.setContentsMargins(4, 4, 4, 4)
        self.grid.setSpacing(2)

        self.lbls = {}
        self.sls = {}
        self.buttons = {}
        self.button_status = {}
        row = 1
        for name, c in self.camCtr.ctrls.items():
            txt = name.replace("_", " ").capitalize() + ":"
            txt = txt.replace("White balance temperature", "WB temperature")
            lbl = QLabel(txt)
            self.grid.addWidget(lbl, row, 1, 1, 1, Qt.AlignRight)
            self.lbls[name] = lbl
            if c["type"] == "bool":
                button = QPushButton()
                if c["value"]:
                    button.setText('ON')
                    button.setStyleSheet("background-color: green")
                    self.button_status[name] = True
                else:
                    button.setText('OFF')
                    button.setStyleSheet("background-color: red")
                    self.button_status[name] = False
                button.clicked.connect(
                    lambda val, name=name: self.button_Change(val, name))
                self.grid.addWidget(button, row, 2, 1, 1, Qt.AlignCenter)
                self.buttons[name] = button
            else:
                sl = QSlider()
                sl.setOrientation(Qt.Horizontal)
                sl.setMaximum(c["max"])
                sl.setMinimum(c["min"])
                sl.valueChanged.connect(
                    lambda val, name=name: self.sl_Change(val, name))
                sl.setMinimumWidth(250)
                sl.setTickInterval(c.get("step", 1))
                sl.setValue(c["value"])
                if c.get("flags", "") == "inactive":
                    lbl.setDisabled(True)
                    sl.setDisabled(True)
                self.grid.addWidget(sl, row, 2, 1, 1, Qt.AlignRight)
                self.sls[name] = sl
            row += 1

        self.actions_grid = QGridLayout()
        self.actions = {}
        for i, action in enumerate(
            ("default", "sync", "update", "load", "save")):
            b = QPushButton()
            b.setText(action.capitalize())
            b.clicked.connect(
                lambda _, action=action: self.action_Slot(action))
            self.actions_grid.addWidget(b, 1, i, 1, 1, Qt.AlignCenter)
        self.grid.addLayout(self.actions_grid, row, 1, 1, 2, Qt.AlignCenter)

        self.mainLayout = QHBoxLayout()
        self.mainLayout.addLayout(self.grid)
        self.setLayout(self.mainLayout)
        self.show()

    def sl_Change(self, val, name):
        if not self.camCtr.setValue(name, val):
            # Give user feedback if parameter was not
            # accepted. Unfortunately, not all invalid parameters are
            # actually flagged.
            self.sync()
            return

    def sync(self):
        self.camCtr.get_ctls()
        for name, c in self.camCtr.ctrls.items():
            if c["type"] == "bool":
                if c["value"]:
                    self.buttons[name].setText('ON')
                    self.buttons[name].setStyleSheet("background-color: green")
                    self.button_status[name] = True
                else:
                    self.buttons[name].setText('OFF')
                    self.buttons[name].setStyleSheet("background-color: red")
                    self.button_status[name] = False
            else:
                self.sls[name].setValue(c["value"])
                if c.get("flags", "") == "inactive":
                    self.sls[name].setDisabled(True)
                    self.lbls[name].setDisabled(True)
                else:
                    self.sls[name].setDisabled(False)
                    self.lbls[name].setDisabled(False)

    def button_Change(self, _, name):
        if self.button_status[name]:
            self.buttons[name].setText('OFF')
            self.buttons[name].setStyleSheet("background-color: red")
            self.button_status[name] = False
            self.camCtr.setValue(name, "0")
        else:
            self.buttons[name].setText('ON')
            self.buttons[name].setStyleSheet("background-color: green")
            self.button_status[name] = True
            self.camCtr.setValue(name, "1")
        # Check if any controls have become enabled.
        self.sync()

    def action_Slot(self, name):
        if name == "default":
            self.camCtr.update(reset=True)
        elif name == "update":
            self.camCtr.update()
        elif name == "save" or name == "load":
            conf_dir = os.path.join(Path.home(), ".config", "cam_control")
            Path(conf_dir).mkdir(parents=True, exist_ok=True)
            if name == "save":
                file_name, _ = QFileDialog.getSaveFileName(
                    self, "Save Cam Control configuration", conf_dir,
                    "Cam Control Conf (*.ccconf)")
                if file_name:
                    if not file_name.endswith(".ccconf"):
                        file_name += ".ccconf"
                    with open(file_name, "w") as fd:
                        json.dump(self.camCtr.ctrls, fd)
            else:
                file_name, _ = QFileDialog.getOpenFileName(
                    self, "Load Cam Control configuration", conf_dir,
                    "Cam Control Conf (*.ccconf)")
                if file_name:
                    with open(file_name, "r") as fd:
                        self.camCtr.ctrls = json.load(fd)
                    self.camCtr.update()
        self.sync()
class M3Dashboard(Dashboard):
    """
    Dashboard for Mekabot

    :param context: the plugin context
    :type context: qt_gui.plugin.Plugin
    """
    def setup(self, context):
        self.name = 'Meka Dashboard'
        self.max_icon_size = QSize(50, 30)
        
        self._widget_initialized = False
        self._service_ready = True
        self._last_dashboard_message_time = rospy.Time.now()
        self._path = os.path.join(rospkg.RosPack().get_path('rqt_m3dashboard'), 'images')
        self._battery_icons = {}
        self._state_buttons = {}
        
        self._dashboard_mekaros_subs = {}
        self._m3field_values = {}
        self._m3field_plots = {}
        
        NAMESPACE = '' 

        rospy.loginfo("Starting up...")
        
        # self._state_button = ControlStateButton("default", 9999)
        # TODO read this list on the parameters
        battery_names = ["m3pwr_pwr038", "m3pwr_pwr042"]
        group_names = {"left_hand":"mh26", "left_arm":"ma30", "head":"ms8", "right_arm":"ma29", "right_hand":"mh24", "zlift":"mz7", "torso":"mt6", "base":"mb7"}
        
        # create as many buttons as groups received
        for group_name in group_names.keys():
            self._state_buttons[group_name] = ControlStateButton(group_name, 0)

        battery_widget = self.init_batteries(battery_names)

        self._dashboard_agg_sub = rospy.Subscriber("/meka_roscontrol_state_manager/state", 
                                                       M3ControlStates,                               
                                                       self.state_callback,
                                                       queue_size=1)
        self._actionclient = actionlib.SimpleActionClient("/meka_state_manager", M3StateChangeAction)
        rospy.loginfo("Looking for state manager...")
        if self._actionclient.wait_for_server(timeout=rospy.Duration(0.1)) is False:
            rospy.logfatal("Failed to connect to state_manager action server in 4 sec")
            self._service_ready = False
        else:
            rospy.loginfo("Found the state manager")
            
        self._state_control = rospy.ServiceProxy('/meka_roscontrol_state_manager/change_state', M3ControlStateChange)

        self._main_widget = QWidget()
        vlayout = QVBoxLayout()
        hlayout = QHBoxLayout()
        hlayout2 = QHBoxLayout()

        self.chk_all = QCheckBox("enable_all")
        self.chk_all.setChecked(False)

        self.spin_retries = QSpinBox()
        # infinite number of times is -1
        self.spin_retries.setMinimum(-1)
        self.spin_retries.setMaximum(10)
        self.spin_retries.setValue(2)
        label_retries = QLabel("trial times")

        self.btn_start = QPushButton("start")
        self.btn_stop = QPushButton("stop")
        self.btn_freeze = QPushButton("freeze")
        if not self._service_ready:
            self.btn_start.setEnabled(False)
            self.btn_stop.setEnabled(False)
            self.btn_freeze.setEnabled(False)
            
        inspection_button = self.init_inspection()
        self.hz_rate = QSpinBox()
        # infinite number of times is -1
        self.hz_rate.setMinimum(1)
        self.hz_rate.setMaximum(100)
        self.hz_rate.setValue(1)
        label_hz = QLabel("hz")

        hlayout2.addWidget(inspection_button)
        hlayout2.addWidget(self.hz_rate)
        hlayout2.addWidget(label_hz)
        
        hlayout.addWidget(self.chk_all)
        hlayout.addWidget(label_retries)
        hlayout.addWidget(self.spin_retries)

        self.inspection_layout = QGridLayout()
        self.stiffness_layout = self.init_stiffness_ctrl()
        
        #vlayout.addWidget()
        vlayout.addWidget(battery_widget)
        vlayout.addLayout(hlayout)
        vlayout.addWidget(self.btn_start)
        vlayout.addWidget(self.btn_freeze)
        vlayout.addWidget(self.btn_stop)
        
        vlayout.addLayout(self.stiffness_layout)
        vlayout.addLayout(hlayout2)
        vlayout.addLayout(self.inspection_layout)

        self.btn_start.clicked.connect(self.on_btn_start_clicked)
        self.btn_stop.clicked.connect(self.on_btn_stop_clicked)
        self.btn_freeze.clicked.connect(self.on_btn_freeze_clicked)
        self.chk_all.stateChanged.connect(self.on_enable_all_clicked)

        self._main_widget.setLayout(vlayout)
        self.context.add_widget(self._main_widget)
        # self._main_widget.addLayout(hlayout)
        self._widget_initialized = True
        
    def init_batteries(self, battery_names):
        
        widget = QWidget()
        layout = QHBoxLayout(widget)        
        fields = ["motor_enabled", "bus_voltage"]        
        dt = Floats
                 
        for battery_name in battery_names:
            self._battery_icons[battery_name] = WrappedBattery(self.context, battery_name)
            layout.addWidget(self._battery_icons[battery_name])
            self._dashboard_mekaros_subs[(battery_name, fields[0])] = rospy.Subscriber("/meka_ros_pub/"+battery_name+"/"+fields[0], dt, self.battery_pwrd_cb, battery_name)                            
            self._dashboard_mekaros_subs[(battery_name, fields[1])] = rospy.Subscriber("/meka_ros_pub/"+battery_name+"/"+fields[1], dt, self.battery_voltage_cb, battery_name)
            
        layout.addStretch(1)
        widget.setFixedHeight(75)

        #self.bat_lbb = QLabel("Battery voltage")
        #self.bat_txt = QLabel()
        #self.bat_txt.setText("0.0")

        return widget
    
    
    def init_stiffness_ctrl(self):
        
        slayout = QVBoxLayout()
        
        prefix = "meka_roscontrol"
        suffix = "stiffness_controller/command"
        
        button = QPushButton("Stiffness controller (beta)")

        self.stiffness_pub = rospy.Publisher("/" + prefix + "/" + suffix, Float64MultiArray, queue_size=1)
        group_names = ["right_arm", "left_arm", "right_hand", "left_hand", "head", "torso", "zlift"]
        # slider for each group
        joint_names = [ "right_arm_j0",
                        "right_arm_j1",
                        "right_arm_j2",
                        "right_arm_j3",
                        "right_arm_j4",
                        "right_arm_j5",
                        "right_arm_j6",
                        "left_arm_j0",
                        "left_arm_j1",
                        "left_arm_j2",
                        "left_arm_j3",
                        "left_arm_j4",
                        "left_arm_j5",
                        "left_arm_j6",
                        "right_hand_j0",
                        "right_hand_j1",
                        "right_hand_j2",
                        "right_hand_j3",
                        "right_hand_j4",
                        "left_hand_j0",
                        "left_hand_j1",
                        "left_hand_j2",
                        "left_hand_j3",
                        "left_hand_j4",
                        "head_j0",
                        "head_j1",
                        "torso_j0",
                        "torso_j1",
                        "zlift_j0"]
        
        self._stiffness_dict = OrderedDict((name, 1.0) for name in joint_names)
        
        menu = QMenu("Menu")
        menu.setStyleSheet("QMenu { menu-scrollable: 1; }");
        self.stiffnessvals = {}
        for group in group_names:
            glayout = QHBoxLayout()
            glayout.addWidget(QLabel(group))
            slider = QSlider(Qt.Horizontal)
            slider.setRange(0,100)
            slider.setValue(100)
            slider.setTickPosition(QSlider.TicksBelow)
            slider.setTickInterval(10)
            slider.setFixedSize(200,15)
            slider.setSingleStep(10)
            slider.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed))
            
            val = QLabel("1.0")
            btn = QPushButton("apply")
            self.stiffnessvals[group] = val
            btn.clicked.connect(partial(self.on_stiffness_apply, group))
            
            glayout.addWidget(slider)
            glayout.addWidget(val)
            glayout.addWidget(btn)
            
            slider.valueChanged.connect(partial(self.on_stiffness_change, slider, val, group))
            
            slayout.addLayout(glayout)
            
            
            groupm = menu.addMenu(group)
            #for joint in joint_names:
                #groupm.addAction("set stiffness", partial(self.set_stiffness, groupm))
                #if group in joint:
                #    groupm.addAction(self._stiffness_dict[joint])
                #    s.addAction(joint, partial(self.request_fields, component, s))
        
        

        button.setMenu(menu)

        return slayout

    def init_inspection(self):

        button = QPushButton("Robot inspection (beta)")

        service_list_comps = '/meka_ros_publisher/list_components'
        service_list_fields = '/meka_ros_publisher/list_fields'
        service_req_values = '/meka_ros_publisher/request_values'
        rospy.loginfo("Waiting for %s, %s and %s", service_list_comps, service_list_fields, service_req_values)
        try:
            rospy.wait_for_service(service_list_comps, 4.0)
            rospy.wait_for_service(service_list_fields, 4.0)
            rospy.wait_for_service(service_req_values, 4.0)
        except rospy.ROSException:
            rospy.logerr("%s and/or %s did not show up. Giving up", service_list_comps, service_list_fields)
            return button
            
        self.list_comps_client = rospy.ServiceProxy(service_list_comps, ListComponents)
        self.list_fields_client = rospy.ServiceProxy(service_list_fields, ListFields)
        self.req_vals_client = rospy.ServiceProxy(service_req_values, RequestValues)
        rospy.loginfo("Found %s, %s and %s", service_list_comps, service_list_fields, service_req_values)

        # get all the components
        try:
            resp = self.list_comps_client("")
        except rospy.ServiceException:
            rospy.logerr("Could not call list_components")
            return button
            
        menu = QMenu("Menu")
        menu.setStyleSheet("QMenu { menu-scrollable: 1; }");
        submenus = []

        self.req_action = {}
        if(resp):
            for component in resp.components:
                s = menu.addMenu(component)
                self.req_action[component] = s.addAction("request fields", partial(self.request_fields, component, s))
                

        button.setMenu(menu)

        return button

    def init_inspection_test(self):

        button = QPushButton("Robot inspection (beta)")

        menu = QMenu("Menu")
        menu.setStyleSheet("QMenu { menu-scrollable: 1; }");
        submenus = []


        for i in range(5):
            # a component has fields which are either strings or arrays.
            s = menu.addMenu(str(i))
            for j in range(5):
                s.addAction(str(j), partial(self.subscribe_to_field_test, "blubber", j))

        button.setMenu(menu)

        return button

    def testbaloon(self, name):

        name_label = QLabel(name)
        lineedit = QLineEdit("random")
        name_label.setBuddy(lineedit);

        close_btn = QPushButton()

        pixmap = QPixmap(self._path + "/close.png")
        icon = QIcon(pixmap);
        close_btn.setIcon(icon)
        close_btn.setIconSize(pixmap.rect().size())

        idx = self.inspection_layout.rowCount()

        close_btn.clicked.connect(partial(self.remove_row, self.inspection_layout, idx, False))
        self.inspection_layout.addWidget(name_label, idx, 0);
        self.inspection_layout.addWidget(lineedit, idx, 1);
        self.inspection_layout.addWidget(close_btn, idx, 2);

    def subscribe_to_field_test(self, component, bla):

        field = str(bla)

        if (component, field) in self._m3field_values:
            rospy.logwarn("Already subscribed to field. Exiting.")
            return


        dt = Floats
        topic = str("/meka_ros_pub/" + component + "/" + field)
        self._dashboard_mekaros_subs[(component, field)] = rospy.Subscriber(topic, dt, self.field_callback, (component, field))
        self._m3field_values[(component, field)] = []

        name_label = QLabel(component + "->" + field + ":")
        for val in range(bla):
            label = QLabel(str(val)[:5])
            label.setStyleSheet("border: 2px solid grey");
            self._m3field_values[(component, field)].append(label)

        idx = self.inspection_layout.rowCount()

        plot_pixmap = QPixmap(self._path + "/plot.png")
        plot_icon = QIcon(plot_pixmap);
        plot_btn = QPushButton()
        plot_btn.setIcon(plot_icon)
        plot_btn.setIconSize(plot_pixmap.rect().size())
        plot_btn.setFixedWidth(30)
        plot_btn.clicked.connect(partial(self.plot_values, component, field))

        close_pixmap = QPixmap(self._path + "/close.png")
        close_icon = QIcon(close_pixmap);
        close_btn = QPushButton()
        close_btn.setIcon(close_icon)
        close_btn.setIconSize(close_pixmap.rect().size())
        close_btn.setFixedWidth(30)
        close_btn.clicked.connect(partial(self.remove_row, self.inspection_layout, idx, False, component, field))

        self.inspection_layout.addWidget(name_label, idx, 0)
        val_layout = QHBoxLayout()
        for label in self._m3field_values[(component, field)]:
            print label
            val_layout.addWidget(label);
        self.inspection_layout.addLayout(val_layout, idx, 1)
        self.inspection_layout.addWidget(plot_btn, idx, 2)
        self.inspection_layout.addWidget(close_btn, idx, 3)


    def request_fields(self, component, top_menu):
        
        # a component has fields which are either strings or arrays.
        try:
            resp2 = self.list_fields_client(component)
        except rospy.ServiceException:
            rospy.logerr("Could not call list_fields")
            return
        if(resp2):
            for field in resp2.fields:
                top_menu.addAction(field, partial(self.subscribe_to_field, component, field))
                
        self.req_action[component].setEnabled(False)


    def subscribe_to_field(self, component, field):
        
        if (component, field) in self._m3field_values:
            rospy.logwarn("Already subscribed to field. Exiting.")
            return
                
        try:
            resp = self.req_vals_client(component, field, "", self.hz_rate.value())
        except rospy.ServiceException:
            rospy.logerr("Could not call request_values")
            return
        
        print resp.values
        
        dt = Floats
        topic = str("/meka_ros_pub/"+component+"/"+field)
        self._dashboard_mekaros_subs[(component, field)] = rospy.Subscriber(topic, dt, self.field_callback, (component, field))
        self._m3field_values[(component, field)] = []
        
        name_label = QLabel(component+"->"+field+":")
        for val in resp.values:
            label = QLabel(str(val)[:5])
            label.setStyleSheet("border: 2px solid grey");
            self._m3field_values[(component, field)].append(label)
        
        idx = self.inspection_layout.rowCount()
        
        plot_pixmap = QPixmap(self._path + "/plot.png")
        plot_icon = QIcon(plot_pixmap);
        #plot_btn = QPushButton()
        #plot_btn.setIcon(plot_icon)
        #plot_btn.setIconSize(plot_pixmap.rect().size())
        #plot_btn.setFixedWidth(30)
        #plot_btn.clicked.connect(partial(self.plot_values, component, field))
        
        close_pixmap = QPixmap(self._path + "/close.png")
        close_icon = QIcon(close_pixmap);
        close_btn = QPushButton()
        close_btn.setIcon(close_icon)
        close_btn.setIconSize(close_pixmap.rect().size())
        close_btn.setFixedWidth(30)
        close_btn.clicked.connect(partial(self.remove_row, self.inspection_layout, idx, False, component, field))
        
        self.inspection_layout.addWidget(name_label, idx, 0)
        val_layout = QHBoxLayout()
        for label in self._m3field_values[(component, field)]:
            val_layout.addWidget(label);
        self.inspection_layout.addLayout(val_layout, idx, 1)
        #self.inspection_layout.addWidget(plot_btn, idx, 2)
        self.inspection_layout.addWidget(close_btn, idx, 3)
        
    def plot_values(self, component, field):
        """
        plot_values create a new plot for the values of the given component and field
        :params component, field:
        :type component: Identifier of the component, field: Identifier of the field
        """
        dialog = PlotDialog()
        QObject.connect(dialog, SIGNAL('rejected()'), partial(self.on_plot_close,  component, field))
        dialog.init_plot(component, field)
        dialog.show()
        
        self._m3field_plots[(component, field)] = dialog
        
        #dialog.exec_()

    
    def change_state(self, cmd):
        """
        change_state calls the m3rosctrl robot interface to change the 
        state of one or multiple joint groups (e.g. "head" to "freeze")
        :params cmd:
        :type cmd: Identifier of the new state
        """
        goal = M3StateChangeGoal()
        goal.retries = 0
        if self.spin_retries.value() == -1:
            goal.strategy = M3StateChangeGoal.KEEP_TRYING
        if self.spin_retries.value() == 0:
            goal.strategy = M3StateChangeGoal.HALT_ON_FAILURE
        if self.spin_retries.value() == 1:
            goal.strategy = M3StateChangeGoal.BEST_POSSIBLE

        if self.spin_retries.value() > 1:
            goal.strategy = M3StateChangeGoal.RETRY_N_TIMES
            goal.retries = self.spin_retries.value()

        # find enabled groups
        for group_name in self._state_buttons:
            if self._state_buttons[group_name]._enable_menu.isChecked():
                goal.command.group_name.append(group_name)
                goal.command.state.append(cmd)
        try:
            if len(goal.command.group_name) > 0:
                self._actionclient.send_goal(goal)
        except rospy.ROSException:
            rospy.logerr("Failed to call change state")

    def on_stiffness_apply(self, group):
        print "change stiffness"
        msg = Float64MultiArray()
        for name in self._stiffness_dict:
            msg.data.append(self._stiffness_dict[name])
        rospy.logdebug(msg)
        self.stiffness_pub.publish(msg)
        

    def on_stiffness_change(self, slider, val, group):
        
        newval = float(slider.value())/100.0
        
        for key, value in self._stiffness_dict.iteritems():
            if group in key:
                self._stiffness_dict[key] = newval
        
        val.setText(str(newval))
        

    def on_enable_all_clicked(self):
        """
        enable all
        """
        state_cmd = M3ControlStateChangeRequest()
                    
        for group_name in self._state_buttons:
            state_cmd.command.group_name.append(group_name)
            if self.chk_all.isChecked():
                self._state_buttons[group_name]._enable_menu.setChecked(True)
                self._state_buttons[group_name]._disable_menu.setChecked(False)
                self._state_buttons[group_name]._enable_menu.setEnabled(False)
                self._state_buttons[group_name]._disable_menu.setEnabled(True)
                state_cmd.command.state.append(STATE_CMD_ENABLE)
            else:
                self._state_buttons[group_name]._enable_menu.setChecked(False)
                self._state_buttons[group_name]._disable_menu.setChecked(True)
                self._state_buttons[group_name]._enable_menu.setEnabled(True)
                self._state_buttons[group_name]._disable_menu.setEnabled(False)
                state_cmd.command.state.append(STATE_CMD_DISABLE)
        try:
            self._state_control(state_cmd)
        except rospy.ServiceException, e:
            QMessageBox.critical(self, "Error", "Service call failed with error: %s" % (e), "Error")
Example #7
0
class _ExecuteTab(QTabWidget):
    """Tab used to execute modules or shell commands on the selected bot."""
    def __init__(self, responses_tab: _ResponsesTab, model):
        super().__init__()

        self._model = model
        self._current_layout = None
        self._current_bot = None

        self._layout = QGridLayout()
        self._sub_layout = QVBoxLayout()
        self._module_view = ModuleView(responses_tab)

        self._layout.setAlignment(Qt.AlignTop)
        self.setLayout(self._layout)
        self.set_empty_layout()

    def set_current_bot(self, bot: Bot):
        """Sets the connected bot this tab will interact with."""
        self._current_bot = bot

    def _clear_layout(self):
        while self._layout.count():
            child = self._layout.takeAt(0)

            if child.widget():
                child.widget().deleteLater()
        while self._sub_layout.count():
            child = self._sub_layout.takeAt(0)

            if child.widget():
                child.widget().deleteLater()

    def set_empty_layout(self):
        """Default layout shown when the user has not yet selected a row."""
        self._current_layout = "Empty"
        self._clear_layout()

        self._layout.addWidget(
            QLabel("Please select a bot in the table above."), 0, 0)

    def set_module_layout(self, module_name: str = "screenshot"):
        """Sets the layout which can execute modules."""
        self._current_layout = "Module"
        self._clear_layout()

        command_type_label = QLabel("Command type: ")
        command_type_combobox = QComboBox()

        command_type_combobox.addItem("Module")
        command_type_combobox.addItem("Shell")

        module_label = QLabel("Module name: ")
        module_combobox = QComboBox()

        for module_name in modules.get_names():
            module_combobox.addItem(module_name)

        module_combobox.currentTextChanged.connect(self._on_module_change)
        command_type_combobox.currentTextChanged.connect(
            self._on_command_type_change)

        self._layout.setColumnStretch(1, 1)
        self._layout.addWidget(command_type_label, 0, 0)
        self._layout.addWidget(command_type_combobox, 0, 1)
        self._layout.addWidget(module_label, 1, 0)
        self._layout.addWidget(module_combobox, 1, 1)

        # Module layout
        cached_module = modules.get_module(module_name)

        if not cached_module:
            cached_module = modules.load_module(module_name, self._module_view,
                                                self._model)

        input_fields = []

        for option_name in cached_module.get_setup_messages():
            input_field = QLineEdit()

            self._sub_layout.addWidget(QLabel(option_name))
            self._sub_layout.addWidget(input_field)
            input_fields.append(input_field)

        run_button = QPushButton("Run")
        run_button.setMaximumWidth(250)
        run_button.setMinimumHeight(25)

        run_button.pressed.connect(lambda: self._on_module_run(
            module_combobox.currentText(), input_fields))

        self._sub_layout.addWidget(QLabel(""))
        self._sub_layout.addWidget(run_button)
        self._sub_layout.setContentsMargins(0, 15, 0, 0)
        self._layout.addLayout(self._sub_layout,
                               self._layout.rowCount() + 2, 0, 1, 2)

        self._on_module_change(module_combobox.currentText())

    def set_shell_layout(self):
        """Sets the layout which can execute shell commands."""
        self._current_layout = "Shell"
        self._clear_layout()

        command_type_label = QLabel("Command type: ")
        command_type_combobox = QComboBox()

        command_type_combobox.addItem("Shell")
        command_type_combobox.addItem("Module")

        command_label = QLabel("Command:")
        command_input = QLineEdit()

        run_button = QPushButton("Run")
        run_button.setMaximumWidth(250)
        run_button.setMinimumHeight(25)

        command_type_combobox.currentTextChanged.connect(
            self._on_command_type_change)
        run_button.pressed.connect(lambda: self._on_command_run(command_input))

        self._layout.addWidget(command_type_label, 0, 0)
        self._layout.addWidget(command_type_combobox, 0, 1)
        self._layout.addWidget(command_label, 1, 0)
        self._layout.addWidget(command_input, 1, 1)

        self._sub_layout.addWidget(QLabel(""))
        self._sub_layout.addWidget(run_button)
        self._sub_layout.setContentsMargins(0, 15, 0, 0)
        self._layout.addLayout(self._sub_layout,
                               self._layout.rowCount() + 2, 0, 1, 2)

    def _on_command_type_change(self, text: str):
        """Handles the command type combobox change event."""
        if text == "Module":
            self.set_module_layout()
        else:
            self.set_shell_layout()

    def _on_module_change(self, module_name: str):
        """Handles module combobox changes."""
        while self._sub_layout.count():
            child = self._sub_layout.takeAt(0)

            if child.widget():
                child.widget().deleteLater()

        cached_module = modules.get_module(module_name)

        if not cached_module:
            cached_module = modules.load_module(module_name, self._module_view,
                                                self._model)

        input_fields = []

        for option_name in cached_module.get_setup_messages():
            input_field = QLineEdit()
            input_fields.append(input_field)

            self._sub_layout.addWidget(QLabel(option_name))
            self._sub_layout.addWidget(input_field)

        run_button = QPushButton("Run")
        run_button.setMaximumWidth(250)
        run_button.setMinimumHeight(25)

        run_button.pressed.connect(
            lambda: self._on_module_run(module_name, input_fields))

        self._sub_layout.addWidget(QLabel(""))
        self._sub_layout.addWidget(run_button)
        self._sub_layout.setContentsMargins(0, 15, 0, 0)

    def display_info(self, text: str):
        message_box = QMessageBox()

        message_box.setIcon(QMessageBox.Information)
        message_box.setWindowTitle("Information")
        message_box.setText(text)
        message_box.setStandardButtons(QMessageBox.Ok)
        message_box.exec_()

    def _on_module_run(self, module_name: str, input_fields: list):
        """Handles running modules."""
        set_options = []

        for input_field in input_fields:
            set_options.append(input_field.text())

        module = modules.get_module(module_name)

        if not module:
            module = modules.load_module(module_name, self._module_view,
                                         self._model)

        successful, options = module.setup(set_options)

        if successful:
            if module_name == "remove_bot":
                code = loaders.get_remove_code(self._current_bot.loader_name)
            elif module_name == "update_bot":
                code = loaders.get_update_code(self._current_bot.loader_name)
            else:
                code = modules.get_code(module_name)

            if not options:
                options = {}

            options["module_name"] = module_name

            self._model.add_command(self._current_bot.uid,
                                    Command(CommandType.MODULE, code, options))

            self.display_info("Module added to the queue of:\n {}@{}".format(
                self._current_bot.username, self._current_bot.hostname))

    def _on_command_run(self, command_input: QLineEdit):
        """Handles running commands."""
        if command_input.text().strip() == "":
            return

        self._model.add_command(
            self._current_bot.uid,
            Command(CommandType.SHELL,
                    command_input.text().encode()))

        command_input.clear()
        self.display_info("Command added to the queue of:\n {}@{}".format(
            self._current_bot.username, self._current_bot.hostname))
class ConfigWidget(QWidget, Logger):
    '''
    Config dialog for Marvin Manager
    '''

    WIZARD_PROFILES = {
        'Annotations': {
            'label': 'mm_annotations',
            'datatype': 'comments',
            'display': {},
            'is_multiple': False
        },
        'Collections': {
            'label': 'mm_collections',
            'datatype': 'text',
            'display': {u'is_names': False},
            'is_multiple': True
        },
        'Last read': {
            'label': 'mm_date_read',
            'datatype': 'datetime',
            'display': {},
            'is_multiple': False
        },
        'Locked': {
            'label': 'mm_locked',
            'datatype': 'bool',
            'display': {},
            'is_multiple': False
        },
        'Progress': {
            'label': 'mm_progress',
            'datatype': 'float',
            'display': {u'number_format': u'{0:.0f}%'},
            'is_multiple': False
        },
        'Read': {
            'label': 'mm_read',
            'datatype': 'bool',
            'display': {},
            'is_multiple': False
        },
        'Reading list': {
            'label': 'mm_reading_list',
            'datatype': 'bool',
            'display': {},
            'is_multiple': False
        },
        'Word count': {
            'label': 'mm_word_count',
            'datatype': 'int',
            'display': {u'number_format': u'{0:n}'},
            'is_multiple': False
        }
    }

    def __init__(self, plugin_action):
        QWidget.__init__(self)
        self.parent = plugin_action

        self.gui = get_gui()
        self.icon = plugin_action.icon
        self.opts = plugin_action.opts
        self.prefs = plugin_prefs
        self.resources_path = plugin_action.resources_path
        self.verbose = plugin_action.verbose

        self.restart_required = False

        self._log_location()

        self.l = QGridLayout()
        self.setLayout(self.l)
        self.column1_layout = QVBoxLayout()
        self.l.addLayout(self.column1_layout, 0, 0)
        self.column2_layout = QVBoxLayout()
        self.l.addLayout(self.column2_layout, 0, 1)

        # ----------------------------- Column 1 -----------------------------
        # ~~~~~~~~ Create the Custom fields options group box ~~~~~~~~
        self.cfg_custom_fields_gb = QGroupBox(self)
        self.cfg_custom_fields_gb.setTitle('Custom column assignments')
        self.column1_layout.addWidget(self.cfg_custom_fields_gb)

        self.cfg_custom_fields_qgl = QGridLayout(self.cfg_custom_fields_gb)
        current_row = 0

        # ++++++++ Labels + HLine ++++++++
        self.marvin_source_label = QLabel("Marvin source")
        self.cfg_custom_fields_qgl.addWidget(self.marvin_source_label, current_row, 0)
        self.calibre_destination_label = QLabel("calibre destination")
        self.cfg_custom_fields_qgl.addWidget(self.calibre_destination_label, current_row, 1)
        current_row += 1
        self.sd_hl = QFrame(self.cfg_custom_fields_gb)
        self.sd_hl.setFrameShape(QFrame.HLine)
        self.sd_hl.setFrameShadow(QFrame.Raised)
        self.cfg_custom_fields_qgl.addWidget(self.sd_hl, current_row, 0, 1, 3)
        current_row += 1

        # ++++++++ Annotations ++++++++
        self.cfg_annotations_label = QLabel('Annotations')
        self.cfg_annotations_label.setAlignment(Qt.AlignLeft)
        self.cfg_custom_fields_qgl.addWidget(self.cfg_annotations_label, current_row, 0)

        self.annotations_field_comboBox = QComboBox(self.cfg_custom_fields_gb)
        self.annotations_field_comboBox.setObjectName('annotations_field_comboBox')
        self.annotations_field_comboBox.setToolTip('Select a custom column to store Marvin annotations')
        self.cfg_custom_fields_qgl.addWidget(self.annotations_field_comboBox, current_row, 1)

        self.cfg_highlights_wizard = QToolButton()
        self.cfg_highlights_wizard.setIcon(QIcon(I('wizard.png')))
        self.cfg_highlights_wizard.setToolTip("Create a custom column to store Marvin annotations")
        self.cfg_highlights_wizard.clicked.connect(partial(self.launch_cc_wizard, 'Annotations'))
        self.cfg_custom_fields_qgl.addWidget(self.cfg_highlights_wizard, current_row, 2)
        current_row += 1

        # ++++++++ Collections ++++++++
        self.cfg_collections_label = QLabel('Collections')
        self.cfg_collections_label.setAlignment(Qt.AlignLeft)
        self.cfg_custom_fields_qgl.addWidget(self.cfg_collections_label, current_row, 0)

        self.collection_field_comboBox = QComboBox(self.cfg_custom_fields_gb)
        self.collection_field_comboBox.setObjectName('collection_field_comboBox')
        self.collection_field_comboBox.setToolTip('Select a custom column to store Marvin collection assignments')
        self.cfg_custom_fields_qgl.addWidget(self.collection_field_comboBox, current_row, 1)

        self.cfg_collections_wizard = QToolButton()
        self.cfg_collections_wizard.setIcon(QIcon(I('wizard.png')))
        self.cfg_collections_wizard.setToolTip("Create a custom column for Marvin collection assignments")
        self.cfg_collections_wizard.clicked.connect(partial(self.launch_cc_wizard, 'Collections'))
        self.cfg_custom_fields_qgl.addWidget(self.cfg_collections_wizard, current_row, 2)
        current_row += 1

        # ++++++++ Last read ++++++++
        self.cfg_date_read_label = QLabel("Last read")
        self.cfg_date_read_label.setAlignment(Qt.AlignLeft)
        self.cfg_custom_fields_qgl.addWidget(self.cfg_date_read_label, current_row, 0)

        self.date_read_field_comboBox = QComboBox(self.cfg_custom_fields_gb)
        self.date_read_field_comboBox.setObjectName('date_read_field_comboBox')
        self.date_read_field_comboBox.setToolTip('Select a custom column to store Last read date')
        self.cfg_custom_fields_qgl.addWidget(self.date_read_field_comboBox, current_row, 1)

        self.cfg_collections_wizard = QToolButton()
        self.cfg_collections_wizard.setIcon(QIcon(I('wizard.png')))
        self.cfg_collections_wizard.setToolTip("Create a custom column to store Last read date")
        self.cfg_collections_wizard.clicked.connect(partial(self.launch_cc_wizard, 'Last read'))
        self.cfg_custom_fields_qgl.addWidget(self.cfg_collections_wizard, current_row, 2)
        current_row += 1

        # ++++++++ Locked ++++++++
        self.cfg_locked_label = QLabel("Locked")
        self.cfg_locked_label.setAlignment(Qt.AlignLeft)
        self.cfg_custom_fields_qgl.addWidget(self.cfg_locked_label, current_row, 0)

        self.locked_field_comboBox = QComboBox(self.cfg_custom_fields_gb)
        self.locked_field_comboBox.setObjectName('locked_field_comboBox')
        self.locked_field_comboBox.setToolTip('Select a custom column to store Locked status')
        self.cfg_custom_fields_qgl.addWidget(self.locked_field_comboBox, current_row, 1)

        self.cfg_locked_wizard = QToolButton()
        self.cfg_locked_wizard.setIcon(QIcon(I('wizard.png')))
        self.cfg_locked_wizard.setToolTip("Create a custom column to store Locked status")
        self.cfg_locked_wizard.clicked.connect(partial(self.launch_cc_wizard, 'Locked'))
        self.cfg_custom_fields_qgl.addWidget(self.cfg_locked_wizard, current_row, 2)
        current_row += 1

        # ++++++++ Progress ++++++++
        self.cfg_progress_label = QLabel('Progress')
        self.cfg_progress_label.setAlignment(Qt.AlignLeft)
        self.cfg_custom_fields_qgl.addWidget(self.cfg_progress_label, current_row, 0)

        self.progress_field_comboBox = QComboBox(self.cfg_custom_fields_gb)
        self.progress_field_comboBox.setObjectName('progress_field_comboBox')
        self.progress_field_comboBox.setToolTip('Select a custom column to store Marvin reading progress')
        self.cfg_custom_fields_qgl.addWidget(self.progress_field_comboBox, current_row, 1)

        self.cfg_progress_wizard = QToolButton()
        self.cfg_progress_wizard.setIcon(QIcon(I('wizard.png')))
        self.cfg_progress_wizard.setToolTip("Create a custom column to store Marvin reading progress")
        self.cfg_progress_wizard.clicked.connect(partial(self.launch_cc_wizard, 'Progress'))
        self.cfg_custom_fields_qgl.addWidget(self.cfg_progress_wizard, current_row, 2)
        current_row += 1

        # ++++++++ Read flag ++++++++
        self.cfg_read_label = QLabel('Read')
        self.cfg_read_label.setAlignment(Qt.AlignLeft)
        self.cfg_custom_fields_qgl.addWidget(self.cfg_read_label, current_row, 0)

        self.read_field_comboBox = QComboBox(self.cfg_custom_fields_gb)
        self.read_field_comboBox.setObjectName('read_field_comboBox')
        self.read_field_comboBox.setToolTip('Select a custom column to store Marvin Read status')
        self.cfg_custom_fields_qgl.addWidget(self.read_field_comboBox, current_row, 1)

        self.cfg_read_wizard = QToolButton()
        self.cfg_read_wizard.setIcon(QIcon(I('wizard.png')))
        self.cfg_read_wizard.setToolTip("Create a custom column to store Marvin Read status")
        self.cfg_read_wizard.clicked.connect(partial(self.launch_cc_wizard, 'Read'))
        self.cfg_custom_fields_qgl.addWidget(self.cfg_read_wizard, current_row, 2)
        current_row += 1

        # ++++++++ Reading list flag ++++++++
        self.cfg_reading_list_label = QLabel('Reading list')
        self.cfg_reading_list_label.setAlignment(Qt.AlignLeft)
        self.cfg_custom_fields_qgl.addWidget(self.cfg_reading_list_label, current_row, 0)

        self.reading_list_field_comboBox = QComboBox(self.cfg_custom_fields_gb)
        self.reading_list_field_comboBox.setObjectName('reading_list_field_comboBox')
        self.reading_list_field_comboBox.setToolTip('Select a custom column to store Marvin Reading list status')
        self.cfg_custom_fields_qgl.addWidget(self.reading_list_field_comboBox, current_row, 1)

        self.cfg_reading_list_wizard = QToolButton()
        self.cfg_reading_list_wizard.setIcon(QIcon(I('wizard.png')))
        self.cfg_reading_list_wizard.setToolTip("Create a custom column to store Marvin Reading list status")
        self.cfg_reading_list_wizard.clicked.connect(partial(self.launch_cc_wizard, 'Reading list'))
        self.cfg_custom_fields_qgl.addWidget(self.cfg_reading_list_wizard, current_row, 2)
        current_row += 1

        # ++++++++ Word count ++++++++
        self.cfg_word_count_label = QLabel('Word count')
        self.cfg_word_count_label.setAlignment(Qt.AlignLeft)
        self.cfg_custom_fields_qgl.addWidget(self.cfg_word_count_label, current_row, 0)

        self.word_count_field_comboBox = QComboBox(self.cfg_custom_fields_gb)
        self.word_count_field_comboBox.setObjectName('word_count_field_comboBox')
        self.word_count_field_comboBox.setToolTip('Select a custom column to store Marvin word counts')
        self.cfg_custom_fields_qgl.addWidget(self.word_count_field_comboBox, current_row, 1)

        self.cfg_word_count_wizard = QToolButton()
        self.cfg_word_count_wizard.setIcon(QIcon(I('wizard.png')))
        self.cfg_word_count_wizard.setToolTip("Create a custom column to store Marvin word counts")
        self.cfg_word_count_wizard.clicked.connect(partial(self.launch_cc_wizard, 'Word count'))
        self.cfg_custom_fields_qgl.addWidget(self.cfg_word_count_wizard, current_row, 2)
        current_row += 1

        self.spacerItem1 = QSpacerItem(20, 20, QSizePolicy.Minimum, QSizePolicy.Expanding)
        self.column1_layout.addItem(self.spacerItem1)

        # ----------------------------- Column 2 -----------------------------
        # ~~~~~~~~ Create the CSS group box ~~~~~~~~
        self.cfg_css_options_gb = QGroupBox(self)
        self.cfg_css_options_gb.setTitle('CSS')
        self.column2_layout.addWidget(self.cfg_css_options_gb)
        self.cfg_css_options_qgl = QGridLayout(self.cfg_css_options_gb)

        current_row = 0

        # ++++++++ Annotations appearance ++++++++
        self.annotations_icon = QIcon(os.path.join(self.resources_path, 'icons', 'annotations_hiliter.png'))
        self.cfg_annotations_appearance_toolbutton = QToolButton()
        self.cfg_annotations_appearance_toolbutton.setIcon(self.annotations_icon)
        self.cfg_annotations_appearance_toolbutton.clicked.connect(self.configure_appearance)
        self.cfg_css_options_qgl.addWidget(self.cfg_annotations_appearance_toolbutton, current_row, 0)
        self.cfg_annotations_label = ClickableQLabel("Book notes, Bookmark notes and Annotations")
        self.cfg_annotations_label.clicked.connect(self.configure_appearance)
        self.cfg_css_options_qgl.addWidget(self.cfg_annotations_label, current_row, 1)
        current_row += 1

        # ++++++++ Injected CSS ++++++++
        self.css_editor_icon = QIcon(I('format-text-heading.png'))
        self.cfg_css_editor_toolbutton = QToolButton()
        self.cfg_css_editor_toolbutton.setIcon(self.css_editor_icon)
        self.cfg_css_editor_toolbutton.clicked.connect(self.edit_css)
        self.cfg_css_options_qgl.addWidget(self.cfg_css_editor_toolbutton, current_row, 0)
        self.cfg_css_editor_label = ClickableQLabel("Articles, Vocabulary")
        self.cfg_css_editor_label.clicked.connect(self.edit_css)
        self.cfg_css_options_qgl.addWidget(self.cfg_css_editor_label, current_row, 1)


        """
        # ~~~~~~~~ Create the Dropbox syncing group box ~~~~~~~~
        self.cfg_dropbox_syncing_gb = QGroupBox(self)
        self.cfg_dropbox_syncing_gb.setTitle('Dropbox')
        self.column2_layout.addWidget(self.cfg_dropbox_syncing_gb)
        self.cfg_dropbox_syncing_qgl = QGridLayout(self.cfg_dropbox_syncing_gb)
        current_row = 0

        # ++++++++ Syncing enabled checkbox ++++++++
        self.dropbox_syncing_checkbox = QCheckBox('Enable Dropbox updates')
        self.dropbox_syncing_checkbox.setObjectName('dropbox_syncing')
        self.dropbox_syncing_checkbox.setToolTip('Refresh custom column content from Marvin metadata')
        self.cfg_dropbox_syncing_qgl.addWidget(self.dropbox_syncing_checkbox,
            current_row, 0, 1, 3)
        current_row += 1

        # ++++++++ Dropbox folder picker ++++++++
        self.dropbox_folder_icon = QIcon(os.path.join(self.resources_path, 'icons', 'dropbox.png'))
        self.cfg_dropbox_folder_toolbutton = QToolButton()
        self.cfg_dropbox_folder_toolbutton.setIcon(self.dropbox_folder_icon)
        self.cfg_dropbox_folder_toolbutton.setToolTip("Specify Dropbox folder location on your computer")
        self.cfg_dropbox_folder_toolbutton.clicked.connect(self.select_dropbox_folder)
        self.cfg_dropbox_syncing_qgl.addWidget(self.cfg_dropbox_folder_toolbutton,
            current_row, 1)

        # ++++++++ Dropbox location lineedit ++++++++
        self.dropbox_location_lineedit = QLineEdit()
        self.dropbox_location_lineedit.setPlaceholderText("Dropbox folder location")
        self.cfg_dropbox_syncing_qgl.addWidget(self.dropbox_location_lineedit,
            current_row, 2)
        """

        # ~~~~~~~~ Create the General options group box ~~~~~~~~
        self.cfg_runtime_options_gb = QGroupBox(self)
        self.cfg_runtime_options_gb.setTitle('General options')
        self.column2_layout.addWidget(self.cfg_runtime_options_gb)
        self.cfg_runtime_options_qvl = QVBoxLayout(self.cfg_runtime_options_gb)

        # ++++++++ Temporary markers: Duplicates ++++++++
        self.duplicate_markers_checkbox = QCheckBox('Apply temporary markers to duplicate books')
        self.duplicate_markers_checkbox.setObjectName('apply_markers_to_duplicates')
        self.duplicate_markers_checkbox.setToolTip('Books with identical content will be flagged in the Library window')
        self.cfg_runtime_options_qvl.addWidget(self.duplicate_markers_checkbox)

        # ++++++++ Temporary markers: Updated ++++++++
        self.updated_markers_checkbox = QCheckBox('Apply temporary markers to books with updated content')
        self.updated_markers_checkbox.setObjectName('apply_markers_to_updated')
        self.updated_markers_checkbox.setToolTip('Books with updated content will be flagged in the Library window')
        self.cfg_runtime_options_qvl.addWidget(self.updated_markers_checkbox)

        # ++++++++ Auto refresh checkbox ++++++++
        self.auto_refresh_checkbox = QCheckBox('Automatically refresh custom column content')
        self.auto_refresh_checkbox.setObjectName('auto_refresh_at_startup')
        self.auto_refresh_checkbox.setToolTip('Update calibre custom column when Marvin XD is opened')
        self.cfg_runtime_options_qvl.addWidget(self.auto_refresh_checkbox)

        # ++++++++ Progress as percentage checkbox ++++++++
        self.reading_progress_checkbox = QCheckBox('Show reading progress as percentage')
        self.reading_progress_checkbox.setObjectName('show_progress_as_percentage')
        self.reading_progress_checkbox.setToolTip('Display percentage in Progress column')
        self.cfg_runtime_options_qvl.addWidget(self.reading_progress_checkbox)

        # ~~~~~~~~ Create the Debug options group box ~~~~~~~~
        self.cfg_debug_options_gb = QGroupBox(self)
        self.cfg_debug_options_gb.setTitle('Debug options')
        self.column2_layout.addWidget(self.cfg_debug_options_gb)
        self.cfg_debug_options_qvl = QVBoxLayout(self.cfg_debug_options_gb)

        # ++++++++ Debug logging checkboxes ++++++++
        self.debug_plugin_checkbox = QCheckBox('Enable debug logging for Marvin XD')
        self.debug_plugin_checkbox.setObjectName('debug_plugin_checkbox')
        self.debug_plugin_checkbox.setToolTip('Print plugin diagnostic messages to console')
        self.cfg_debug_options_qvl.addWidget(self.debug_plugin_checkbox)

        self.debug_libimobiledevice_checkbox = QCheckBox('Enable debug logging for libiMobileDevice')
        self.debug_libimobiledevice_checkbox.setObjectName('debug_libimobiledevice_checkbox')
        self.debug_libimobiledevice_checkbox.setToolTip('Print libiMobileDevice diagnostic messages to console')
        self.cfg_debug_options_qvl.addWidget(self.debug_libimobiledevice_checkbox)

        self.spacerItem2 = QSpacerItem(20, 20, QSizePolicy.Minimum, QSizePolicy.Expanding)
        self.column2_layout.addItem(self.spacerItem2)

        # ~~~~~~~~ End of construction zone ~~~~~~~~
        self.resize(self.sizeHint())

        # ~~~~~~~~ Populate/restore config options ~~~~~~~~
        #  Annotations comboBox
        self.populate_annotations()
        self.populate_collections()
        self.populate_date_read()
        self.populate_locked()
        self.populate_progress()
        self.populate_read()
        self.populate_reading_list()
        self.populate_word_count()

        """
        # Restore Dropbox settings, hook changes
        dropbox_syncing = self.prefs.get('dropbox_syncing', False)
        self.dropbox_syncing_checkbox.setChecked(dropbox_syncing)
        self.set_dropbox_syncing(dropbox_syncing)
        self.dropbox_syncing_checkbox.clicked.connect(partial(self.set_dropbox_syncing))
        self.dropbox_location_lineedit.setText(self.prefs.get('dropbox_folder', ''))
        """

        # Restore general settings
        self.duplicate_markers_checkbox.setChecked(self.prefs.get('apply_markers_to_duplicates', True))
        self.updated_markers_checkbox.setChecked(self.prefs.get('apply_markers_to_updated', True))
        self.auto_refresh_checkbox.setChecked(self.prefs.get('auto_refresh_at_startup', False))
        self.reading_progress_checkbox.setChecked(self.prefs.get('show_progress_as_percentage', False))

        # Restore debug settings, hook changes
        self.debug_plugin_checkbox.setChecked(self.prefs.get('debug_plugin', False))
        self.debug_plugin_checkbox.stateChanged.connect(self.set_restart_required)
        self.debug_libimobiledevice_checkbox.setChecked(self.prefs.get('debug_libimobiledevice', False))
        self.debug_libimobiledevice_checkbox.stateChanged.connect(self.set_restart_required)

        # Hook changes to Annotations comboBox
#         self.annotations_field_comboBox.currentIndexChanged.connect(
#             partial(self.save_combobox_setting, 'annotations_field_comboBox'))
#        self.connect(self.annotations_field_comboBox,
#                     SIGNAL('currentIndexChanged(const QString &)'),
#                     self.annotations_destination_changed)
        self.annotations_field_comboBox.currentIndexChanged.connect(self.annotations_destination_changed)
        # Launch the annotated_books_scanner
        field = get_cc_mapping('annotations', 'field', None)
        self.annotated_books_scanner = InventoryAnnotatedBooks(self.gui, field)
        self.annotated_books_scanner.signal.connect(self.inventory_complete)
        QTimer.singleShot(1, self.start_inventory)

    def annotations_destination_changed(self, qs_new_destination_name):
        '''
        If the destination field changes, move all existing annotations from old to new
        '''
        self._log_location(str(qs_new_destination_name))
        self._log("self.eligible_annotations_fields: %s" % self.eligible_annotations_fields)

        old_destination_field = get_cc_mapping('annotations', 'field', None)
        old_destination_name = get_cc_mapping('annotations', 'combobox', None)

        self._log("old_destination_field: %s" % old_destination_field)
        self._log("old_destination_name: %s" % old_destination_name)

#        new_destination_name = unicode(qs_new_destination_name)
        # Signnls available have changed. Now receivin an indec rather than a name. Can get the name
        # from the combobox
        new_destination_name = unicode(self.annotations_field_comboBox.currentText())
        self._log("new_destination_name: %s" % repr(new_destination_name))

        if old_destination_name == new_destination_name:
            self._log_location("old_destination_name = new_destination_name, no changes")
            return

        if new_destination_name == '':
            self._log_location("annotations storage disabled")
            set_cc_mapping('annotations', field=None, combobox=new_destination_name)
            return

        new_destination_field = self.eligible_annotations_fields[new_destination_name]

        if existing_annotations(self.parent, old_destination_field):
            command = self.launch_new_destination_dialog(old_destination_name, new_destination_name)

            if command == 'move':
                set_cc_mapping('annotations', field=new_destination_field, combobox=new_destination_name)

                if self.annotated_books_scanner.isRunning():
                    self.annotated_books_scanner.wait()
                move_annotations(self, self.annotated_books_scanner.annotation_map,
                    old_destination_field, new_destination_field)

            elif command == 'change':
                # Keep the updated destination field, but don't move annotations
                pass

            elif command == 'cancel':
                # Restore previous destination
                self.annotations_field_comboBox.blockSignals(True)
                old_index = self.annotations_field_comboBox.findText(old_destination_name)
                self.annotations_field_comboBox.setCurrentIndex(old_index)
                self.annotations_field_comboBox.blockSignals(False)

        else:
            # No existing annotations, just update prefs
            self._log("no existing annotations, updating destination to '{0}'".format(new_destination_name))
            set_cc_mapping('annotations', field=new_destination_field, combobox=new_destination_name)

    def configure_appearance(self):
        '''
        '''
        self._log_location()
        appearance_settings = {
                                'appearance_css': default_elements,
                                'appearance_hr_checkbox': False,
                                'appearance_timestamp_format': default_timestamp
                              }

        # Save, hash the original settings
        original_settings = {}
        osh = hashlib.md5()
        for setting in appearance_settings:
            original_settings[setting] = plugin_prefs.get(setting, appearance_settings[setting])
            osh.update(repr(plugin_prefs.get(setting, appearance_settings[setting])))

        # Display the Annotations appearance dialog
        aa = AnnotationsAppearance(self, self.annotations_icon, plugin_prefs)
        cancelled = False
        if aa.exec_():
            # appearance_hr_checkbox and appearance_timestamp_format changed live to prefs during previews
            plugin_prefs.set('appearance_css', aa.elements_table.get_data())
            # Generate a new hash
            nsh = hashlib.md5()
            for setting in appearance_settings:
                nsh.update(repr(plugin_prefs.get(setting, appearance_settings[setting])))
        else:
            for setting in appearance_settings:
                plugin_prefs.set(setting, original_settings[setting])
            nsh = osh

        # If there were changes, and there are existing annotations,
        # and there is an active Annotations field, offer to re-render
        field = get_cc_mapping('annotations', 'field', None)
        if osh.digest() != nsh.digest() and existing_annotations(self.parent, field):
            title = 'Update annotations?'
            msg = '<p>Update existing annotations to new appearance settings?</p>'
            d = MessageBox(MessageBox.QUESTION,
                           title, msg,
                           show_copy_button=False)
            self._log_location("QUESTION: %s" % msg)
            if d.exec_():
                self._log_location("Updating existing annotations to modified appearance")

                # Wait for indexing to complete
                while not self.annotated_books_scanner.isFinished():
                    Application.processEvents()

                move_annotations(self, self.annotated_books_scanner.annotation_map,
                    field, field, window_title="Updating appearance")

    def edit_css(self):
        '''
        '''
        self._log_location()
        from calibre_plugins.marvin_manager.book_status import dialog_resources_path
        klass = os.path.join(dialog_resources_path, 'css_editor.py')
        if os.path.exists(klass):
            sys.path.insert(0, dialog_resources_path)
            this_dc = importlib.import_module('css_editor')
            sys.path.remove(dialog_resources_path)
            dlg = this_dc.CSSEditorDialog(self, 'css_editor')
            dlg.initialize(self)
            dlg.exec_()

    def get_eligible_custom_fields(self, eligible_types=[], is_multiple=None):
        '''
        Discover qualifying custom fields for eligible_types[]
        '''
        #self._log_location(eligible_types)

        eligible_custom_fields = {}
        for cf in self.gui.current_db.custom_field_keys():
            cft = self.gui.current_db.metadata_for_field(cf)['datatype']
            cfn = self.gui.current_db.metadata_for_field(cf)['name']
            cfim = self.gui.current_db.metadata_for_field(cf)['is_multiple']
            #self._log("cf: %s  cft: %s  cfn: %s cfim: %s" % (cf, cft, cfn, cfim))
            if cft in eligible_types:
                if is_multiple is not None:
                    if bool(cfim) == is_multiple:
                        eligible_custom_fields[cfn] = cf
                else:
                    eligible_custom_fields[cfn] = cf
        return eligible_custom_fields

    def inventory_complete(self, msg):
        self._log_location(msg)

    def launch_cc_wizard(self, column_type):
        '''
        '''
        def _update_combo_box(comboBox, destination, previous):
            '''
            '''
            cb = getattr(self, comboBox)
            cb.blockSignals(True)
            all_items = [str(cb.itemText(i))
                         for i in range(cb.count())]
            if previous and previous in all_items:
                all_items.remove(previous)
            all_items.append(destination)

            cb.clear()
            cb.addItems(sorted(all_items, key=lambda s: s.lower()))

            # Select the new destination in the comboBox
            idx = cb.findText(destination)
            if idx > -1:
                cb.setCurrentIndex(idx)

            cb.blockSignals(False)

        from calibre_plugins.marvin_manager.book_status import dialog_resources_path

        klass = os.path.join(dialog_resources_path, 'cc_wizard.py')
        if os.path.exists(klass):
            #self._log("importing CC Wizard dialog from '%s'" % klass)
            sys.path.insert(0, dialog_resources_path)
            this_dc = importlib.import_module('cc_wizard')
            sys.path.remove(dialog_resources_path)
            dlg = this_dc.CustomColumnWizard(self,
                                             column_type,
                                             self.WIZARD_PROFILES[column_type],
                                             verbose=True)
            dlg.exec_()

            if dlg.modified_column:
                self._log("modified_column: %s" % dlg.modified_column)

                self.restart_required = True

                destination = dlg.modified_column['destination']
                label = dlg.modified_column['label']
                previous = dlg.modified_column['previous']
                source = dlg.modified_column['source']

                if source == "Annotations":
                    _update_combo_box("annotations_field_comboBox", destination, previous)

                    # Add/update the new destination so save_settings() can find it
                    self.eligible_annotations_fields[destination] = label

                    # Save manually in case user cancels
                    set_cc_mapping('annotations', combobox=destination, field=label)

                elif source == 'Collections':
                    _update_combo_box("collection_field_comboBox", destination, previous)

                    # Add/update the new destination so save_settings() can find it
                    self.eligible_collection_fields[destination] = label

                    # Save manually in case user cancels
                    set_cc_mapping('collections', combobox=destination, field=label)

                elif source == 'Last read':
                    _update_combo_box("date_read_field_comboBox", destination, previous)

                    # Add/update the new destination so save_settings() can find it
                    self.eligible_date_read_fields[destination] = label

                    # Save manually in case user cancels
                    set_cc_mapping('date_read', combobox=destination, field=label)

                elif source == 'Locked':
                    _update_combo_box("locked_field_comboBox", destination, previous)

                    # Add/update the new destination so save_settings() can find it
                    self.eligible_locked_fields[destination] = label

                    # Save manually in case user cancels
                    set_cc_mapping('locked', combobox=destination, field=label)

                elif source == "Progress":
                    _update_combo_box("progress_field_comboBox", destination, previous)

                    # Add/update the new destination so save_settings() can find it
                    self.eligible_progress_fields[destination] = label

                    # Save manually in case user cancels
                    set_cc_mapping('progress', combobox=destination, field=label)

                elif source == "Read":
                    _update_combo_box("read_field_comboBox", destination, previous)

                    # Add/update the new destination so save_settings() can find it
                    self.eligible_read_fields[destination] = label

                    # Save manually in case user cancels
                    set_cc_mapping('read', combobox=destination, field=label)

                elif source == "Reading list":
                    _update_combo_box("reading_list_field_comboBox", destination, previous)

                    # Add/update the new destination so save_settings() can find it
                    self.eligible_reading_list_fields[destination] = label

                    # Save manually in case user cancels
                    set_cc_mapping('reading_list', combobox=destination, field=label)

                elif source == "Word count":
                    _update_combo_box("word_count_field_comboBox", destination, previous)

                    # Add/update the new destination so save_settings() can find it
                    self.eligible_word_count_fields[destination] = label

                    # Save manually in case user cancels
                    set_cc_mapping('word_count', combobox=destination, field=label)
        else:
            self._log("ERROR: Can't import from '%s'" % klass)

    def launch_new_destination_dialog(self, old, new):
        '''
        Return 'move', 'change' or 'cancel'
        '''
        from calibre_plugins.marvin_manager.book_status import dialog_resources_path
        self._log_location()
        klass = os.path.join(dialog_resources_path, 'new_destination.py')
        if os.path.exists(klass):
            self._log("importing new destination dialog from '%s'" % klass)
            sys.path.insert(0, dialog_resources_path)
            this_dc = importlib.import_module('new_destination')
            sys.path.remove(dialog_resources_path)
            dlg = this_dc.NewDestinationDialog(self, old, new)
            dlg.exec_()
            return dlg.command

    def populate_annotations(self):
        datatype = self.WIZARD_PROFILES['Annotations']['datatype']
        self.eligible_annotations_fields = self.get_eligible_custom_fields([datatype])
        self.annotations_field_comboBox.addItems([''])
        ecf = sorted(self.eligible_annotations_fields.keys(), key=lambda s: s.lower())
        self.annotations_field_comboBox.addItems(ecf)

        # Retrieve stored value
        existing = get_cc_mapping('annotations', 'combobox')
        if existing:
            ci = self.annotations_field_comboBox.findText(existing)
            self.annotations_field_comboBox.setCurrentIndex(ci)

    def populate_collections(self):
        datatype = self.WIZARD_PROFILES['Collections']['datatype']
        self.eligible_collection_fields = self.get_eligible_custom_fields([datatype],
                                                                          is_multiple=True)
        self.collection_field_comboBox.addItems([''])
        ecf = sorted(self.eligible_collection_fields.keys(), key=lambda s: s.lower())
        self.collection_field_comboBox.addItems(ecf)

        # Retrieve stored value
        existing = get_cc_mapping('collections', 'combobox')
        if existing:
            ci = self.collection_field_comboBox.findText(existing)
            self.collection_field_comboBox.setCurrentIndex(ci)

    def populate_date_read(self):
        #self.eligible_date_read_fields = self.get_eligible_custom_fields(['datetime'])
        datatype = self.WIZARD_PROFILES['Last read']['datatype']
        self.eligible_date_read_fields = self.get_eligible_custom_fields([datatype])
        self.date_read_field_comboBox.addItems([''])
        ecf = sorted(self.eligible_date_read_fields.keys(), key=lambda s: s.lower())
        self.date_read_field_comboBox.addItems(ecf)

        # Retrieve stored value
        existing = get_cc_mapping('date_read', 'combobox')
        if existing:
            ci = self.date_read_field_comboBox.findText(existing)
            self.date_read_field_comboBox.setCurrentIndex(ci)

    def populate_locked(self):
        datatype = self.WIZARD_PROFILES['Locked']['datatype']
        self.eligible_locked_fields = self.get_eligible_custom_fields([datatype])
        self.locked_field_comboBox.addItems([''])
        ecf = sorted(self.eligible_locked_fields.keys(), key=lambda s: s.lower())
        self.locked_field_comboBox.addItems(ecf)

        # Retrieve stored value
        existing = get_cc_mapping('locked', 'combobox')
        if existing:
            ci = self.locked_field_comboBox.findText(existing)
            self.locked_field_comboBox.setCurrentIndex(ci)

    def populate_progress(self):
        #self.eligible_progress_fields = self.get_eligible_custom_fields(['float'])
        datatype = self.WIZARD_PROFILES['Progress']['datatype']
        self.eligible_progress_fields = self.get_eligible_custom_fields([datatype])
        self.progress_field_comboBox.addItems([''])
        ecf = sorted(self.eligible_progress_fields.keys(), key=lambda s: s.lower())
        self.progress_field_comboBox.addItems(ecf)

        # Retrieve stored value
        existing = get_cc_mapping('progress', 'combobox')
        if existing:
            ci = self.progress_field_comboBox.findText(existing)
            self.progress_field_comboBox.setCurrentIndex(ci)

    def populate_read(self):
        datatype = self.WIZARD_PROFILES['Read']['datatype']
        self.eligible_read_fields = self.get_eligible_custom_fields([datatype])
        self.read_field_comboBox.addItems([''])
        ecf = sorted(self.eligible_read_fields.keys(), key=lambda s: s.lower())
        self.read_field_comboBox.addItems(ecf)

        # Retrieve stored value
        existing = get_cc_mapping('read', 'combobox')
        if existing:
            ci = self.read_field_comboBox.findText(existing)
            self.read_field_comboBox.setCurrentIndex(ci)

    def populate_reading_list(self):
        datatype = self.WIZARD_PROFILES['Reading list']['datatype']
        self.eligible_reading_list_fields = self.get_eligible_custom_fields([datatype])
        self.reading_list_field_comboBox.addItems([''])
        ecf = sorted(self.eligible_reading_list_fields.keys(), key=lambda s: s.lower())
        self.reading_list_field_comboBox.addItems(ecf)

        # Retrieve stored value
        existing = get_cc_mapping('reading_list', 'combobox')
        if existing:
            ci = self.reading_list_field_comboBox.findText(existing)
            self.reading_list_field_comboBox.setCurrentIndex(ci)

    def populate_word_count(self):
        #self.eligible_word_count_fields = self.get_eligible_custom_fields(['int'])
        datatype = self.WIZARD_PROFILES['Word count']['datatype']
        self.eligible_word_count_fields = self.get_eligible_custom_fields([datatype])
        self.word_count_field_comboBox.addItems([''])
        ecf = sorted(self.eligible_word_count_fields.keys(), key=lambda s: s.lower())
        self.word_count_field_comboBox.addItems(ecf)

        # Retrieve stored value
        existing = get_cc_mapping('word_count', 'combobox')
        if existing:
            ci = self.word_count_field_comboBox.findText(existing)
            self.word_count_field_comboBox.setCurrentIndex(ci)

    """
    def select_dropbox_folder(self):
        '''
        '''
        self._log_location()
        dropbox_location = QFileDialog.getExistingDirectory(
            self,
            "Dropbox folder",
            os.path.expanduser("~"),
            QFileDialog.ShowDirsOnly | QFileDialog.DontResolveSymlinks)
        self.dropbox_location_lineedit.setText(unicode(dropbox_location))

    def set_dropbox_syncing(self, state):
        '''
        Called when checkbox changes state, or when restoring state
        Set enabled state of Dropbox folder picker to match
        '''
        self.cfg_dropbox_folder_toolbutton.setEnabled(state)
        self.dropbox_location_lineedit.setEnabled(state)
    """

    def set_restart_required(self, state):
        '''
        Set restart_required flag to show show dialog when closing dialog
        '''
        self.restart_required = True

    """
    def save_combobox_setting(self, cb, index):
        '''
        Apply changes immediately
        '''
        cf = str(getattr(self, cb).currentText())
        self._log_location("%s => %s" % (cb, repr(cf)))

        if cb == 'annotations_field_comboBox':
            field = None
            if cf:
                field = self.eligible_annotations_fields[cf]
            set_cc_mapping('annotations', combobox=cf, field=field)
    """

    def save_settings(self):
        self._log_location()

        # Annotations field
        cf = unicode(self.annotations_field_comboBox.currentText())
        field = None
        if cf:
            field = self.eligible_annotations_fields[cf]
        set_cc_mapping('annotations', combobox=cf, field=field)

        # Collections field
        cf = unicode(self.collection_field_comboBox.currentText())
        field = None
        if cf:
            field = self.eligible_collection_fields[cf]
        set_cc_mapping('collections', combobox=cf, field=field)

        # Date read field
        cf = unicode(self.date_read_field_comboBox.currentText())
        field = None
        if cf:
            field = self.eligible_date_read_fields[cf]
        set_cc_mapping('date_read', combobox=cf, field=field)

        # Locked field
        cf = unicode(self.locked_field_comboBox.currentText())
        field = None
        if cf:
            field = self.eligible_locked_fields[cf]
        set_cc_mapping('locked', combobox=cf, field=field)

        # Progress field
        cf = unicode(self.progress_field_comboBox.currentText())
        field = None
        if cf:
            field = self.eligible_progress_fields[cf]
        set_cc_mapping('progress', combobox=cf, field=field)

        # Read field
        cf = unicode(self.read_field_comboBox.currentText())
        field = None
        if cf:
            field = self.eligible_read_fields[cf]
        set_cc_mapping('read', combobox=cf, field=field)

        # Reading list field
        cf = unicode(self.reading_list_field_comboBox.currentText())
        field = None
        if cf:
            field = self.eligible_reading_list_fields[cf]
        set_cc_mapping('reading_list', combobox=cf, field=field)

        # Word count field
        cf = unicode(self.word_count_field_comboBox.currentText())
        field = None
        if cf:
            field = self.eligible_word_count_fields[cf]
        set_cc_mapping('word_count', combobox=cf, field=field)

        '''
        # Save Dropbox settings
        self.prefs.set('dropbox_syncing', self.dropbox_syncing_checkbox.isChecked())
        self.prefs.set('dropbox_folder', unicode(self.dropbox_location_lineedit.text()))
        '''

        # Save general settings
        self.prefs.set('apply_markers_to_duplicates', self.duplicate_markers_checkbox.isChecked())
        self.prefs.set('apply_markers_to_updated', self.updated_markers_checkbox.isChecked())
        self.prefs.set('auto_refresh_at_startup', self.auto_refresh_checkbox.isChecked())
        self.prefs.set('show_progress_as_percentage', self.reading_progress_checkbox.isChecked())

        # Save debug settings
        self.prefs.set('debug_plugin', self.debug_plugin_checkbox.isChecked())
        self.prefs.set('debug_libimobiledevice', self.debug_libimobiledevice_checkbox.isChecked())

        # If restart needed, inform user
        if self.restart_required:
            do_restart = show_restart_warning('Restart calibre for the changes to be applied.',
                                              parent=self.gui)
            if do_restart:
                self.gui.quit(restart=True)

    def start_inventory(self):
        self._log_location()
        self.annotated_books_scanner.start()
Example #9
0
class LLT_ConjAdd(QMainWindow):
    def __init__(self):
        super(LLT_ConjAdd, self).__init__()
        self.w = QWidget()
        self.setCentralWidget(self.w)

        #construct GUI
        self.setWindowTitle("Verb Conjugations")
        self.setGeometry(0, 0, 900, 600)

        self.conjDicList = []
        self.wordList = []
        self.newDic = {'INF': '', 'IND': '', 'SUB': '', 'IMP': ''}

        self.headingLab = QLabel("Add New Conjugation Table")
        self.wordInfLab = QLabel("Infinitive")
        self.wordInfLab.setAlignment(Qt.AlignRight)
        self.infEntry = QLineEdit()

        #INDICATIVE FORM TABLE
        self.indLab = QLabel("Indicative")
        self.indYoLab = QLabel("Yo")
        self.indYoLab.setAlignment(Qt.AlignRight)
        self.indTuLab = QLabel("Tú")
        self.indTuLab.setAlignment(Qt.AlignRight)
        self.indUstLab = QLabel("Él/la, Ud")
        self.indUstLab.setAlignment(Qt.AlignRight)
        self.indNosLab = QLabel("Nosotros")
        self.indNosLab.setAlignment(Qt.AlignRight)
        self.indUstdsLab = QLabel("Ellos/as, Uds")
        self.indUstdsLab.setAlignment(Qt.AlignRight)

        self.indPresLab = QLabel("Present")
        self.indPretLab = QLabel("Preterite")
        self.indImpLab = QLabel("Imperfect")
        self.indFutLab = QLabel("Future")
        self.indCondLab = QLabel("Conditional")

        self.entryGridInd = QGridLayout()
        for i in range(5):
            for j in range(5):
                self.entry = QLineEdit()
                self.entryGridInd.addWidget(self.entry, i, j)

        #SUBJUNCTIVE FORM TABLE
        self.subLab = QLabel("Subjunctive")
        self.subYoLab = QLabel("Yo")
        self.subYoLab.setAlignment(Qt.AlignRight)
        self.subTuLab = QLabel("Tú")
        self.subTuLab.setAlignment(Qt.AlignRight)
        self.subUstLab = QLabel("Él/la, Ud")
        self.subUstLab.setAlignment(Qt.AlignRight)
        self.subNosLab = QLabel("Nosotros/as")
        self.subNosLab.setAlignment(Qt.AlignRight)
        self.subUstdsLab = QLabel("Ellos/as, Uds")
        self.subUstdsLab.setAlignment(Qt.AlignRight)

        self.subPresLab = QLabel("Present")
        self.subImpLab = QLabel("Imperfect")
        self.subFutLab = QLabel("Future")

        self.entryGridSub = QGridLayout()
        for i in range(5):
            for j in range(3):
                self.entry = QLineEdit()
                self.entryGridSub.addWidget(self.entry, i, j)

        #IMPERATIVE FORM TABLE
        self.impvLab = QLabel("Imperative")
        self.impvTu = QLabel("Tú")
        self.impvTu.setAlignment(Qt.AlignRight)
        self.impvUd = QLabel("Usted")
        self.impvUd.setAlignment(Qt.AlignRight)
        self.impvNos = QLabel("Nosotros/as")
        self.impvNos.setAlignment(Qt.AlignRight)
        self.impvUdes = QLabel("Ustedes")
        self.impvUdes.setAlignment(Qt.AlignRight)

        self.impvAffLab = QLabel("Affirmative")
        self.impvNegLab = QLabel("Negative")

        self.entryGridImpv = QGridLayout()
        for i in range(4):
            for j in range(2):
                self.entry = QLineEdit()
                self.entryGridImpv.addWidget(self.entry, i, j)
        self.checkBut = QPushButton("Check")
        self.checkBut.clicked.connect(self.check)
        self.saveBut = QPushButton("Save")
        self.saveBut.clicked.connect(self.save)
        self.newBut = QPushButton("New Word")
        self.newBut.clicked.connect(self.new)
        self.clearBut = QPushButton("Clear")
        self.clearBut.clicked.connect(self.clear)
        self.quitBut = QPushButton("Quit")
        self.quitBut.clicked.connect(self.quit)

        self.theGrid = QGridLayout()

        self.theGrid.addWidget(self.headingLab, 0, 0)
        self.theGrid.addWidget(self.wordInfLab, 1, 0)
        self.theGrid.addWidget(self.infEntry, 1, 1)

        self.theGrid.addWidget(self.indLab, 3, 0)
        self.theGrid.addWidget(self.indPresLab, 3, 1)
        self.theGrid.addWidget(self.indPretLab, 3, 2)
        self.theGrid.addWidget(self.indImpLab, 3, 3)
        self.theGrid.addWidget(self.indFutLab, 3, 4)
        self.theGrid.addWidget(self.indCondLab, 3, 5)
        self.theGrid.addWidget(self.indYoLab, 4, 0)
        self.theGrid.addWidget(self.indTuLab, 5, 0)
        self.theGrid.addWidget(self.indUstLab, 6, 0)
        self.theGrid.addWidget(self.indNosLab, 7, 0)
        self.theGrid.addWidget(self.indUstdsLab, 8, 0)
        self.theGrid.addLayout(self.entryGridInd, 4, 1, 5, 5)

        self.theGrid.addWidget(self.subLab, 11, 0)
        self.theGrid.addWidget(self.subPresLab, 11, 1)
        self.theGrid.addWidget(self.subImpLab, 11, 2)
        self.theGrid.addWidget(self.subFutLab, 11, 3)
        self.theGrid.addWidget(self.subYoLab, 12, 0)
        self.theGrid.addWidget(self.subTuLab, 13, 0)
        self.theGrid.addWidget(self.subUstLab, 14, 0)
        self.theGrid.addWidget(self.subNosLab, 15, 0)
        self.theGrid.addWidget(self.subUstdsLab, 16, 0)
        self.theGrid.addLayout(self.entryGridSub, 12, 1, 5, 3)

        self.theGrid.addWidget(self.impvLab, 19, 0)
        self.theGrid.addWidget(self.impvAffLab, 19, 1)
        self.theGrid.addWidget(self.impvNegLab, 19, 2)
        self.theGrid.addWidget(self.impvTu, 20, 0)
        self.theGrid.addWidget(self.impvUd, 21, 0)
        self.theGrid.addWidget(self.impvNos, 22, 0)
        self.theGrid.addWidget(self.impvUdes, 23, 0)
        self.theGrid.addLayout(self.entryGridImpv, 20, 1, 4, 2)

        self.theGrid.addWidget(self.checkBut, 19, 5)
        self.theGrid.addWidget(self.saveBut, 20, 5)
        self.theGrid.addWidget(self.clearBut, 21, 5)
        self.theGrid.addWidget(self.newBut, 22, 5)
        self.theGrid.addWidget(self.quitBut, 23, 5)

        for i in range(24):
            self.theGrid.setRowStretch(i, 1)
        for j in range(6):
            self.theGrid.setColumnStretch(j, 1)
        self.w.setLayout(self.theGrid)
        self.getDic()

    def check(self):
        word = self.infEntry.text().upper()
        if word in self.wordList:
            msgBox = QMessageBox()
            msgBox.setText(word + ' already in dictionary')
            msgBox.exec_()
        else:
            msgBox = QMessageBox()
            msgBox.setText(word + ' not in dictionary yet')
            msgBox.exec_()

    def save(self):
        infinitive = self.infEntry.text().upper()
        if infinitive in self.wordList:
            msgBox = QMessageBox()
            msgBox.setText(infinitive + ' already in dictionary')
            msgBox.exec_()
        else:
            self.wordList.append(infinitive)
            indList = []
            subList = []
            impList = []
            for i in range(self.entryGridInd.count()):
                item = self.entryGridInd.itemAt(i)
                child = item.widget()
                indList.append(child.text().upper())

            for i in range(self.entryGridSub.count()):
                item = self.entryGridSub.itemAt(i)
                child = item.widget()
                subList.append(child.text().upper())

            for i in range(self.entryGridImpv.count()):
                item = self.entryGridImpv.itemAt(i)
                child = item.widget()
                impList.append(child.text().upper())

                self.newDic['INF'] = infinitive
                self.newDic['IND'] = indList
                self.newDic['SUB'] = subList
                self.newDic['IMP'] = impList

            self.conjDicList.append(self.newDic)
            c = open('conj.txt', 'w')
            json.dump(self.conjDicList, c)
            c.close()
            msgBox = QMessageBox()
            msgBox.setText(infinitive + ' has been saved')
            msgBox.exec_()
            self.newDic = {'INF': '', 'IND': '', 'SUB': '', 'IMP': ''}

    def new(self):
        confirm = QMessageBox.question(
            self.w, 'New Word',
            'Are you sure you want to clear all entries \nand start a new word?',
            QMessageBox.Yes | QMessageBox.No)
        if confirm == QMessageBox.Yes:
            self.infEntry.clear()
            for i in range(self.entryGridInd.count()):
                item = self.entryGridInd.itemAt(i)
                child = item.widget()
                child.clear()
            for i in range(self.entryGridSub.count()):
                item = self.entryGridSub.itemAt(i)
                child = item.widget()
                child.clear()
            for i in range(self.entryGridImpv.count()):
                item = self.entryGridImpv.itemAt(i)
                child = item.widget()
                child.clear()
        else:
            pass

    def clear(self):
        confirm = QMessageBox.question(
            self.w, 'Clear', 'Are you sure you want to clear all entries?',
            QMessageBox.Yes | QMessageBox.No)
        if confirm == QMessageBox.Yes:
            self.infEntry.clear()
            for i in range(self.entryGridInd.count()):
                item = self.entryGridInd.itemAt(i)
                child = item.widget()
                child.clear()
            for i in range(self.entryGridSub.count()):
                item = self.entryGridSub.itemAt(i)
                child = item.widget()
                child.clear()
            for i in range(self.entryGridImpv.count()):
                item = self.entryGridImpv.itemAt(i)
                child = item.widget()
                child.clear()
        else:
            pass

    def quit(self):
        confirm = QMessageBox.question(self.w, 'Quit',
                                       'Are you sure you want to exit?',
                                       QMessageBox.Yes | QMessageBox.No)
        if confirm == QMessageBox.Yes:
            self.close()
        else:
            pass

    def getDic(self):
        try:
            c = open('conj.txt', 'r')
            self.conjDicList = json.load(c)
            c.close()
            for item in self.conjDicList:
                self.wordList.append(item['INF'])

        except:
            self.conjDicList = []
    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)
Example #11
0
    def __init__(self, parent = None):
        super(MainWidget, self).__init__(parent)

        # member variables
        self._lTheorySpd = 0
        self._rTheorySpd = 0
        self._serialSend = SerialSend()

        # mainWindow properties
        self.setObjectName('MainWidget')
        self.setWindowIcon(QIcon(':/image/default/app.icon'))
        self.setWindowTitle('%s V%s' % (
                            qApp.applicationDisplayName(),
                            qApp.applicationVersion()))
        self.resize(800, 480)

        # mainWindow layout

        # top

        self.groupBoxTop = QGroupBox(self)
        self.groupBoxTop.setObjectName('groupBoxTop')

        # command dashboard
        buttonLeftPower = JSwitchButton(parent = self.groupBoxTop)
        buttonRightPower = JSwitchButton(parent = self.groupBoxTop)
        buttonSettings = QPushButton(self.groupBoxTop)
        buttonHistory = QPushButton(self.groupBoxTop)
        buttonQuit = QPushButton(self.groupBoxTop)

        buttonLeftPower.setObjectName('buttonLeftPower')
        buttonRightPower.setObjectName('buttonRightPower')
        buttonSettings.setObjectName('buttonSettings')
        buttonHistory.setObjectName('buttonHistory')
        buttonQuit.setObjectName('buttonQuit')

        areaPortState = QWidget(self)
        areaPortState.setObjectName('areaPortState')
        areaPortState.setStyleSheet('QWidget#areaPortState{border-radius:3px;'
                                    'border:1px solid #505050;'
                                    'background-color:rgba(64,64,64,50);}')
        vertLayoutPortState = QVBoxLayout(areaPortState)
        vertLayoutPortState.setContentsMargins(50, 2, 50, 2)
        vertLayoutPortState.setSpacing(3)

        buttonPortState = JSwitchButton(pixmap = QPixmap(':/carmonitor/image/button-port-state.png'), parent = areaPortState)
        buttonPortState.setObjectName('buttonPortState')
        vertLayoutPortState.addWidget(QLabel('串口', areaPortState), 0, Qt.AlignHCenter)
        vertLayoutPortState.addWidget(buttonPortState)

        #
        horiLayoutTop = QHBoxLayout(self.groupBoxTop)
        horiLayoutTop.setContentsMargins(0, 0, 0, 0)
        horiLayoutTop.addSpacing(25)
        horiLayoutTop.addWidget(buttonSettings, 0, Qt.AlignLeft)
        horiLayoutTop.addSpacing(20)
        horiLayoutTop.addWidget(buttonHistory, 0, Qt.AlignLeft)
        horiLayoutTop.addSpacing(65)
        horiLayoutTop.addWidget(buttonLeftPower)
        horiLayoutTop.addWidget(QLabel('左电源开关', self.groupBoxTop))
        horiLayoutTop.addStretch()
        horiLayoutTop.addWidget(areaPortState, 0, Qt.AlignTop)
        horiLayoutTop.addStretch()
        horiLayoutTop.addWidget(QLabel('右电源开关', self.groupBoxTop))
        horiLayoutTop.addWidget(buttonRightPower)
        horiLayoutTop.addSpacing(150)
        horiLayoutTop.addWidget(buttonQuit, 0, Qt.AlignRight)
        horiLayoutTop.addSpacing(25)

        # middle

        # curves
        self.curveLBP = CurveWidget(title = '左刹车压力(MPa)', parent = self)
        self.curveLRP = CurveWidget(title = '左转速(r/min)', parent = self)
        self.curveRBP = CurveWidget(title = '右刹车压力(MPa)', parent = self)
        self.curveRRP = CurveWidget(title = '右转速(r/min)', parent = self)

        self.curveLBP.setObjectName('curveLBP')
        self.curveLRP.setObjectName('curveLRP')
        self.curveRBP.setObjectName('curveRBP')
        self.curveRRP.setObjectName('curveRRP')

        # self.curveLBP.setAxisScale(QwtPlot.yLeft, 0, 30, 5)

        # areaMiddle
        self.areaMiddle = QWidget(self)
        self.areaMiddle.setObjectName('areaMiddle')
        self.areaMiddle.setFixedWidth(280)

        #
        groupBoxStatus = QGroupBox(self)
        groupBoxStatus.setObjectName('groupBoxStatus')

        # status-view
        gridLayoutStatus = QGridLayout(groupBoxStatus)
        gridLayoutStatus.setContentsMargins(5, 5, 5, 5)
        gridLayoutStatus.setHorizontalSpacing(8)
        gridLayoutStatus.setVerticalSpacing(3)

        # Brake-Command
        editLeftBrakeCmd = QLineEdit(groupBoxStatus)
        editRightBrakeCmd = QLineEdit(groupBoxStatus)
        editLeftBrakeCmd.setObjectName('editLeftBrakeCmd')
        editRightBrakeCmd.setObjectName('editRightBrakeCmd')
        editLeftBrakeCmd.setReadOnly(True)
        editRightBrakeCmd.setReadOnly(True)
        gridLayoutStatus.addWidget(QLabel('刹车指令:', groupBoxStatus),
                                   0, 0, 1, 2, Qt.AlignCenter)
        gridLayoutStatus.addWidget(editLeftBrakeCmd, 1, 0, 1, 1)
        gridLayoutStatus.addWidget(editRightBrakeCmd, 1, 1, 1, 1)

        # Major Brake Pressure
        self.editMLeftBrakeP = QLineEdit(groupBoxStatus)
        self.editMRightBrakeP = QLineEdit(groupBoxStatus)
        self.editMLeftBrakeP.setObjectName('editMLeftBrakeP')
        self.editMRightBrakeP.setObjectName('editMRightBrakeP')
        self.editMLeftBrakeP.setReadOnly(True)
        self.editMRightBrakeP.setReadOnly(True)
        gridLayoutStatus.addWidget(QLabel('主刹车压力:', groupBoxStatus),
                                   2, 0, 1, 2, Qt.AlignCenter)
        gridLayoutStatus.addWidget(self.editMLeftBrakeP, 3, 0, 1, 1)
        gridLayoutStatus.addWidget(self.editMRightBrakeP, 3, 1, 1, 1)

        # Assistant Brake Pressure
        self.editALeftBrakeP = QLineEdit(groupBoxStatus)
        self.editARightBrakeP = QLineEdit(groupBoxStatus)
        self.editALeftBrakeP.setObjectName('editALeftBrakeP')
        self.editARightBrakeP.setObjectName('editARightBrakeP')
        self.editALeftBrakeP.setReadOnly(True)
        self.editARightBrakeP.setReadOnly(True)
        gridLayoutStatus.addWidget(QLabel('副刹车压力:', groupBoxStatus),
                                   4, 0, 1, 2, Qt.AlignCenter)
        gridLayoutStatus.addWidget(self.editALeftBrakeP, 5, 0, 1, 1)
        gridLayoutStatus.addWidget(self.editARightBrakeP, 5, 1, 1, 1)

        # Rotation Rate
        self.editLeftRotateRate = QLineEdit(groupBoxStatus)
        self.editRightRotateRate = QLineEdit(groupBoxStatus)
        self.editLeftRotateRate.setObjectName('editLeftRotateRate')
        self.editRightRotateRate.setObjectName('editRightRotateRate')
        gridLayoutStatus.addWidget(QLabel('实际转速:', groupBoxStatus),
                                   6, 0, 1, 2, Qt.AlignCenter)
        gridLayoutStatus.addWidget(self.editLeftRotateRate, 7, 0, 1, 1)
        gridLayoutStatus.addWidget(self.editRightRotateRate, 7, 1, 1, 1)

        # Theory Rotation Rate
        self.editTheoryLeftRotateRate = QLineEdit(groupBoxStatus)
        self.editTheoryRightRotateRate = QLineEdit(groupBoxStatus)
        self.editTheoryLeftRotateRate.setObjectName('editTheoryLeftRotateRate')
        self.editTheoryRightRotateRate.setObjectName('editTheoryRightRotateRate')
        gridLayoutStatus.addWidget(QLabel('理论转速:', groupBoxStatus),
                                   8, 0, 1, 2, Qt.AlignCenter)
        gridLayoutStatus.addWidget(self.editTheoryLeftRotateRate, 9, 0, 1, 1)
        gridLayoutStatus.addWidget(self.editTheoryRightRotateRate, 9, 1, 1, 1)

        #
        groupBoxCtrl = QGroupBox(self)
        groupBoxCtrl.setObjectName('groupBoxCtrl')

        # status-view
        gridLayoutCtrl = QGridLayout(groupBoxCtrl)
        gridLayoutCtrl.setContentsMargins(5, 5, 5, 5)
        gridLayoutCtrl.setSpacing(20)

        # left-button
        buttonLeftDashboard = JDashButton('左指令旋钮', groupBoxCtrl)
        buttonLeftSpeedGain = JDashButton('左转速增益', groupBoxCtrl)
        buttonLeftSpeedKnob = JDashButton('左转速增益', groupBoxCtrl)
        buttonLeftTracksip = JTracksipButton(parent = groupBoxCtrl)
        buttonLeftDashboard.setObjectName('buttonLeftDashboard')
        buttonLeftSpeedGain.setObjectName('buttonLeftSpeedGain')
        buttonLeftSpeedKnob.setObjectName('buttonLeftSpeedKnob')
        buttonLeftTracksip.setObjectName('buttonLeftTracksip')
        buttonLeftTracksip.setFixedSize(110, 45)

        # right-button
        buttonRightDashboard = JDashButton('右指令旋钮', groupBoxCtrl)
        buttonRightSpeedGain = JDashButton('右转速增益', groupBoxCtrl)
        buttonRightSpeedKnob = JDashButton('右转速增益', groupBoxCtrl)
        buttonRightTracksip = JTracksipButton(parent = groupBoxCtrl)
        buttonRightDashboard.setObjectName('buttonRightDashboard')
        buttonRightSpeedGain.setObjectName('buttonRightSpeedGain')
        buttonRightSpeedKnob.setObjectName('buttonRightSpeedKnob')
        buttonRightTracksip.setObjectName('buttonRightTracksip')
        buttonRightTracksip.setFixedSize(110, 45)

        horiLayoutTracksip = QHBoxLayout()
        horiLayoutTracksip.setContentsMargins(0, 0, 0, 0)
        horiLayoutTracksip.setSpacing(5)
        horiLayoutTracksip.addWidget(buttonLeftTracksip)
        horiLayoutTracksip.addWidget(QLabel('打滑', self), 0, Qt.AlignHCenter)
        horiLayoutTracksip.addWidget(buttonRightTracksip)
        gridLayoutCtrl.addLayout(horiLayoutTracksip, 0, 0, 1, 2)

        horiLayoutDashboard = QHBoxLayout()
        horiLayoutDashboard.setContentsMargins(0, 0, 0, 0)
        horiLayoutDashboard.setSpacing(5)
        horiLayoutDashboard.addWidget(buttonLeftDashboard)
        horiLayoutDashboard.addWidget(QLabel('    ', self), 0, Qt.AlignHCenter)
        horiLayoutDashboard.addWidget(buttonRightDashboard)
        gridLayoutCtrl.addLayout(horiLayoutDashboard, 1, 0, 1, 2)

        horiLayoutSpeedGain = QHBoxLayout()
        horiLayoutSpeedGain.setContentsMargins(0, 0, 0, 0)
        horiLayoutSpeedGain.setSpacing(5)
        horiLayoutSpeedGain.addWidget(buttonLeftSpeedGain)
        horiLayoutSpeedGain.addWidget(QLabel('(粗调)', self), 0, Qt.AlignHCenter)
        horiLayoutSpeedGain.addWidget(buttonRightSpeedGain)
        gridLayoutCtrl.addLayout(horiLayoutSpeedGain, 2, 0, 1, 2)


        horiLayoutSpeedKnob = QHBoxLayout()
        horiLayoutSpeedKnob.setContentsMargins(0, 0, 0, 0)
        horiLayoutSpeedKnob.setSpacing(5)
        horiLayoutSpeedKnob.addWidget(buttonLeftSpeedKnob)
        horiLayoutSpeedKnob.addWidget(QLabel('(细调)', self), 0, Qt.AlignHCenter)
        horiLayoutSpeedKnob.addWidget(buttonRightSpeedKnob)
        gridLayoutCtrl.addLayout(horiLayoutSpeedKnob, 3, 0, 1, 2)

        #
        vertLayoutMid = QVBoxLayout(self.areaMiddle)
        vertLayoutMid.setContentsMargins(0, 0, 0, 0)
        vertLayoutMid.setSpacing(0)
        vertLayoutMid.addWidget(groupBoxStatus)
        vertLayoutMid.addWidget(groupBoxCtrl)
        vertLayoutMid.addSpacing(20)

        #
        gridLayoutBottom = QGridLayout()
        gridLayoutBottom.setContentsMargins(0, 0, 0, 0)
        gridLayoutBottom.setSpacing(1)
        gridLayoutBottom.addWidget(self.curveLBP, 0, 0, 1, 1)
        gridLayoutBottom.addWidget(self.curveLRP, 1, 0, 1, 1)
        gridLayoutBottom.addWidget(self.areaMiddle, 0, 1, 2, 1)
        gridLayoutBottom.addWidget(self.curveRBP, 0, 2, 1, 1)
        gridLayoutBottom.addWidget(self.curveRRP, 1, 2, 1, 1)

        # main-layout
        vertLayoutMain = QVBoxLayout(self)
        vertLayoutMain.setContentsMargins(5, 5, 5, 5)
        vertLayoutMain.addWidget(self.groupBoxTop)
        vertLayoutMain.addLayout(gridLayoutBottom)

        # global properties
        qApp.setProperty('MainWidget', self)
        self._serialProxy = SerialPortProxy(self)
        self._serialProxy._serialSimulate = SerialSimulate(self._serialProxy)  #
        qApp.setProperty('SerialProxy', self._serialProxy)

        #
        buttonSettings.clicked.connect(self.onButtonSettingsClicked)
        buttonHistory.clicked.connect(self.onButtonHistoryClicked)
        buttonPortState.clicked.connect(self.onButtonPortStateClicked)
        buttonQuit.clicked.connect(self.onButtonQuitClicked)

        # curves
        self.curveLBP.doubleClicked.connect(self.onCurveDoubleClicked)
        self.curveLRP.doubleClicked.connect(self.onCurveDoubleClicked)
        self.curveRBP.doubleClicked.connect(self.onCurveDoubleClicked)
        self.curveRRP.doubleClicked.connect(self.onCurveDoubleClicked)

        # switch-power
        buttonLeftPower.stateChanged.connect(self.onButtonLeftPowerStateChanged)
        buttonRightPower.stateChanged.connect(self.onButtonRightPowerStateChanged)

        # switch-tracksip
        buttonLeftTracksip.stateChanged.connect(self.onButtonLeftTracksipStateChanged)
        buttonRightTracksip.stateChanged.connect(self.onButtonRightTracksipStateChanged)

        self._serialProxy.stateChanged.connect(self.onSerialStateChanged)
        self._serialProxy.serialPortError.connect(self.onSerialPortError)
        self._serialProxy.displayRespond.connect(self.onSerialDisplayRespond)

        #
        buttonLeftSpeedGain.clicked.connect(self.execSliderWidget)
        buttonLeftSpeedKnob.clicked.connect(self.execSliderWidget)
        buttonRightSpeedGain.clicked.connect(self.execSliderWidget)
        buttonRightSpeedKnob.clicked.connect(self.execSliderWidget)

        # final initialization

        self.editMLeftBrakeP.setText('0 MPa')
        self.editMRightBrakeP.setText('0 MPa')
        self.editALeftBrakeP.setText('0 MPa')
        self.editARightBrakeP.setText('0 MPa')

        self.editLeftRotateRate.setText('0 r/min')
        self.editRightRotateRate.setText('0 r/min')
        self.editTheoryLeftRotateRate.setText('0 r/min')
        self.editTheoryRightRotateRate.setText('0 r/min')

        #
        c_memset(self._serialSend, 0, ctypes.sizeof(self._serialSend))

        # SQL
        sqlName = applicationDirPath() \
            + '/../data/cm-' \
            + QDateTime.currentDateTime().toLocalTime().toString('yyyy-MM-dd-HH-mm-ss') \
            + '.db'
        if not DatabaseMgr().create(sqlName):
            assert(False)

        # start serialport
        self._serialProxy.start()

        #
        buttonLeftTracksip.setState(self._serialSend.ctrlWord.lTracksip)
        buttonRightTracksip.setState(self._serialSend.ctrlWord.lTracksip)