Beispiel #1
0
class ConfigWidget(QWidget):

    def __init__(self, plugin_action):
        QWidget.__init__(self)
        self.plugin_action = plugin_action
        layout = QVBoxLayout(self)
        self.setLayout(layout)
        self.help_anchor = "configuration"
        
        title_layout = ImageTitleLayout(self, 'images/icon.png', 'Sony Utilities Options')
        layout.addLayout(title_layout)

#        c = plugin_prefs[STORE_NAME]
        library_config = get_library_config(self.plugin_action.gui.current_db)

        custom_column_group = QGroupBox(_('Custom Columns'), self)
        layout.addWidget(custom_column_group )
        options_layout = QGridLayout()
        custom_column_group.setLayout(options_layout)

        avail_text_columns   = self.get_text_custom_columns()
        avail_number_columns = self.get_number_custom_columns()
        avail_rating_columns = self.get_rating_custom_columns()
        avail_date_columns   = self.get_date_custom_columns()
#        debug_print("avail_rating_columns=", avail_rating_columns)
#        debug_print("default columns=", self.plugin_action.gui.library_view.model().orig_headers)
        current_Location_column  = library_config.get(KEY_CURRENT_LOCATION_CUSTOM_COLUMN, DEFAULT_LIBRARY_VALUES[KEY_CURRENT_LOCATION_CUSTOM_COLUMN])
        precent_read_column      = library_config.get(KEY_PERCENT_READ_CUSTOM_COLUMN, DEFAULT_LIBRARY_VALUES[KEY_PERCENT_READ_CUSTOM_COLUMN])
        rating_column            = library_config.get(KEY_RATING_CUSTOM_COLUMN, DEFAULT_LIBRARY_VALUES[KEY_RATING_CUSTOM_COLUMN])
        last_read_column         = library_config.get(KEY_LAST_READ_CUSTOM_COLUMN, DEFAULT_LIBRARY_VALUES[KEY_LAST_READ_CUSTOM_COLUMN])

        store_on_connect         = get_plugin_pref(COMMON_OPTIONS_STORE_NAME, KEY_STORE_ON_CONNECT)
        prompt_to_store          = get_plugin_pref(COMMON_OPTIONS_STORE_NAME, KEY_PROMPT_TO_STORE)
        store_if_more_recent     = get_plugin_pref(COMMON_OPTIONS_STORE_NAME, KEY_STORE_IF_MORE_RECENT)
        do_not_store_if_reopened = get_plugin_pref(COMMON_OPTIONS_STORE_NAME, KEY_DO_NOT_STORE_IF_REOPENED)

#         do_check_for_firmware_updates = get_plugin_pref(UPDATE_OPTIONS_STORE_NAME, KEY_DO_UPDATE_CHECK)
#         do_early_firmware_updates     = get_plugin_pref(UPDATE_OPTIONS_STORE_NAME, KEY_DO_EARLY_FIRMWARE_CHECK)
#         self.update_check_last_time   = get_plugin_pref(UPDATE_OPTIONS_STORE_NAME, KEY_LAST_FIRMWARE_CHECK_TIME)

        do_daily_backup          = get_plugin_pref(BACKUP_OPTIONS_STORE_NAME, KEY_DO_DAILY_BACKUP)
        dest_directory           = get_plugin_pref(BACKUP_OPTIONS_STORE_NAME, KEY_BACKUP_DEST_DIRECTORY)
        copies_to_keep           = get_plugin_pref(BACKUP_OPTIONS_STORE_NAME, KEY_BACKUP_COPIES_TO_KEEP)
#        debug_print("current_Location_column=%s, precent_read_column=%s, rating_column=%s" % (current_Location_column, precent_read_column, rating_column))

        current_Location_label = QLabel(_('Current Reading Location Column:'), self)
        current_Location_label.setToolTip(_("Select a custom column to store the current reading location. The column type must be 'text'. Leave this blank if you do not want to store or restore the current reading location."))
        self.current_Location_combo = CustomColumnComboBox(self, avail_text_columns, current_Location_column)
        current_Location_label.setBuddy(self.current_Location_combo)
        options_layout.addWidget(current_Location_label, 0, 0, 1, 1)
        options_layout.addWidget(self.current_Location_combo, 0, 1, 1, 1)
        
        percent_read_label = QLabel(_('Percent Read Column:'), self)
        percent_read_label.setToolTip(_("Column used to store the current percent read. The column type must be a 'integer'. Leave this blank if you do not want to store or restore the percentage read."))
        self.percent_read_combo = CustomColumnComboBox(self, avail_number_columns, precent_read_column)
        percent_read_label.setBuddy(self.percent_read_combo)
        options_layout.addWidget(percent_read_label, 2, 0, 1, 1)
        options_layout.addWidget(self.percent_read_combo, 2, 1, 1, 1)

        rating_label = QLabel(_('Rating Column:'), self)
        rating_label.setToolTip(_("Column used to store the rating. The column type must be a 'integer'. Leave this blank if you do not want to store or restore the rating."))
        self.rating_combo = CustomColumnComboBox(self, avail_rating_columns, rating_column)
        rating_label.setBuddy(self.rating_combo)
        options_layout.addWidget(rating_label, 3, 0, 1, 1)
        options_layout.addWidget(self.rating_combo, 3, 1, 1, 1)

        last_read_label = QLabel(_('Last Read Column:'), self)
        last_read_label.setToolTip(_("Column used to store when the book was last read. The column type must be a 'Date'. Leave this blank if you do not want to store the last read timestamp."))
        self.last_read_combo = CustomColumnComboBox(self, avail_date_columns, last_read_column)
        last_read_label.setBuddy(self.last_read_combo)
        options_layout.addWidget(last_read_label, 4, 0, 1, 1)
        options_layout.addWidget(self.last_read_combo, 4, 1, 1, 1)

        auto_store_group = QGroupBox(_('Store on connect'), self)
        layout.addWidget(auto_store_group )
        options_layout = QGridLayout()
        auto_store_group.setLayout(options_layout)

        self.store_on_connect_checkbox = QCheckBox(_("Store current bookmarks on connect"), self)
        self.store_on_connect_checkbox.setToolTip(_("When this is checked, the library will be updated with the current bookmark for all books on the device."))
        self.store_on_connect_checkbox.setCheckState(Qt.Checked if store_on_connect else Qt.Unchecked)
        self.store_on_connect_checkbox.clicked.connect(self.store_on_connect_checkbox_clicked)
        options_layout.addWidget(self.store_on_connect_checkbox, 0, 0, 1, 3)

        self.prompt_to_store_checkbox = QCheckBox(_("Prompt to store any changes"), self)
        self.prompt_to_store_checkbox.setToolTip(_("Enable this to be prompted to save the changed bookmarks after an automatic store is done."))
        self.prompt_to_store_checkbox.setCheckState(Qt.Checked if prompt_to_store else Qt.Unchecked)
        self.prompt_to_store_checkbox.setEnabled(store_on_connect)
        options_layout.addWidget(self.prompt_to_store_checkbox, 1, 0, 1, 1)

        self.store_if_more_recent_checkbox = QCheckBox(_("Only if more recent"), self)
        self.store_if_more_recent_checkbox.setToolTip(_("Only store the reading position if the last read timestamp on the device is more recent than in the library."))
        self.store_if_more_recent_checkbox.setCheckState(Qt.Checked if store_if_more_recent else Qt.Unchecked)
        self.store_if_more_recent_checkbox.setEnabled(store_on_connect)
        options_layout.addWidget(self.store_if_more_recent_checkbox, 1, 1, 1, 1)

        self.do_not_store_if_reopened_checkbox = QCheckBox(_("Not if finished in library"), self)
        self.do_not_store_if_reopened_checkbox.setToolTip(_("Do not store the reading position if the library has the book as finished. This is if the percent read is 100%."))
        self.do_not_store_if_reopened_checkbox.setCheckState(Qt.Checked if do_not_store_if_reopened else Qt.Unchecked)
        self.do_not_store_if_reopened_checkbox.setEnabled(store_on_connect)
        options_layout.addWidget(self.do_not_store_if_reopened_checkbox, 1, 2, 1, 1)

#         update_options_group = QGroupBox(_('Firmware Update Options'), self)
#         layout.addWidget(update_options_group)
#         options_layout = QGridLayout()
#         update_options_group.setLayout(options_layout)
# 
#         self.do_update_check = QCheckBox(_('Check for Sony firmware updates daily?'), self)
#         self.do_update_check.setToolTip(_('If this is selected the plugin will check for Sony firmware updates when your Sony device is plugged in, once per 24-hour period.'))
#         self.do_update_check.setCheckState(Qt.Checked if do_check_for_firmware_updates else Qt.Unchecked)
#         options_layout.addWidget(self.do_update_check, 0, 0, 1, 1)
# 
#         self.do_early_firmware_check = QCheckBox(_('Use early firmware adopter affiliate?'), self)
#         self.do_early_firmware_check.setToolTip(_('WARNING: THIS OPTION RISKS DOWNLOADING THE WRONG FIRMWARE FOR YOUR DEVICE! YOUR DEVICE MAY NOT FUNCTION PROPERLY IF THIS HAPPENS! Choose this option to attempt to download Sony firmware updates before they are officially available for your device.'))
#         self.do_early_firmware_check.setCheckState(Qt.Checked if do_early_firmware_updates else Qt.Unchecked)
#         options_layout.addWidget(self.do_early_firmware_check, 0, 1, 1, 1)

        backup_options_group = QGroupBox(_('Device Database Backup'), self)
        layout.addWidget(backup_options_group)
        options_layout = QGridLayout()
        backup_options_group.setLayout(options_layout)

        self.do_daily_backp_checkbox = QCheckBox(_('Backup the device database daily'), self)
        self.do_daily_backp_checkbox.setToolTip(_('If this is selected the plugin will backup the device database the first time it is connected each day.'))
        self.do_daily_backp_checkbox.setCheckState(Qt.Checked if do_daily_backup else Qt.Unchecked)
        self.do_daily_backp_checkbox.clicked.connect(self.do_daily_backp_checkbox_clicked)
        options_layout.addWidget(self.do_daily_backp_checkbox, 0, 0, 1, 3)

        self.dest_directory_label = QLabel(_("Destination:"), self)
        self.dest_directory_label.setToolTip(_("Select the destination the annotations files are to be backed up in."))
        self.dest_directory_edit = QLineEdit(self)
        self.dest_directory_edit.setMinimumSize(150, 0)
        self.dest_directory_edit.setText(dest_directory)
        self.dest_directory_label.setBuddy(self.dest_directory_edit)
        self.dest_pick_button = QPushButton(_("..."), self)
        self.dest_pick_button.setMaximumSize(24, 20)
        self.dest_pick_button.clicked.connect(self._get_dest_directory_name)
        options_layout.addWidget(self.dest_directory_label, 1, 0, 1, 1)
        options_layout.addWidget(self.dest_directory_edit, 1, 1, 1, 1)
        options_layout.addWidget(self.dest_pick_button, 1, 2, 1, 1)

        self.copies_to_keep_checkbox = QCheckBox(_('Copies to keep'), self)
        self.copies_to_keep_checkbox.setToolTip(_("Select this to limit the number of backup kept. If not set, the backup files must be manually deleted."))
        self.copies_to_keep_spin = QSpinBox(self)
        self.copies_to_keep_spin.setMinimum(2)
        self.copies_to_keep_spin.setToolTip(_("The number of backup copies of the database to keep. The minimum is 2."))
        options_layout.addWidget(self.copies_to_keep_checkbox, 1, 3, 1, 1)
        options_layout.addWidget(self.copies_to_keep_spin, 1, 4, 1, 1)
        self.copies_to_keep_checkbox.clicked.connect(self.copies_to_keep_checkbox_clicked)
        if copies_to_keep == -1:
            self.copies_to_keep_checkbox.setCheckState(not Qt.Checked)
        else:
            self.copies_to_keep_checkbox.setCheckState(Qt.Checked)
            self.copies_to_keep_spin.setProperty('value', copies_to_keep)

        self.do_daily_backp_checkbox_clicked(do_daily_backup)

        other_options_group = QGroupBox(_('Other Options'), self)
        layout.addWidget(other_options_group )
        options_layout = QGridLayout()
        other_options_group.setLayout(options_layout)

        library_default_label = QLabel(_('&Library Button default:'), self)
        library_default_label.setToolTip(_('If plugin is placed as a toolbar button, choose a default action when clicked on'))
        self.library_default_combo = KeyComboBox(self, self.plugin_action.library_actions_map, unicode(get_plugin_pref(COMMON_OPTIONS_STORE_NAME, KEY_BUTTON_ACTION_LIBRARY)))
        library_default_label.setBuddy(self.library_default_combo)
        options_layout.addWidget(library_default_label, 0, 0, 1, 1)
        options_layout.addWidget(self.library_default_combo, 0, 1, 1, 2)

        device_default_label = QLabel(_('&Device Button default:'), self)
        device_default_label.setToolTip(_('If plugin is placed as a toolbar button, choose a default action when clicked on'))
        self.device_default_combo = KeyComboBox(self, self.plugin_action.device_actions_map, unicode(get_plugin_pref(COMMON_OPTIONS_STORE_NAME, KEY_BUTTON_ACTION_DEVICE)))
        device_default_label.setBuddy(self.device_default_combo)
        options_layout.addWidget(device_default_label, 1, 0, 1, 1)
        options_layout.addWidget(self.device_default_combo, 1, 1, 1, 2)

        keyboard_shortcuts_button = QPushButton(_('Keyboard shortcuts...'), self)
        keyboard_shortcuts_button.setToolTip(_('Edit the keyboard shortcuts associated with this plugin'))
        keyboard_shortcuts_button.clicked.connect(self.edit_shortcuts)
        layout.addWidget(keyboard_shortcuts_button)
        layout.addStretch(1)

    def store_on_connect_checkbox_clicked(self, checked):
        self.prompt_to_store_checkbox.setEnabled(checked)
        self.store_if_more_recent_checkbox.setEnabled(checked)
        self.do_not_store_if_reopened_checkbox.setEnabled(checked)

    def do_daily_backp_checkbox_clicked(self, checked):
        self.dest_directory_edit.setEnabled(checked)
        self.dest_pick_button.setEnabled(checked)
        self.dest_directory_label.setEnabled(checked)
        self.copies_to_keep_checkbox.setEnabled(checked)
        self.copies_to_keep_checkbox_clicked(checked and self.copies_to_keep_checkbox.checkState() == Qt.Checked)

    def copies_to_keep_checkbox_clicked(self, checked):
        self.copies_to_keep_spin.setEnabled(checked)

    # Called by Calibre before save_settings 
    def validate(self):
#        import traceback
#        traceback.print_stack()
        
        debug_print('BEGIN Validate')
        valid = True
        # Only save if we were able to get data to avoid corrupting stored data
#        if self.do_daily_backp_checkbox.checkState() == Qt.Checked and not len(self.dest_directory_edit.text()):
#            error_dialog(self, 'No destination directory',
#                            'If the automatic device backup is set, there must be a destination directory.',
#                            show=True, show_copy_button=False)
#            valid = False

        debug_print('END Validate, status = %s' % valid)
        return valid

    def save_settings(self):

        new_prefs = {}
        new_prefs[KEY_BUTTON_ACTION_DEVICE]     = unicode(self.device_default_combo.currentText())
        new_prefs[KEY_BUTTON_ACTION_LIBRARY]    = unicode(self.library_default_combo.currentText())
        new_prefs[KEY_STORE_ON_CONNECT]         = self.store_on_connect_checkbox.checkState() == Qt.Checked
        new_prefs[KEY_PROMPT_TO_STORE]          = self.prompt_to_store_checkbox.checkState() == Qt.Checked
        new_prefs[KEY_STORE_IF_MORE_RECENT]     = self.store_if_more_recent_checkbox.checkState() == Qt.Checked
        new_prefs[KEY_DO_NOT_STORE_IF_REOPENED] = self.do_not_store_if_reopened_checkbox.checkState() == Qt.Checked
        plugin_prefs[COMMON_OPTIONS_STORE_NAME] = new_prefs

        new_update_prefs = {}
#         new_update_prefs[KEY_DO_UPDATE_CHECK]          = self.do_update_check.checkState() == Qt.Checked
#         new_update_prefs[KEY_DO_EARLY_FIRMWARE_CHECK]  = self.do_early_firmware_check.checkState() == Qt.Checked
#         new_update_prefs[KEY_LAST_FIRMWARE_CHECK_TIME] = self.update_check_last_time
        plugin_prefs[UPDATE_OPTIONS_STORE_NAME]        = new_update_prefs

        backup_prefs = {}
        backup_prefs[KEY_DO_DAILY_BACKUP]       = self.do_daily_backp_checkbox.checkState() == Qt.Checked
        backup_prefs[KEY_BACKUP_DEST_DIRECTORY] = unicode(self.dest_directory_edit.text())
        backup_prefs[KEY_BACKUP_COPIES_TO_KEEP] = int(unicode(self.copies_to_keep_spin.value())) if self.copies_to_keep_checkbox.checkState() == Qt.Checked else -1 
        plugin_prefs[BACKUP_OPTIONS_STORE_NAME] = backup_prefs

        db = self.plugin_action.gui.current_db
        library_config = get_library_config(db)
        library_config[KEY_CURRENT_LOCATION_CUSTOM_COLUMN] = self.current_Location_combo.get_selected_column()
        library_config[KEY_PERCENT_READ_CUSTOM_COLUMN]     = self.percent_read_combo.get_selected_column()
        library_config[KEY_RATING_CUSTOM_COLUMN]           = self.rating_combo.get_selected_column()
        library_config[KEY_LAST_READ_CUSTOM_COLUMN]        = self.last_read_combo.get_selected_column()
        set_library_config(db, library_config)

    def get_number_custom_columns(self):
        column_types = ['float','int']
        return self.get_custom_columns(column_types)

    def get_rating_custom_columns(self):
        column_types = ['rating','int']
        custom_columns = self.get_custom_columns(column_types)
        ratings_column_name = self.plugin_action.gui.library_view.model().orig_headers['rating']
        custom_columns['rating'] = {'name': ratings_column_name}
        return custom_columns

    def get_text_custom_columns(self):
        column_types = ['text']
        return self.get_custom_columns(column_types)

    def get_date_custom_columns(self):
        column_types = ['datetime']
        return self.get_custom_columns(column_types)

    def get_custom_columns(self, column_types):
        custom_columns = self.plugin_action.gui.library_view.model().custom_columns
        available_columns = {}
        for key, column in custom_columns.iteritems():
            typ = column['datatype']
            if typ in column_types and not column['is_multiple']:
                available_columns[key] = column
        return available_columns

    def help_link_activated(self, url):
        self.plugin_action.show_help(anchor="configuration")

    def edit_shortcuts(self):
        d = KeyboardConfigDialog(self.plugin_action.gui, self.plugin_action.action_spec[0])
        if d.exec_() == d.Accepted:
            self.plugin_action.gui.keyboard.finalize()

    def _get_dest_directory_name(self):
        path = choose_dir(self, 'backup annotations destination dialog','Choose destination directory')
        if path:
            self.dest_directory_edit.setText(path)
Beispiel #2
0
class BulkSeries(BulkBase):

    def setup_ui(self, parent):
        self.make_widgets(parent, EditWithComplete)
        values = self.all_values = list(self.db.all_custom(num=self.col_id))
        values.sort(key=sort_key)
        self.main_widget.setSizeAdjustPolicy(self.main_widget.AdjustToMinimumContentsLengthWithIcon)
        self.main_widget.setMinimumContentsLength(25)
        self.widgets.append(QLabel('', parent))
        w = QWidget(parent)
        layout = QHBoxLayout(w)
        layout.setContentsMargins(0, 0, 0, 0)
        self.remove_series = QCheckBox(parent)
        self.remove_series.setText(_('Clear series'))
        layout.addWidget(self.remove_series)
        self.idx_widget = QCheckBox(parent)
        self.idx_widget.setText(_('Automatically number books'))
        self.idx_widget.setToolTip('<p>' + _(
            'If not checked, the series number for the books will be set to 1. '
            'If checked, selected books will be automatically numbered, '
            'in the order you selected them. So if you selected '
            'Book A and then Book B, Book A will have series number 1 '
            'and Book B series number 2.') + '</p>')
        layout.addWidget(self.idx_widget)
        self.force_number = QCheckBox(parent)
        self.force_number.setText(_('Force numbers to start with '))
        self.force_number.setToolTip('<p>' + _(
            'Series will normally be renumbered from the highest '
            'number in the database for that series. Checking this '
            'box will tell calibre to start numbering from the value '
            'in the box') + '</p>')
        layout.addWidget(self.force_number)
        self.series_start_number = QDoubleSpinBox(parent)
        self.series_start_number.setMinimum(0.0)
        self.series_start_number.setMaximum(9999999.0)
        self.series_start_number.setProperty("value", 1.0)
        layout.addWidget(self.series_start_number)
        self.series_increment = QDoubleSpinBox(parent)
        self.series_increment.setMinimum(0.00)
        self.series_increment.setMaximum(99999.0)
        self.series_increment.setProperty("value", 1.0)
        self.series_increment.setToolTip('<p>' + _(
            'The amount by which to increment the series number '
            'for successive books. Only applicable when using '
            'force series numbers.') + '</p>')
        self.series_increment.setPrefix('+')
        layout.addWidget(self.series_increment)
        layout.addItem(QSpacerItem(20, 10, QSizePolicy.Expanding, QSizePolicy.Minimum))
        self.widgets.append(w)
        self.idx_widget.stateChanged.connect(self.a_c_checkbox_changed)
        self.force_number.stateChanged.connect(self.a_c_checkbox_changed)
        self.series_start_number.valueChanged.connect(self.a_c_checkbox_changed)
        self.series_increment.valueChanged.connect(self.a_c_checkbox_changed)
        self.remove_series.stateChanged.connect(self.a_c_checkbox_changed)
        self.main_widget
        self.ignore_change_signals = False

    def a_c_checkbox_changed(self):
        def disable_numbering_checkboxes(idx_widget_enable):
            if idx_widget_enable:
                self.idx_widget.setEnabled(True)
            else:
                self.idx_widget.setChecked(False)
                self.idx_widget.setEnabled(False)
            self.force_number.setChecked(False)
            self.force_number.setEnabled(False)
            self.series_start_number.setEnabled(False)
            self.series_increment.setEnabled(False)

        if self.ignore_change_signals:
            return
        self.ignore_change_signals = True
        apply_changes = False
        if self.remove_series.isChecked():
            self.main_widget.setText('')
            self.main_widget.setEnabled(False)
            disable_numbering_checkboxes(idx_widget_enable=False)
            apply_changes = True
        elif self.main_widget.text():
            self.remove_series.setEnabled(False)
            self.idx_widget.setEnabled(True)
            apply_changes = True
        else:  # no text, no clear. Basically reinitialize
            self.main_widget.setEnabled(True)
            self.remove_series.setEnabled(True)
            disable_numbering_checkboxes(idx_widget_enable=False)
            apply_changes = False

        self.force_number.setEnabled(self.idx_widget.isChecked())
        self.series_start_number.setEnabled(self.force_number.isChecked())
        self.series_increment.setEnabled(self.force_number.isChecked())

        self.ignore_change_signals = False
        self.a_c_checkbox.setChecked(apply_changes)

    def initialize(self, book_id):
        self.idx_widget.setChecked(False)
        self.main_widget.set_separator(None)
        self.main_widget.update_items_cache(self.all_values)
        self.main_widget.setEditText('')
        self.a_c_checkbox.setChecked(False)

    def getter(self):
        n = unicode(self.main_widget.currentText()).strip()
        autonumber = self.idx_widget.checkState()
        force = self.force_number.checkState()
        start = self.series_start_number.value()
        remove = self.remove_series.checkState()
        increment = self.series_increment.value()
        return n, autonumber, force, start, remove, increment

    def commit(self, book_ids, notify=False):
        if not self.a_c_checkbox.isChecked():
            return
        val, update_indices, force_start, at_value, clear, increment = self.gui_val
        val = None if clear else self.normalize_ui_val(val)
        if clear or val != '':
            extras = []
            for book_id in book_ids:
                if clear:
                    extras.append(None)
                    continue
                if update_indices:
                    if force_start:
                        s_index = at_value
                        at_value += increment
                    elif tweaks['series_index_auto_increment'] != 'const':
                        s_index = self.db.get_next_cc_series_num_for(val, num=self.col_id)
                    else:
                        s_index = 1.0
                else:
                    s_index = self.db.get_custom_extra(book_id, num=self.col_id,
                                                       index_is_id=True)
                extras.append(s_index)
            self.db.set_custom_bulk(book_ids, val, extras=extras,
                                   num=self.col_id, notify=notify)
Beispiel #3
0
class ConversionDialog(Dialog):
    def __init__(self, parent, force_entire_book=False):
        self.prefs = self.prefsPrep()
        self.parent = parent
        self.force_entire_book = force_entire_book
        self.criteria = None
        Dialog.__init__(self, _('Chinese Conversion'),
                        'chinese_conversion_dialog', parent)

    def setup_ui(self):
        self.quote_for_trad_target = _("Update quotes: "",'' -> 「」,『』")
        self.quote_for_simp_target = _("Update quotes: 「」,『』 -> "",''")

        # Create layout for entire dialog
        layout = QVBoxLayout(self)
        self.setLayout(layout)

        #Create a scroll area for the top part of the dialog
        self.scrollArea = QScrollArea(self)
        self.scrollArea.setWidgetResizable(True)

        # Create widget for all the contents of the dialog except the OK and Cancel buttons
        self.scrollContentWidget = QWidget(self.scrollArea)
        self.scrollArea.setWidget(self.scrollContentWidget)
        widgetLayout = QVBoxLayout(self.scrollContentWidget)

        # Add scrollArea to dialog
        layout.addWidget(self.scrollArea)

        self.operation_group_box = QGroupBox(_('Conversion Direction'))
        widgetLayout.addWidget(self.operation_group_box)
        operation_group_box_layout = QVBoxLayout()
        self.operation_group_box.setLayout(operation_group_box_layout)

        operation_group = QButtonGroup(self)
        self.no_conversion_button = QRadioButton(_('No Conversion'))
        operation_group.addButton(self.no_conversion_button)
        self.trad_to_simp_button = QRadioButton(_('Traditional to Simplified'))
        operation_group.addButton(self.trad_to_simp_button)
        self.simp_to_trad_button = QRadioButton(_('Simplified to Traditional'))
        operation_group.addButton(self.simp_to_trad_button)
        self.trad_to_trad_button = QRadioButton(
            _('Traditional to Traditional'))
        operation_group.addButton(self.trad_to_trad_button)
        operation_group_box_layout.addWidget(self.no_conversion_button)
        operation_group_box_layout.addWidget(self.trad_to_simp_button)
        operation_group_box_layout.addWidget(self.simp_to_trad_button)
        operation_group_box_layout.addWidget(self.trad_to_trad_button)
        self.no_conversion_button.toggled.connect(self.update_gui)
        self.trad_to_simp_button.toggled.connect(self.update_gui)
        self.simp_to_trad_button.toggled.connect(self.update_gui)
        self.trad_to_trad_button.toggled.connect(self.update_gui)

        self.style_group_box = QGroupBox(_('Language Styles'))
        widgetLayout.addWidget(self.style_group_box)
        style_group_box_layout = QVBoxLayout()
        self.style_group_box.setLayout(style_group_box_layout)

        input_layout = QHBoxLayout()
        style_group_box_layout.addLayout(input_layout)
        self.input_region_label = QLabel(_('Input:'))
        input_layout.addWidget(self.input_region_label)
        self.input_combo = QComboBox()
        input_layout.addWidget(self.input_combo)
        self.input_combo.addItems([_('Mainland'), _('Hong Kong'), _('Taiwan')])
        self.input_combo.setToolTip(_('Select the origin region of the input'))
        self.input_combo.currentIndexChanged.connect(self.update_gui)

        output_layout = QHBoxLayout()
        style_group_box_layout.addLayout(output_layout)
        self.output_region_label = QLabel(_('Output:'))
        output_layout.addWidget(self.output_region_label)
        self.output_combo = QComboBox()
        output_layout.addWidget(self.output_combo)
        self.output_combo.addItems(
            [_('Mainland'), _('Hong Kong'),
             _('Taiwan')])
        self.output_combo.setToolTip(
            _('Select the desired region of the output'))
        self.output_combo.currentIndexChanged.connect(self.update_gui)

        self.use_target_phrases = QCheckBox(
            _('Use output target phrases if possible'))
        self.use_target_phrases.setToolTip(
            _('Check to allow region specific word replacements if available'))
        style_group_box_layout.addWidget(self.use_target_phrases)
        self.use_target_phrases.stateChanged.connect(self.update_gui)

        self.quotation_group_box = QGroupBox(_('Quotation Marks'))
        widgetLayout.addWidget(self.quotation_group_box)
        quotation_group_box_layout = QVBoxLayout()
        self.quotation_group_box.setLayout(quotation_group_box_layout)

        quotation_group = QButtonGroup(self)
        self.quotation_no_conversion_button = QRadioButton(_('No Conversion'))
        quotation_group.addButton(self.quotation_no_conversion_button)
        self.quotation_trad_to_simp_button = QRadioButton(
            self.quote_for_simp_target)
        quotation_group.addButton(self.quotation_trad_to_simp_button)
        self.quotation_simp_to_trad_button = QRadioButton(
            self.quote_for_trad_target)
        quotation_group.addButton(self.quotation_simp_to_trad_button)
        quotation_group_box_layout.addWidget(
            self.quotation_no_conversion_button)
        quotation_group_box_layout.addWidget(
            self.quotation_simp_to_trad_button)
        quotation_group_box_layout.addWidget(
            self.quotation_trad_to_simp_button)
        self.quotation_no_conversion_button.toggled.connect(self.update_gui)
        self.quotation_trad_to_simp_button.toggled.connect(self.update_gui)
        self.quotation_simp_to_trad_button.toggled.connect(self.update_gui)
        self.use_smart_quotes = QCheckBox(
            """Use curved 'Smart" quotes if applicable""")
        self.use_smart_quotes.setToolTip(
            _('Use smart curved half-width quotes rather than straight full-width quotes'
              ))
        quotation_group_box_layout.addWidget(self.use_smart_quotes)
        self.use_smart_quotes.stateChanged.connect(self.update_gui)

        self.other_group_box = QGroupBox(_('Other Changes'))
        widgetLayout.addWidget(self.other_group_box)
        other_group_box_layout = QVBoxLayout()
        self.other_group_box.setLayout(other_group_box_layout)

        text_dir_layout = QHBoxLayout()
        other_group_box_layout.addLayout(text_dir_layout)
        direction_label = QLabel(_('Text Direction:'))
        text_dir_layout.addWidget(direction_label)
        self.text_dir_combo = QComboBox()
        text_dir_layout.addWidget(self.text_dir_combo)
        self.text_dir_combo.addItems(
            [_('No Conversion'),
             _('Horizontal'),
             _('Vertical')])
        self.text_dir_combo.setToolTip(
            _('Select the desired text orientation'))
        self.text_dir_combo.currentIndexChanged.connect(self.update_gui)

        self.optimization_group_box = QGroupBox(
            _('Reader Device Optimization'))
        other_group_box_layout.addWidget(self.optimization_group_box)
        optimization_group_box_layout = QVBoxLayout()
        self.optimization_group_box.setLayout(optimization_group_box_layout)

        punc_group = QButtonGroup(self)
        self.text_dir_punc_none_button = QRadioButton(
            """No presentation optimization""")
        optimization_group_box_layout.addWidget(self.text_dir_punc_none_button)
        self.text_dir_punc_button = QRadioButton(
            """Optimize presentation for Readium reader""")
        self.text_dir_punc_button.setToolTip(
            _('Use vert/horiz punctuation presentation forms for Chrome Readium Epub3 reader'
              ))
        optimization_group_box_layout.addWidget(self.text_dir_punc_button)
        self.text_dir_punc_kindle_button = QRadioButton(
            """Optimize presentation for Kindle reader""")
        self.text_dir_punc_kindle_button.setToolTip(
            _('Use vert/horiz puncuation presentation forms for Kindle reader')
        )
        optimization_group_box_layout.addWidget(
            self.text_dir_punc_kindle_button)
        self.text_dir_punc_none_button.toggled.connect(self.update_gui)
        self.text_dir_punc_button.toggled.connect(self.update_gui)
        self.text_dir_punc_kindle_button.toggled.connect(self.update_gui)

        source_group = QButtonGroup(self)
        self.file_source_button = QRadioButton(_('Selected File Only'))
        self.book_source_button = QRadioButton(_('Entire eBook'))
        source_group.addButton(self.file_source_button)
        source_group.addButton(self.book_source_button)
        self.source_group_box = QGroupBox(_('Source'))
        if not self.force_entire_book:
            widgetLayout.addWidget(self.source_group_box)
            source_group_box_layout = QVBoxLayout()
            self.source_group_box.setLayout(source_group_box_layout)
            source_group_box_layout.addWidget(self.file_source_button)
            source_group_box_layout.addWidget(self.book_source_button)

        layout.addSpacing(10)
        self.button_box = QDialogButtonBox(QDialogButtonBox.Ok
                                           | QDialogButtonBox.Cancel)

        self.button_box.accepted.connect(self._ok_clicked)
        self.button_box.rejected.connect(self.reject)
        layout.addWidget(self.button_box)

        self.input_combo.setCurrentIndex(self.prefs['input_format'])
        self.output_combo.setCurrentIndex(self.prefs['output_format'])
        self.no_conversion_button.setChecked(self.prefs['no_conversion'])
        self.trad_to_simp_button.setChecked(self.prefs['trad_to_simp'])
        self.simp_to_trad_button.setChecked(self.prefs['simp_to_trad'])
        self.trad_to_trad_button.setChecked(self.prefs['trad_to_trad'])
        if not self.force_entire_book:
            self.file_source_button.setChecked(self.prefs['use_html_file'])
            self.book_source_button.setChecked(self.prefs['use_entire_book'])
        else:
            self.file_source_button.setChecked(False)
            self.book_source_button.setChecked(True)

        self.quotation_no_conversion_button.setChecked(
            self.prefs['quote_no_conversion'])
        self.quotation_trad_to_simp_button.setChecked(
            self.prefs['quote_trad_to_simp'])
        self.quotation_simp_to_trad_button.setChecked(
            self.prefs['quote_simp_to_trad'])

        self.use_smart_quotes.setChecked(self.prefs['use_smart_quotes'])
        self.text_dir_combo.setCurrentIndex(self.prefs['orientation'])
        self.text_dir_punc_none_button.setChecked(
            self.prefs['no_optimization'])
        self.text_dir_punc_button.setChecked(
            self.prefs['readium_optimization'])
        self.text_dir_punc_kindle_button.setChecked(
            self.prefs['kindle_optimization'])
        self.update_gui()

    def update_gui(self):
        if (self.quotation_trad_to_simp_button.isChecked()):
            self.use_smart_quotes.setEnabled(True)
        else:
            self.use_smart_quotes.setEnabled(False)

        if self.text_dir_combo.currentIndex() == 0:
            self.optimization_group_box.setEnabled(False)
            self.text_dir_punc_none_button.setEnabled(False)
            self.text_dir_punc_button.setEnabled(False)
            self.text_dir_punc_kindle_button.setEnabled(False)
        else:
            self.optimization_group_box.setEnabled(True)
            self.text_dir_punc_none_button.setEnabled(True)
            self.text_dir_punc_button.setEnabled(True)
            self.text_dir_punc_kindle_button.setEnabled(True)

        if self.no_conversion_button.isChecked():
            self.input_combo.setEnabled(False)
            self.output_combo.setEnabled(False)
            self.use_target_phrases.setEnabled(False)
            self.output_region_label.setEnabled(False)
            self.input_region_label.setEnabled(False)
            self.style_group_box.setEnabled(False)

        elif self.trad_to_simp_button.isChecked():
            self.input_combo.setEnabled(True)
            #only mainland output locale for simplified output
            self.output_combo.setCurrentIndex(0)
            self.output_combo.setEnabled(False)
            self.use_target_phrases.setEnabled(True)
            self.output_region_label.setEnabled(False)
            self.input_region_label.setEnabled(True)
            self.style_group_box.setEnabled(True)

        elif self.simp_to_trad_button.isChecked():
            #only mainland input locale for simplified input
            self.input_combo.setCurrentIndex(0)
            self.input_combo.setEnabled(False)
            self.output_combo.setEnabled(True)
            self.use_target_phrases.setEnabled(True)
            self.output_region_label.setEnabled(True)
            self.input_region_label.setEnabled(False)
            self.style_group_box.setEnabled(True)

        elif self.trad_to_trad_button.isChecked():
            #Trad->Trad
            #currently only mainland input locale for Trad->Trad
            self.input_combo.setCurrentIndex(0)
            self.input_combo.setEnabled(False)
            self.output_combo.setEnabled(True)
            self.use_target_phrases.setEnabled(True)
            self.output_region_label.setEnabled(True)
            self.input_region_label.setEnabled(False)
            self.style_group_box.setEnabled(True)

        else:
            self.input_combo.setEnabled(True)
            self.output_combo.setEnabled(True)
            self.use_target_phrases.setEnabled(True)
            self.style_group_box.setEnabled(True)
            self.output_region_label.setEnabled(True)
            self.input_region_label.setEnabled(True)

    def _ok_clicked(self):
        output_mode = 0
        if self.trad_to_simp_button.isChecked():
            output_mode = 1  #trad -> simp
        if self.simp_to_trad_button.isChecked():
            output_mode = 2  #simp -> trad
        elif self.trad_to_trad_button.isChecked():
            output_mode = 3  #trad -> trad

        quote_mode = 0
        if self.quotation_trad_to_simp_button.isChecked():
            quote_mode = 1  #trad -> simp
        if self.quotation_simp_to_trad_button.isChecked():
            quote_mode = 2  #simp -> trad

        optimization_mode = 0
        if self.text_dir_punc_button.isChecked():
            optimization_mode = 1  #Readium
        if self.text_dir_punc_kindle_button.isChecked():
            optimization_mode = 2  #Kindle

        self.criteria = (self.file_source_button.isChecked(), output_mode,
                         self.input_combo.currentIndex(),
                         self.output_combo.currentIndex(),
                         self.use_target_phrases.isChecked(), quote_mode,
                         self.use_smart_quotes.isChecked(),
                         self.text_dir_combo.currentIndex(), optimization_mode)
        self.savePrefs()
        self.accept()

    def getCriteria(self):
        return self.criteria

    def prefsPrep(self):
        from calibre.utils.config import JSONConfig
        plugin_prefs = JSONConfig(
            'plugins/{0}_ChineseConversion_settings'.format(PLUGIN_SAFE_NAME))
        plugin_prefs.defaults['input_format'] = 0
        plugin_prefs.defaults['output_format'] = 0
        plugin_prefs.defaults['no_conversion'] = True
        plugin_prefs.defaults['trad_to_simp'] = False
        plugin_prefs.defaults['use_html_file'] = True
        plugin_prefs.defaults['simp_to_trad'] = False
        plugin_prefs.defaults['trad_to_trad'] = False
        plugin_prefs.defaults['use_entire_book'] = True
        plugin_prefs.defaults['use_target_phrases'] = True
        plugin_prefs.defaults['quote_no_conversion'] = True
        plugin_prefs.defaults['quote_trad_to_simp'] = False
        plugin_prefs.defaults['quote_simp_to_trad'] = False
        plugin_prefs.defaults['use_smart_quotes'] = False
        plugin_prefs.defaults['orientation'] = 0
        plugin_prefs.defaults['no_optimization'] = True
        plugin_prefs.defaults['readium_optimization'] = False
        plugin_prefs.defaults['kindle_optimization'] = False
        return plugin_prefs

    def savePrefs(self):
        self.prefs['input_format'] = self.input_combo.currentIndex()
        self.prefs['output_format'] = self.output_combo.currentIndex()
        self.prefs['no_conversion'] = self.no_conversion_button.isChecked()
        self.prefs['trad_to_simp'] = self.trad_to_simp_button.isChecked()
        self.prefs['use_html_file'] = self.file_source_button.isChecked()
        self.prefs['simp_to_trad'] = self.simp_to_trad_button.isChecked()
        self.prefs['trad_to_trad'] = self.trad_to_trad_button.isChecked()
        self.prefs['use_entire_book'] = self.book_source_button.isChecked()
        self.prefs['use_target_phrases'] = self.use_target_phrases.isChecked()
        self.prefs[
            'quote_no_conversion'] = self.quotation_no_conversion_button.isChecked(
            )
        self.prefs[
            'quote_trad_to_simp'] = self.quotation_trad_to_simp_button.isChecked(
            )
        self.prefs[
            'quote_simp_to_trad'] = self.quotation_simp_to_trad_button.isChecked(
            )
        self.prefs['use_smart_quotes'] = self.use_smart_quotes.isChecked()
        self.prefs['orientation'] = self.text_dir_combo.currentIndex()
        self.prefs[
            'no_optimization'] = self.text_dir_punc_none_button.isChecked()
        self.prefs[
            'readium_optimization'] = self.text_dir_punc_button.isChecked()
        self.prefs[
            'kindle_optimization'] = self.text_dir_punc_kindle_button.isChecked(
            )
class BasicTab(QWidget):
    def __init__(self, parent_dialog, plugin_action):
        QWidget.__init__(self)
        self.parent_dialog = parent_dialog
        self.plugin_action = plugin_action

        self.l = QVBoxLayout()
        self.setLayout(self.l)

        label = QLabel(_('When Summing Columns, Calculate:'))
        label.setWordWrap(True)
        self.l.addWidget(label)
        #self.l.addSpacing(5)

        scrollable = QScrollArea()
        scrollcontent = QWidget()
        scrollable.setWidget(scrollcontent)
        scrollable.setWidgetResizable(True)
        self.l.addWidget(scrollable)

        self.sl = QVBoxLayout()
        scrollcontent.setLayout(self.sl)

        self.showsums = QCheckBox(_('Sum'), self)
        self.showsums.setToolTip(
            _('Sum of numeric columns for selected books.'))
        self.showsums.setChecked(prefs['showsums'])
        self.sl.addWidget(self.showsums)
        if 'Reading List' not in plugin_action.gui.iactions:
            self.showsums.setEnabled(False)

        self.showaverages = QCheckBox(_('Average'), self)
        self.showaverages.setToolTip(
            _('Average of numeric columns for selected books.'))
        self.showaverages.setChecked(prefs['showaverages'])
        self.sl.addWidget(self.showaverages)

        self.showstds = QCheckBox(_('Standard Deviation'), self)
        self.showstds.setToolTip(
            _('Standard Deviation of numeric columns for selected books.'))
        self.showstds.setChecked(prefs['showstds'])
        self.sl.addWidget(self.showstds)

        self.sl.insertStretch(-1)

        self.l.addSpacing(15)

        label = QLabel(
            _("These controls aren't plugin settings as such, but convenience buttons for setting Keyboard shortcuts and viewing all plugins settings."
              ))
        label.setWordWrap(True)
        self.l.addWidget(label)
        self.l.addSpacing(5)

        keyboard_shortcuts_button = QPushButton(_('Keyboard shortcuts...'),
                                                self)
        keyboard_shortcuts_button.setToolTip(
            _('Edit the keyboard shortcuts associated with this plugin'))
        keyboard_shortcuts_button.clicked.connect(parent_dialog.edit_shortcuts)
        self.l.addWidget(keyboard_shortcuts_button)

        view_prefs_button = QPushButton(_('&View library preferences...'),
                                        self)
        view_prefs_button.setToolTip(
            _('View data stored in the library database for this plugin'))
        view_prefs_button.clicked.connect(self.view_prefs)
        self.l.addWidget(view_prefs_button)

    def view_prefs(self):
        d = PrefsViewerDialog(self.plugin_action.gui, PREFS_NAMESPACE)
        d.exec_()

    def reset_dialogs(self):
        for key in dynamic.keys():
            if key.startswith('columnsum_') and key.endswith('_again') \
                                                  and dynamic[key] is False:
                dynamic[key] = True
        info_dialog(self,
                    _('Done'),
                    _('Confirmation dialogs have all been reset'),
                    show=True,
                    show_copy_button=False)
Beispiel #5
0
class BasicTab(QWidget):

    def __init__(self, parent_dialog, plugin_action):
        QWidget.__init__(self)
        self.parent_dialog = parent_dialog
        self.plugin_action = plugin_action
        
        self.l = QVBoxLayout()
        self.setLayout(self.l)

        label = QLabel(_('When Ejecting a Device, Check for:'))
        label.setWordWrap(True)
        self.l.addWidget(label)
        #self.l.addSpacing(5)
        
        scrollable = QScrollArea()
        scrollcontent = QWidget()
        scrollable.setWidget(scrollcontent)
        scrollable.setWidgetResizable(True)
        self.l.addWidget(scrollable)

        self.sl = QVBoxLayout()
        scrollcontent.setLayout(self.sl)
        
        self.checkreadinglistsync = QCheckBox(_('Reading List books to Sync'),self)
        self.checkreadinglistsync.setToolTip(_('Check Reading List plugin for books ready to Sync to the current device.'))
        self.checkreadinglistsync.setChecked(prefs['checkreadinglistsync'])
        self.sl.addWidget(self.checkreadinglistsync)
        if 'Reading List' not in plugin_action.gui.iactions:
            self.checkreadinglistsync.setEnabled(False)
        
        self.checkdups = QCheckBox(_('Duplicated Books'),self)
        self.checkdups.setToolTip(_('Check for books that are on the device more than once.'))
        self.checkdups.setChecked(prefs['checkdups'])
        self.sl.addWidget(self.checkdups)
        
        self.checknotinlibrary = QCheckBox(_('Deleted Books (not in Library)'),self)
        self.checknotinlibrary.setToolTip(_('Check for books on the device that are not in the current library.'))
        self.checknotinlibrary.setChecked(prefs['checknotinlibrary'])
        self.sl.addWidget(self.checknotinlibrary)
        
        self.checknotondevice = QCheckBox(_('Added Books (not on Device)'),self)
        self.checknotondevice.setToolTip(_('Check for books in the current library that are not on the device.'))
        self.checknotondevice.setChecked(prefs['checknotondevice'])
        self.sl.addWidget(self.checknotondevice)
                
        self.sl.insertStretch(-1)
        
        self.l.addSpacing(15)        

        label = QLabel(_("These controls aren't plugin settings as such, but convenience buttons for setting Keyboard shortcuts and viewing all plugins settings."))
        label.setWordWrap(True)
        self.l.addWidget(label)
        self.l.addSpacing(5)
        
        keyboard_shortcuts_button = QPushButton(_('Keyboard shortcuts...'), self)
        keyboard_shortcuts_button.setToolTip(_('Edit the keyboard shortcuts associated with this plugin'))
        keyboard_shortcuts_button.clicked.connect(parent_dialog.edit_shortcuts)
        self.l.addWidget(keyboard_shortcuts_button)
                
        view_prefs_button = QPushButton(_('&View library preferences...'), self)
        view_prefs_button.setToolTip(_('View data stored in the library database for this plugin'))
        view_prefs_button.clicked.connect(self.view_prefs)
        self.l.addWidget(view_prefs_button)
        
    def view_prefs(self):
        d = PrefsViewerDialog(self.plugin_action.gui, PREFS_NAMESPACE)
        d.exec_()
        
    def reset_dialogs(self):
        for key in dynamic.keys():
            if key.startswith('smarteject_') and key.endswith('_again') \
                                                  and dynamic[key] is False:
                dynamic[key] = True
        info_dialog(self, _('Done'),
                    _('Confirmation dialogs have all been reset'),
                    show=True,
                    show_copy_button=False)
Beispiel #6
0
class PaintPanel(QWidget):
    def __init__(self, signals, Parent=None):
        '''
        Constructor
        '''
        super().__init__(Parent)

        self.InitData(signals)  # 先初始化数据,再初始化界面
        self.InitView()

    def InitData(self, signals):
        '''
                  初始化成员变量
        '''
        self.PaintBoard = PaintBoard(signals, Parent=self)
        # 获取颜色列表(字符串类型)
        self.colorList = QColor.colorNames()

    def InitView(self):
        '''
                  初始化界面
        '''
        self.setFixedSize(*panel_resolution)
        self.setWindowTitle("PaintBoard Example PyQt5")

        # 新建一个水平布局作为本窗体的主布局
        main_layout = QVBoxLayout(self)
        # 设置主布局内边距以及控件间距为10px
        main_layout.setSpacing(10)

        # 新建垂直子布局用于放置按键
        sub_layout = QHBoxLayout()
        sub_layout.setContentsMargins(100, 0, 100, 0)

        # 设置此子布局和内部控件的间距为10px
        # sub_layout.setContentsMargins(10, 10, 10, 10)

        # ---------------------通知栏和时钟--------------------------
        self.InformLayout = QHBoxLayout()
        self.InformWidget = QLabel('')
        self.InformWidget.setStyleSheet(
            get_qlabel_font_stylesheet(color=INFORM_MSG_COLOR,
                                       size=25))  # 设置通告栏的字体格式
        self.InformLayout.addWidget(self.InformWidget)
        self.InformClock = QLCDNumber(self)
        self.InformClock.setDigitCount(3)
        self.InformClock.setMode(QLCDNumber.Dec)
        self.InformClock.setSegmentStyle(QLCDNumber.Flat)
        self.InformLayout.addWidget(self.InformClock)
        main_layout.addLayout(self.InformLayout)
        # ---------------------------------------------------

        # 在主界面左上侧放置画板
        main_layout.addWidget(self.PaintBoard)

        # ---------------------橡皮擦--------------------------
        self.EraserCheckbox = QCheckBox("使用橡皮擦")
        self.EraserCheckbox.setParent(self)
        sub_layout.addWidget(self.EraserCheckbox)
        # ---------------------------------------------------

        # splitter = QSplitter(self)  # 占位符
        # sub_layout.addWidget(splitter)

        # ------------------------笔画粗细-----------------------
        self.pen_thick_child_panel = QHBoxLayout()
        self.label_penThickness = QLabel(self)
        self.label_penThickness.setText("画笔粗细")
        self.label_penThickness.setAlignment(core.Qt.AlignRight
                                             | core.Qt.AlignVCenter)
        self.pen_thick_child_panel.addWidget(self.label_penThickness)
        self.spinBox_penThickness = QSpinBox(self)
        self.spinBox_penThickness.setMaximum(20)
        self.spinBox_penThickness.setMinimum(2)
        self.spinBox_penThickness.setValue(
            config.paint.DefaultThickness)  # 默认粗细为10
        self.spinBox_penThickness.setSingleStep(2)  # 最小变化值为2
        self.pen_thick_child_panel.addWidget(self.spinBox_penThickness)
        sub_layout.addLayout(self.pen_thick_child_panel)
        # ---------------------------------------------------

        # -----------------------笔划颜色-------------------------
        self.pen_color_child_panel = QHBoxLayout()
        self.label_penColor = QLabel(self)
        self.label_penColor.setAlignment(core.Qt.AlignRight
                                         | core.Qt.AlignVCenter)
        self.label_penColor.setText("画笔颜色")
        self.pen_color_child_panel.addWidget(self.label_penColor)
        self.comboBox_penColor = QComboBox(self)
        self.fill_color_list(self.comboBox_penColor, self.colorList,
                             default_color)  # 用各种颜色填充下拉列表
        # ---------------------------------------------------

        # -------------------------清空画板按钮----------------------
        self.pen_color_child_panel.addWidget(self.comboBox_penColor)
        sub_layout.addLayout(self.pen_color_child_panel)
        self.ctrl_child_panel = QHBoxLayout()
        self.btn_Clear = QPushButton("清空画板")
        self.btn_Clear.setParent(self)  # 设置父对象为本界面
        self.ctrl_child_panel.addWidget(self.btn_Clear)
        sub_layout.addLayout(self.ctrl_child_panel)
        # ---------------------------------------------------

        # -----------------------退出按钮-----------------------
        # self.btn_Quit = QPushButton("退出")
        # self.btn_Quit.setParent(self)  # 设置父对象为本界面
        # self.ctrl_child_panel.addWidget(self.btn_Quit)
        # ---------------------------------------------------

        self.InitLogic()

        main_layout.addLayout(sub_layout)  # 将子布局加入主布局

    def InitLogic(self):
        '''
            初始化所有组件的事件处理逻辑
        '''
        # 橡皮擦点击事件处理
        self.EraserCheckbox.clicked.connect(self.on_eraser_checkbox_clicked)

        # 笔划粗细改变事件处理
        self.spinBox_penThickness.valueChanged.connect(
            self.on_pen_thickness_change
        )  # 关联spinBox值变化信号和函数on_PenThicknessChange

        # 笔划颜色改变事件处理
        self.comboBox_penColor.currentIndexChanged.connect(
            self.on_pen_color_change)  # 关联下拉列表的当前索引变更信号与函数on_PenColorChange

        # 清空画板按钮事件处理
        self.btn_Clear.clicked.connect(self.PaintBoard.clear)

        # 退出事件处理
        # self.btn_Quit.clicked.connect(self.Quit)

    def update_inform(self, inform):
        self.InformWidget.setText(inform)

    def fill_color_list(self, comboBox, valList, defVal):
        '''
            填充下拉菜单中的菜单项,使用colorList填充
        '''
        index_default = self.colorList.index(config.paint.DefaultColor)
        index = 0
        for color in valList:
            if color == defVal:
                index_default = index
            index += 1
            pix = QPixmap(70, 20)
            pix.fill(QColor(color))
            comboBox.addItem(QIcon(pix), None)
            comboBox.setIconSize(QSize(70, 20))
            comboBox.setSizeAdjustPolicy(QComboBox.AdjustToContents)

        assert default_color != -1, '默认颜色在颜色列表中不存在!'
        comboBox.setCurrentIndex(index_default)

    def set_painting(self, painting):
        self.PaintBoard.set_paint.emit(painting)

    def set_clock_digit(self, number):
        self.InformClock.display(str(number))

    def set_pen_color(self, color):
        self.PaintBoard.set_pen_color(color)
        self.comboBox_penColor.setCurrentIndex(self.colorList.index(color))

    def set_pen_thickness(self, thickness):
        self.PaintBoard.set_pen_thickness(thickness)
        self.spinBox_penThickness.setValue(int(thickness))

    def set_eraser(self, e):
        self.EraserCheckbox.setChecked(bool(e))
        self.PaintBoard.set_eraser(bool(e))

    def set_setting_visible(self, v):
        self.spinBox_penThickness.setEnabled(v)  #.setVisible(v)
        self.comboBox_penColor.setEnabled(v)
        self.EraserCheckbox.setEnabled(v)
        self.btn_Clear.setEnabled(v)

    def extern_click(self, x, y):
        self.PaintBoard.extern_click(x, y)

    def extern_paint(self, ps):
        self.PaintBoard.extern_paint(ps)
        # self.update()

    def extern_clear(self, *args, **kwargs):
        self.PaintBoard.clear()

    def resetLastPoint(self, x, y):
        self.PaintBoard.reset_last_point(x, y)

    def on_pen_color_change(self):
        color_index = self.comboBox_penColor.currentIndex()
        color_str = self.colorList[color_index]
        self.PaintBoard.change_pen_color(color_str)

    def on_pen_thickness_change(self):
        penThickness = self.spinBox_penThickness.value()
        # print('thick change to ', penThickness)
        self.PaintBoard.change_pen_thickness(penThickness)

    def on_eraser_checkbox_clicked(self):
        e = self.EraserCheckbox.isChecked()
        self.PaintBoard.set_eraser(e)

    def Quit(self):
        self.close()
Beispiel #7
0
class ConfigWidget(QWidget, Logger):
    # Manually managed controls when saving/restoring
    EXCLUDED_CONTROLS = [
        'cfg_annotations_destination_comboBox'
        ]

    #LOCATION_TEMPLATE = "{cls}:{func}({arg1}) {arg2}"

    WIZARD_PROFILES = {
        'Annotations': {
            'label': 'mm_annotations',
            'datatype': 'comments',
            'display': {},
            'is_multiple': False
            }
        }

    def __init__(self, plugin_action):
        self.gui = plugin_action.gui
        self.opts = plugin_action.opts

        QWidget.__init__(self)
        self.l = QVBoxLayout()
        self.setLayout(self.l)

        # ~~~~~~~~ Create the runtime options group box ~~~~~~~~
        self.cfg_runtime_options_gb = QGroupBox(self)
        self.cfg_runtime_options_gb.setTitle(_('Runtime options'))
        self.l.addWidget(self.cfg_runtime_options_gb)
        self.cfg_runtime_options_qvl = QVBoxLayout(self.cfg_runtime_options_gb)

        # ~~~~~~~~ Disable caching checkbox ~~~~~~~~
        self.cfg_disable_caching_checkbox = QCheckBox(_('Disable caching'))
        self.cfg_disable_caching_checkbox.setObjectName('cfg_disable_caching_checkbox')
        self.cfg_disable_caching_checkbox.setToolTip(_('Force reload of reader database'))
        self.cfg_disable_caching_checkbox.setChecked(False)
        self.cfg_runtime_options_qvl.addWidget(self.cfg_disable_caching_checkbox)

        # ~~~~~~~~ plugin logging checkbox ~~~~~~~~
        self.cfg_plugin_debug_log_checkbox = QCheckBox(_('Enable debug logging for Annotations plugin'))
        self.cfg_plugin_debug_log_checkbox.setObjectName('cfg_plugin_debug_log_checkbox')
        self.cfg_plugin_debug_log_checkbox.setToolTip(_('Print plugin diagnostic messages to console'))
        self.cfg_plugin_debug_log_checkbox.setChecked(False)
        self.cfg_runtime_options_qvl.addWidget(self.cfg_plugin_debug_log_checkbox)

        # ~~~~~~~~ libiMobileDevice logging checkbox ~~~~~~~~
        self.cfg_libimobiledevice_debug_log_checkbox = QCheckBox(_('Enable debug logging for libiMobileDevice'))
        self.cfg_libimobiledevice_debug_log_checkbox.setObjectName('cfg_libimobiledevice_debug_log_checkbox')
        self.cfg_libimobiledevice_debug_log_checkbox.setToolTip(_('Print libiMobileDevice debug messages to console'))
        self.cfg_libimobiledevice_debug_log_checkbox.setChecked(False)
        self.cfg_libimobiledevice_debug_log_checkbox.setEnabled(LIBIMOBILEDEVICE_AVAILABLE)
        self.cfg_runtime_options_qvl.addWidget(self.cfg_libimobiledevice_debug_log_checkbox)

        # ~~~~~~~~ Create the Annotations options group box ~~~~~~~~
        self.cfg_annotation_options_gb = QGroupBox(self)
        self.cfg_annotation_options_gb.setTitle(_('Annotation options'))
        self.l.addWidget(self.cfg_annotation_options_gb)

        self.cfg_annotation_options_qgl = QGridLayout(self.cfg_annotation_options_gb)
        current_row = 0

        # Add the label/combobox for annotations destination
        self.cfg_annotations_destination_label = QLabel(_('<b>Add fetched annotations to<b>'))
        self.cfg_annotations_destination_label.setAlignment(Qt.AlignLeft)
        self.cfg_annotation_options_qgl.addWidget(self.cfg_annotations_destination_label, current_row, 0)
        current_row += 1

        self.cfg_annotations_destination_comboBox = QComboBox(self.cfg_annotation_options_gb)
        self.cfg_annotations_destination_comboBox.setObjectName('cfg_annotations_destination_comboBox')
        self.cfg_annotations_destination_comboBox.setToolTip(_('Custom field to store annotations'))
        self.cfg_annotation_options_qgl.addWidget(self.cfg_annotations_destination_comboBox, current_row, 0)

        # Populate annotations_field combobox
        db = self.gui.current_db
        all_custom_fields = db.custom_field_keys()
        self.custom_fields = {}
        for custom_field in all_custom_fields:
            field_md = db.metadata_for_field(custom_field)
            if field_md['datatype'] in ['comments']:
                self.custom_fields[field_md['name']] = {'field': custom_field,
                                                   'datatype': field_md['datatype']}

        all_fields = self.custom_fields.keys() + ['Comments']
        for cf in sorted(all_fields):
            self.cfg_annotations_destination_comboBox.addItem(cf)

        # Add CC Wizard
        self.cfg_annotations_wizard = QToolButton()
        self.cfg_annotations_wizard.setIcon(QIcon(I('wizard.png')))
        self.cfg_annotations_wizard.setToolTip(_("Create a custom column to store annotations"))
        self.cfg_annotations_wizard.clicked.connect(partial(self.launch_cc_wizard, 'Annotations'))
        self.cfg_annotation_options_qgl.addWidget(self.cfg_annotations_wizard, current_row, 2)

        current_row += 1

        # ~~~~~~~~ Add a horizontal line ~~~~~~~~
        self.cfg_appearance_hl = QFrame(self)
        self.cfg_appearance_hl.setGeometry(QRect(0, 0, 1, 3))
        self.cfg_appearance_hl.setFrameShape(QFrame.HLine)
        self.cfg_appearance_hl.setFrameShadow(QFrame.Raised)
        self.cfg_annotation_options_qgl.addWidget(self.cfg_appearance_hl, current_row, 0)
        current_row += 1

        # ~~~~~~~~ Add the Modify… button ~~~~~~~~
        self.cfg_annotations_appearance_pushbutton = QPushButton(_("Modify appearance…"))
        self.cfg_annotations_appearance_pushbutton.clicked.connect(self.configure_appearance)
        self.cfg_annotation_options_qgl.addWidget(self.cfg_annotations_appearance_pushbutton, current_row, 0)
        current_row += 1

        self.spacerItem = QtGui.QSpacerItem(20, 40, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
        self.cfg_annotation_options_qgl.addItem(self.spacerItem, current_row, 0, 1, 1)

        # ~~~~~~~~ Compilations group box ~~~~~~~~
        self.cfg_compilation_options_gb = QGroupBox(self)
        self.cfg_compilation_options_gb.setTitle(_('Compilations'))
        self.l.addWidget(self.cfg_compilation_options_gb)
        self.cfg_compilation_options_qgl = QGridLayout(self.cfg_compilation_options_gb)
        current_row = 0

        #   News clippings
        self.cfg_news_clippings_checkbox = QCheckBox(_('Collect News clippings'))
        self.cfg_news_clippings_checkbox.setObjectName('cfg_news_clippings_checkbox')
        self.cfg_compilation_options_qgl.addWidget(self.cfg_news_clippings_checkbox,
            current_row, 0)

        self.cfg_news_clippings_lineEdit = QLineEdit()
        self.cfg_news_clippings_lineEdit.setObjectName('cfg_news_clippings_lineEdit')
        self.cfg_news_clippings_lineEdit.setToolTip(_('Title for collected news clippings'))
        self.cfg_compilation_options_qgl.addWidget(self.cfg_news_clippings_lineEdit,
            current_row, 1)

        # ~~~~~~~~ End of construction zone ~~~~~~~~

        self.resize(self.sizeHint())

        # Restore state of controls, populate annotations combobox
        self.controls = inventory_controls(self, dump_controls=False)
        restore_state(self)
        self.populate_annotations()

        # Hook changes to annotations_destination_combobox
#        self.connect(self.cfg_annotations_destination_comboBox,
#                     pyqtSignal('currentIndexChanged(const QString &)'),
#                     self.annotations_destination_changed)
        self.cfg_annotations_destination_comboBox.currentIndexChanged.connect(self.annotations_destination_changed)

        # Hook changes to diagnostic checkboxes
        self.cfg_disable_caching_checkbox.stateChanged.connect(self.restart_required)
        self.cfg_libimobiledevice_debug_log_checkbox.stateChanged.connect(self.restart_required)
        self.cfg_plugin_debug_log_checkbox.stateChanged.connect(self.restart_required)

        # Hook changes to News clippings, initialize
        self.cfg_news_clippings_checkbox.stateChanged.connect(self.news_clippings_toggled)
        self.news_clippings_toggled(self.cfg_news_clippings_checkbox.checkState())
        self.cfg_news_clippings_lineEdit.editingFinished.connect(self.news_clippings_destination_changed)

        # Launch the annotated_books_scanner
        field = get_cc_mapping('annotations', 'field', 'Comments')
        self.annotated_books_scanner = InventoryAnnotatedBooks(self.gui, field)
        self.annotated_books_scanner.signal.connect(self.inventory_complete)
#        self.connect(self.annotated_books_scanner, self.annotated_books_scanner.signal,
#            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(repr(qs_new_destination_name))
        self._log("self.custom_fields: %s" % self.custom_fields)

        old_destination_field = get_cc_mapping('annotations', 'field', None)
        if old_destination_field and not (old_destination_field in self.gui.current_db.custom_field_keys() or old_destination_field == 'Comments'):
            return
        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)

        # Catch initial change from None to Comments - first run only
        if old_destination_field is None:
            return

#        new_destination_name = unicode(qs_new_destination_name)
        new_destination_name = unicode(self.cfg_annotations_destination_comboBox.currentText())
        self._log("new_destination_name: %s" % new_destination_name)

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

        new_destination_field = None
        if new_destination_name == 'Comments':
            new_destination_field = 'Comments'
        else:
            new_destination_field = self.custom_fields[new_destination_name]['field']

        if existing_annotations(self.opts.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.cfg_annotations_destination_comboBox.blockSignals(True)
                old_index = self.cfg_annotations_destination_comboBox.findText(old_destination_name)
                self.cfg_annotations_destination_comboBox.setCurrentIndex(old_index)
                self.cfg_annotations_destination_comboBox.blockSignals(False)

            """
            # Warn user that change will move existing annotations to new field
            title = 'Move annotations?'
            msg = ("<p>Existing annotations will be moved from <b>%s</b> to <b>%s</b>.</p>" %
                    (old_destination_name, new_destination_name) +
                   "<p>New annotations will be added to <b>%s</b>.</p>" %
                    new_destination_name +
                   "<p>Proceed?</p>")
            d = MessageBox(MessageBox.QUESTION,
                           title, msg,
                           show_copy_button=False)
            self._log_location("QUESTION: %s" % msg)
            if d.exec_():
                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)

            else:
                self.cfg_annotations_destination_comboBox.blockSignals(True)
                old_index = self.cfg_annotations_destination_comboBox.findText(old_destination_name)
                self.cfg_annotations_destination_comboBox.setCurrentIndex(old_index)
                self.cfg_annotations_destination_comboBox.blockSignals(False)
            """

        else:
            # No existing annotations, just update prefs
            set_cc_mapping('annotations', field=new_destination_field, combobox=new_destination_name)

    def configure_appearance(self):
        '''
        '''
        from calibre_plugins.annotations.appearance import default_elements
        from calibre_plugins.annotations.appearance import default_timestamp
        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 appearance dialog
        aa = AnnotationsAppearance(self, get_icon('images/annotations.png'), 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, offer to re-render
        field = get_cc_mapping('annotations', 'field', None)
        if osh.digest() != nsh.digest() and existing_annotations(self.opts.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")
                if self.annotated_books_scanner.isRunning():
                    self.annotated_books_scanner.wait()
                move_annotations(self, self.annotated_books_scanner.annotation_map,
                    field, field, window_title=_("Updating appearance"))

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

    def launch_cc_wizard(self, column_type):
        '''
        '''
        def _update_combo_box(comboBox, destination, previous):
            '''
            '''
            self._log_location()

            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)

            # Process the changed destination
            self.annotations_destination_changed(destination)

            cb.blockSignals(False)


        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)

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

                self._log("destination: %s" % destination)
                self._log("label: %s" % label)
                self._log("previous: %s" % previous)
                self._log("source: %s" % source)

                if source == "Annotations":
                    # Add/update the new destination so save_settings() can find it
                    if destination in self.custom_fields:
                        self.custom_fields[destination]['field'] = label
                    else:
                        self.custom_fields[destination] = {'field': label}

                    _update_combo_box('cfg_annotations_destination_comboBox', destination, previous)

                    # Save field manually in case user cancels
                    #self.prefs.set('cfg_annotations_destination_comboBox', destination)
                    #self.prefs.set('cfg_annotations_destination_field', label)
                    set_cc_mapping('annotations', field=label, combobox=destination)

                    # Inform user to restart
                    self.restart_required('custom_column')

        else:
            self._log("ERROR: Can't import from '%s'" % klass)

    def launch_new_destination_dialog(self, old, new):
        '''
        Return 'move', 'change' or 'cancel'
        '''
        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 news_clippings_destination_changed(self):
        qs_new_destination_name = self.cfg_news_clippings_lineEdit.text()
        if not re.match(r'^\S+[A-Za-z0-9 ]+$', qs_new_destination_name):
            # Complain about News clippings title
            title = _('Invalid title for News clippings')
            msg = _("Supply a valid title for News clippings, for example 'My News Clippings'.")
            d = MessageBox(MessageBox.WARNING,
                           title, msg,
                           show_copy_button=False)
            self._log_location("WARNING: %s" % msg)
            d.exec_()

    def news_clippings_toggled(self, state):
        if state == Qt.Checked:
            self.cfg_news_clippings_lineEdit.setEnabled(True)
        else:
            self.cfg_news_clippings_lineEdit.setEnabled(False)

    def populate_annotations(self):
        '''
        Restore annotations combobox
        '''
        self._log_location()
        target = 'Comments'
        existing = get_cc_mapping('annotations', 'combobox')
        if existing:
            target = existing
        ci = self.cfg_annotations_destination_comboBox.findText(target)
        self.cfg_annotations_destination_comboBox.setCurrentIndex(ci)

    def restart_required(self, state):
        title = _('Restart required')
        msg = _('To apply changes, restart calibre.')
        d = MessageBox(MessageBox.WARNING,
                       title, msg,
                       show_copy_button=False)
        self._log_location("WARNING: %s" % (msg))
        d.exec_()

    def save_settings(self):
        save_state(self)

        # Save the annotation destination field
        ann_dest = unicode(self.cfg_annotations_destination_comboBox.currentText())
        self._log_location("INFO: ann_dest=%s" % (ann_dest))
        self._log_location("INFO: self.custom_fields=%s" % (self.custom_fields))
        if ann_dest == 'Comments':
            set_cc_mapping('annotations', field='Comments', combobox='Comments')
        elif ann_dest:
            set_cc_mapping('annotations', field=self.custom_fields[ann_dest]['field'], combobox=ann_dest)

    def start_inventory(self):
        self.annotated_books_scanner.start()
Beispiel #8
0
class MakeBrickDialog(QDialog):
    def __init__(self, parent, modal=True, flags=Qt.WindowFlags()):
        QDialog.__init__(self, parent, flags)
        self.model = None
        self._model_dir = None
        self.setModal(modal)
        self.setWindowTitle("Convert sources to FITS brick")
        lo = QVBoxLayout(self)
        lo.setContentsMargins(10, 10, 10, 10)
        lo.setSpacing(5)
        # file selector
        self.wfile = FileSelector(self,
                                  label="FITS filename:",
                                  dialog_label="Output FITS file",
                                  default_suffix="fits",
                                  file_types="FITS files (*.fits *.FITS)",
                                  file_mode=QFileDialog.ExistingFile)
        lo.addWidget(self.wfile)
        # reference frequency
        lo1 = QHBoxLayout()
        lo.addLayout(lo1)
        lo1.setContentsMargins(0, 0, 0, 0)
        label = QLabel("Frequency, MHz:", self)
        lo1.addWidget(label)
        tip = """<P>If your sky model contains spectral information (such as spectral indices), then a brick may be generated
    for a specific frequency. If a frequency is not specified here, the reference frequency of the model sources will be assumed.</P>"""
        self.wfreq = QLineEdit(self)
        self.wfreq.setValidator(QDoubleValidator(self))
        label.setToolTip(tip)
        self.wfreq.setToolTip(tip)
        lo1.addWidget(self.wfreq)
        # beam gain
        lo1 = QHBoxLayout()
        lo.addLayout(lo1)
        lo1.setContentsMargins(0, 0, 0, 0)
        self.wpb_apply = QCheckBox("Apply primary beam expression:", self)
        self.wpb_apply.setChecked(True)
        lo1.addWidget(self.wpb_apply)
        tip = """<P>If this option is specified, a primary power beam gain will be applied to the sources before inserting
    them into the brick. This can be any valid Python expression making use of the variables 'r' (corresponding
    to distance from field centre, in radians) and 'fq' (corresponding to frequency.)</P>"""
        self.wpb_exp = QLineEdit(self)
        self.wpb_apply.setToolTip(tip)
        self.wpb_exp.setToolTip(tip)
        lo1.addWidget(self.wpb_exp)
        # overwrite or add mode
        lo1 = QHBoxLayout()
        lo.addLayout(lo1)
        lo1.setContentsMargins(0, 0, 0, 0)
        self.woverwrite = QRadioButton("overwrite image", self)
        self.woverwrite.setChecked(True)
        lo1.addWidget(self.woverwrite)
        self.waddinto = QRadioButton("add into image", self)
        lo1.addWidget(self.waddinto)
        # add to model
        self.wadd = QCheckBox(
            "Add resulting brick to sky model as a FITS image component", self)
        lo.addWidget(self.wadd)
        lo1 = QHBoxLayout()
        lo.addLayout(lo1)
        lo1.setContentsMargins(0, 0, 0, 0)
        self.wpad = QLineEdit(self)
        self.wpad.setValidator(QDoubleValidator(self))
        self.wpad.setText("1.1")
        lab = QLabel("...with padding factor:", self)
        lab.setToolTip(
            """<P>The padding factor determines the amount of null padding inserted around the image during
      the prediction stage. Padding alleviates the effects of tapering and detapering in the uv-brick, which can show
      up towards the edges of the image. For a factor of N, the image will be padded out to N times its original size.
      This increases memory use, so if you have no flux at the edges of the image anyway, then a pad factor of 1 is
      perfectly fine.</P>""")
        self.wpad.setToolTip(lab.toolTip())
        self.wadd.toggled[bool].connect(self.wpad.setEnabled)
        self.wadd.toggled[bool].connect(lab.setEnabled)
        self.wpad.setEnabled(False)
        lab.setEnabled(False)
        lo1.addStretch(1)
        lo1.addWidget(lab, 0)
        lo1.addWidget(self.wpad, 1)
        self.wdel = QCheckBox(
            "Remove from the sky model sources that go into the brick", self)
        lo.addWidget(self.wdel)
        # OK/cancel buttons
        lo.addSpacing(10)
        lo2 = QHBoxLayout()
        lo.addLayout(lo2)
        lo2.setContentsMargins(5, 5, 5, 5)
        self.wokbtn = QPushButton("OK", self)
        self.wokbtn.setMinimumWidth(128)
        self.wokbtn.clicked.connect(self.accept)
        self.wokbtn.setEnabled(False)
        cancelbtn = QPushButton("Cancel", self)
        cancelbtn.setMinimumWidth(128)
        cancelbtn.clicked.connect(self.reject)
        lo2.addWidget(self.wokbtn)
        lo2.addStretch(1)
        lo2.addWidget(cancelbtn)
        self.setMinimumWidth(384)
        # signals
        self.wfile.filenameSelected.connect(self._fileSelected)
        # internal state
        self.qerrmsg = QErrorMessage(self)

    def setModel(self, model):
        self.model = model
        pb = self.model.primaryBeam()
        if pb:
            self.wpb_exp.setText(pb)
        else:
            self.wpb_apply.setChecked(False)
            self.wpb_exp.setText("")
        if model.filename():
            self._model_dir = os.path.dirname(os.path.abspath(
                model.filename()))
        else:
            self._model_dir = os.path.abspath('.')
        self.wfile.setDirectory(self._model_dir)
        self._fileSelected(self.wfile.filename(), quiet=True)

    def _fileSelected(self, filename, quiet=False):
        self.wokbtn.setEnabled(False)
        if not filename:
            return None
        # check that filename matches model
        if not os.path.samefile(self._model_dir, os.path.dirname(filename)):
            self.wfile.setFilename('')
            if not quiet:
                QMessageBox.warning(
                    self, "Directory mismatch",
                    """<P>The FITS file must reside in the same directory
          as the current sky model.</P>""")
            self.wfile.setDirectory(self._model_dir)
            return None
        # read fits file
        busy = BusyIndicator()
        try:
            input_hdu = pyfits.open(filename)[0]
            hdr = input_hdu.header
            # get frequency, if specified
            for axis in range(1, hdr['NAXIS'] + 1):
                if hdr['CTYPE%d' % axis].upper() == 'FREQ':
                    self.wfreq.setText(str(hdr['CRVAL%d' % axis] / 1e+6))
                    break
        except Exception as err:
            busy.reset_cursor()
            self.wfile.setFilename('')
            if not quiet:
                QMessageBox.warning(
                    self, "Error reading FITS",
                    "Error reading FITS file %s: %s" % (filename, str(err)))
            return None
        self.wokbtn.setEnabled(True)
        # if filename is not in model already, enable the "add to model" control
        for src in self.model.sources:
            if isinstance(getattr(src, 'shape', None), ModelClasses.FITSImage) \
                    and os.path.exists(src.shape.filename) and os.path.exists(filename) \
                    and os.path.samefile(src.shape.filename, filename):
                self.wadd.setChecked(True)
                self.wadd.setEnabled(False)
                self.wadd.setText("image already in sky model")
                break
        else:
            self.wadd.setText("add image to sky model")
        busy.reset_cursor()
        return filename

    def accept(self):
        """Tries to make a brick, and closes the dialog if successful."""
        sources = [
            src for src in self.model.sources
            if src.selected and src.typecode == 'pnt'
        ]
        filename = self.wfile.filename()
        if not self._fileSelected(filename):
            return
        # get PB expression
        pbfunc = None
        if self.wpb_apply.isChecked():
            pbexp = str(self.wpb_exp.text())
            try:
                pbfunc = eval("lambda r,fq:" + pbexp)
            except Exception as err:
                QMessageBox.warning(
                    self, "Error parsing PB experssion",
                    "Error parsing primary beam expression %s: %s" %
                    (pbexp, str(err)))
                return
        # get frequency
        freq = str(self.wfreq.text())
        freq = float(freq) * 1e+6 if freq else None
        # get pad factor
        pad = str(self.wpad.text())
        pad = max(float(pad), 1) if pad else 1
        # read fits file
        busy = BusyIndicator()
        try:
            input_hdu = pyfits.open(filename)[0]
        except Exception as err:
            busy.reset_cursor()
            QMessageBox.warning(
                self, "Error reading FITS",
                "Error reading FITS file %s: %s" % (filename, str(err)))
            return
        # reset data if asked to
        if self.woverwrite.isChecked():
            input_hdu.data[...] = 0
        # insert sources
        Imaging.restoreSources(input_hdu,
                               sources,
                               0,
                               primary_beam=pbfunc,
                               freq=freq)
        # save fits file
        try:
            # pyfits seems to produce an exception:
            #         TypeError: formatwarning() takes exactly 4 arguments (5 given)
            # when attempting to overwrite a file. As a workaround, remove the file first.
            if os.path.exists(filename):
                os.remove(filename)
            input_hdu.writeto(filename)
        except Exception as err:
            traceback.print_exc()
            busy.reset_cursor()
            QMessageBox.warning(
                self, "Error writing FITS",
                "Error writing FITS file %s: %s" % (filename, str(err)))
            return
        changed = False
        sources = self.model.sources
        # remove sources from model if asked to
        if self.wdel.isChecked():
            sources = [
                src for src in sources
                if not (src.selected and src.typecode == 'pnt')
            ]
            changed = True
        # add image to model if asked to
        if self.wadd.isChecked():
            hdr = input_hdu.header
            # get image parameters
            max_flux = float(input_hdu.data.max())
            wcs = WCS(hdr, mode='pyfits')
            # Get reference pixel coordinates
            # wcs.getCentreWCSCoords() doesn't work, as that gives us the middle of the image
            # So scan the header to get the CRPIX values
            ra0 = dec0 = 1
            for iaxis in range(hdr['NAXIS']):
                axs = str(iaxis + 1)
                name = hdr.get('CTYPE' + axs, axs).upper()
                if name.startswith("RA"):
                    ra0 = hdr.get('CRPIX' + axs, 1) - 1
                elif name.startswith("DEC"):
                    dec0 = hdr.get('CRPIX' + axs, 1) - 1
            # convert pixel to degrees
            ra0, dec0 = wcs.pix2wcs(ra0, dec0)
            ra0 *= DEG
            dec0 *= DEG
            sx, sy = wcs.getHalfSizeDeg()
            sx *= DEG
            sy *= DEG
            nx, ny = input_hdu.data.shape[-1:-3:-1]
            # check if this image is already contained in the model
            for src in sources:
                if isinstance(getattr(src, 'shape', None),
                              ModelClasses.FITSImage) and os.path.samefile(
                                  src.shape.filename, filename):
                    # update source parameters
                    src.pos.ra, src.pos.dec = ra0, dec0
                    src.flux.I = max_flux
                    src.shape.ex, src.shape.ey = sx, sy
                    src.shape.nx, src.shape.ny = nx, ny
                    src.shape.pad = pad
                    break
            # not contained, make new source object
            else:
                pos = ModelClasses.Position(ra0, dec0)
                flux = ModelClasses.Flux(max_flux)
                shape = ModelClasses.FITSImage(sx,
                                               sy,
                                               0,
                                               os.path.basename(filename),
                                               nx,
                                               ny,
                                               pad=pad)
                img_src = SkyModel.Source(os.path.splitext(
                    os.path.basename(filename))[0],
                                          pos,
                                          flux,
                                          shape=shape)
                sources.append(img_src)
            changed = True
        if changed:
            self.model.setSources(sources)
            self.model.emitUpdate(SkyModel.SkyModel.UpdateAll, origin=self)
        self.parent().showMessage("Wrote %d sources to FITS file %s" %
                                  (len(sources), filename))
        busy.reset_cursor()
        return QDialog.accept(self)
Beispiel #9
0
class ConverterWidget(QMainWindow):

    name = 'RWConvert'

    # For easier usage calculate the path relative to here.
    here = os.path.abspath(os.path.dirname(__file__))
    getPath = partial(os.path.join, here)

    # Setup a plugin base for "rwconvert.plugins" and make sure to load
    # all the default built-in plugins from the builtin_plugins folder.
    pluginBase = PluginBase(package='rwconvert.plugins',
                            searchpath=[getPath('./plugins')])

    plugins = {}
    filenames = []
    currentPlugin = None
    filetypes = 'All Files (*.*)'
    config = {}

    def __init__(self):
        '''
        init
        '''

        super().__init__()

        self.comms = CommSignals()
        """
        Read configuration file and return its contents
        """
        self.initConfig()
        cfgDir = self.config[PATHS]['config_path']
        self.cfgFileName = os.path.join(cfgDir,
                                        self.config[FILES]['config_name'])
        if not os.path.isfile(self.cfgFileName):
            os.makedirs(os.path.dirname(self.cfgFileName), exist_ok=True)
            self.exportConfig()
        else:
            with open(self.cfgFileName, 'r') as configFile:
                c = yaml.load(configFile)
                c = {} if c is None else c
                if c != {}:
                    self.config = c

        # and a source which loads the plugins from the "plugins"
        # folder.  We also pass the application name as identifier.  This
        # is optional but by doing this out plugins have consistent
        # internal module names which allows pickle to work.
        self.source = self.pluginBase.make_plugin_source(
            searchpath=[self.getPath('./plugins')], identifier=self.name)

        # Here we list all the plugins the source knows about, load them
        # and the use the "setup" function provided by the plugin to
        # initialize the plugin.
        for pluginName in self.source.list_plugins():
            plugin = self.source.load_plugin(pluginName)
            pluginClass = getattr(plugin, pluginName)
            instance = pluginClass(self.config, self.comms)
            self.plugins[pluginName] = instance

        self.initGui()


#         self.showFullScreen()

    def initConfig(self):
        if PATHS not in self.config: self.config[PATHS] = {}
        if FILES not in self.config: self.config[FILES] = {}
        if FORMS not in self.config: self.config[FORMS] = {}
        if NAMES not in self.config: self.config[NAMES] = {}

        self.config[PATHS]['homepath'] = os.path.expanduser('~')
        self.config[PATHS]['docpath'] = os.path.join(
            self.config[PATHS]['homepath'], 'Documents')
        self.config[PATHS]['download_path'] = os.path.join(
            self.config[PATHS]['homepath'], 'Downloads')
        self.config[PATHS]['save_path'] = os.path.join(
            self.config[PATHS]['docpath'], self.name)
        self.config[PATHS]['config_path'] = appdirs.user_config_dir(self.name)
        self.config[PATHS]['db_path'] = os.path.join(
            self.config[PATHS]['config_path'], 'data')

        self.config[FILES]['config_name'] = 'config.yaml'
        self.config[FILES]['db_name'] = 'rwconvert.sqlite'
        self.config[FILES]['save_file'] = 'roadwarrior'
        self.config[FILES]['save_ext'] = '.xlsx'

        self.config[FORMS]['include_date'] = False
        self.config[FORMS]['prefix_date'] = False
        self.config[FORMS]['include_routes'] = False
        self.config[FORMS]['combine_routes'] = False

        self.config[NAMES]['current_plugin_name'] = ''

        self.destFilename = self.config['Files']['save_file'] + self.config[
            'Files']['save_ext']

        # Create destination file directory if it doesn't already exist'
        #         if not os.path.exists(self.config[PATHS]['config_path']):
        os.makedirs(self.config[PATHS]['config_path'], exist_ok=True)
        #         if not os.path.exists(self.config[PATHS]['db_path']):
        os.makedirs(self.config[PATHS]['db_path'], exist_ok=True)
        #         if not os.path.exists(self.config[PATHS]['save_path']):
        os.makedirs(self.config[PATHS]['save_path'], exist_ok=True)

    def initGui(self):
        self.setGeometry(300, 300, 800, 600)
        self.setWindowTitle('Road warrior Upload File Converter')
        self.center()

        fMain = QFrame()
        mainLayout = QGridLayout()
        fMain.setLayout(mainLayout)
        self.setCentralWidget(fMain)

        tabWidget = QTabWidget()
        mainLayout.addWidget(tabWidget, 0, 0)

        self.closeBtn = QPushButton('Close Application')
        self.closeBtn.clicked.connect(self.handleCloseClicked)
        mainLayout.addWidget(self.closeBtn, 1, 0)

        tabWidget.addTab(self.initConvertPage(), 'Converter')
        tabWidget.addTab(self.initResultPage(), 'Converter')
        tabWidget.addTab(self.initConfigPage(), 'Configuration')

    def initResultPage(self):
        f = QFrame()
        l = QHBoxLayout()
        f.setLayout(l)

        self.convertedTabs = QTabWidget()
        l.addWidget(self.convertedTabs)
        '''
        This just adds a blank page with an empty tab widget.
        Pages are added on the fly when the files are converted as
        this could involve one or many pages depending on combine_route
        flag and number of routes.

        self.convertedTabs is the empty tab widget
        '''

        return f

    def initConvertPage(self):
        f = QFrame()
        l = QGridLayout()
        f.setLayout(l)

        row = 0

        l.addWidget(QLabel('Converter :'), row, 0)
        currentPluginBox = QComboBox()
        currentPluginBox.currentTextChanged.connect(self.selectPlugin)
        l.addWidget(currentPluginBox, row, 1)
        for key in self.plugins.keys():
            currentPluginBox.addItem(key)
        row += 1

        l.addWidget(QLabel('Destination path :'), row, 0)
        self.destPathLbl = QLabel(
            self.joinSavePath(self.config[PATHS]['save_path'],
                              self.config[FILES]['save_file'],
                              self.config[FILES]['save_ext']))
        self.destPathLbl.setSizePolicy(QSizePolicy.Expanding,
                                       QSizePolicy.Fixed)
        l.addWidget(self.destPathLbl, row, 1)

        row += 1

        f2 = QFrame()
        l2 = QHBoxLayout()
        f2.setLayout(l2)

        f3 = QFrame()
        l3 = QGridLayout()
        f3.setLayout(l3)

        l3.addWidget(QLabel('Destination Files :'), 0, 0)
        self.destFilesList = QListWidget()
        self.destFilesList.setAlternatingRowColors(True)
        l3.addWidget(self.destFilesList, 1, 0)

        l2.addWidget(self.initFileFrame())
        l2.addWidget(f3)
        l.addWidget(f2, row, 0, 1, 3)

        row += 1

        l.addWidget(self.initSrcFiles(), row, 0, 1, 3)

        row += 1

        self.convertBtn = QPushButton('Convert')
        self.convertBtn.clicked.connect(self.handleConvertClicked)
        self.convertBtn.setEnabled(False)
        l.addWidget(self.convertBtn, row, 0, 1, 3)

        return f

    def initSrcFiles(self):

        row = 0

        f = QFrame()
        l = QGridLayout()
        f.setLayout(l)

        l.addWidget(QLabel('Source Files :'), row, 0)
        self.srcFilesList = QListWidget()
        self.srcFilesList.setSelectionMode(QAbstractItemView.MultiSelection)
        self.srcFilesList.setAlternatingRowColors(True)
        self.srcFilesList.model().rowsInserted.connect(
            self.handleSrcFilesChanged)
        self.srcFilesList.model().rowsRemoved.connect(
            self.handleSrcFilesChanged)
        selectionModel = self.srcFilesList.selectionModel()
        selectionModel.selectionChanged.connect(
            self.handleSrcFilesSelectionChanged)
        l.addWidget(self.srcFilesList, row, 1, 3, 1)

        self.srcFilesBtn = QPushButton('Select Files')
        self.srcFilesBtn.clicked.connect(self.handleSelectSrcFiles)
        self.srcFilesBtn.setSizePolicy(QSizePolicy.Preferred,
                                       QSizePolicy.Expanding)
        l.addWidget(self.srcFilesBtn, row, 2)
        row += 1

        self.addFilesBtn = QPushButton('Add Files')
        self.addFilesBtn.clicked.connect(self.handleAddSrcFiles)
        self.addFilesBtn.setSizePolicy(QSizePolicy.Preferred,
                                       QSizePolicy.Expanding)
        l.addWidget(self.addFilesBtn, row, 2)
        row += 1

        self.removeFilesBtn = QPushButton('Remove Files')
        self.removeFilesBtn.setEnabled(False)
        self.removeFilesBtn.clicked.connect(self.handleRemoveSrcFiles)
        self.removeFilesBtn.setSizePolicy(QSizePolicy.Preferred,
                                          QSizePolicy.Expanding)
        l.addWidget(self.removeFilesBtn, row, 2)
        row += 1

        self.errorEdit = QPlainTextEdit()
        self.errorEdit.setReadOnly(True)
        l.addWidget(self.errorEdit, row, 1, 1, 2)
        self.comms.errorSignal.connect(self.errorEdit.appendPlainText)

        return f

    def initFileFrame(self):
        row = 0

        f = QFrame()
        l = QGridLayout()
        f.setLayout(l)
        #         f.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Expanding)

        l.addWidget(QLabel('Destination File :'), row, 0)
        self.destFilenameEdit = QLineEdit(self.destFilename)
        self.destFilenameEdit.setEnabled(False)
        self.destFilenameEdit.textChanged.connect(
            self.handleDestFilenameChanged)
        l.addWidget(self.destFilenameEdit, row, 1)
        row += 1

        self.combineRoutesBox = QCheckBox('combine_routes')
        if self.config[FORMS]['combine_routes']:
            self.combineRoutesBox.setChecked(True)
        else:
            self.combineRoutesBox.setChecked(False)
        self.combineRoutesBox.clicked.connect(self.handleCombineRoutesClicked)
        self.combineRoutesBox.setEnabled(False)
        l.addWidget(self.combineRoutesBox, row, 0)
        row += 1

        addDateInNameBox = QCheckBox('Add date to filename')
        addDateInNameBox.clicked.connect(self.handleAddDateClicked)
        l.addWidget(addDateInNameBox, row, 0)
        if self.config[FORMS]['prefix_date']:
            self.prefixDateInNameBox = QPushButton('prefix_date')
            self.prefixDateInNameBox.setEnabled(False)
        else:
            self.prefixDateInNameBox = QPushButton('Suffix date')
            self.prefixDateInNameBox.setEnabled(False)
        self.prefixDateInNameBox.setToolTip('Click to change to prefix date.')
        self.prefixDateInNameBox.clicked.connect(self.handlePrefixDateClicked)

        l.addWidget(self.prefixDateInNameBox, row, 1)
        row += 1

        addRouteInNameBox = QCheckBox('Add route to filename')
        addRouteInNameBox.clicked.connect(self.handleAddRouteClicked)
        l.addWidget(addRouteInNameBox, row, 0)
        #         row += 1

        return f

    def initConfigPage(self):
        row = 0

        f = QFrame()
        l = QGridLayout()
        f.setLayout(l)

        srcLbl = QLabel('Source directory :')
        srcLbl.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        l.addWidget(srcLbl, row, 0)

        self.srcDirBox = QLineEdit()
        self.srcDirBox.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
        self.srcDirBox.setText(self.config[PATHS]['download_path'])
        l.addWidget(self.srcDirBox, row, 1)

        srcDirSelectBtn = QPushButton('Select')
        srcDirSelectBtn.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        srcDirSelectBtn.clicked.connect(self.handleSelectSrcDirectory)
        l.addWidget(srcDirSelectBtn, row, 2)

        row += 1

        destLbl = QLabel('Destination directory :')
        destLbl.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        l.addWidget(destLbl, row, 0)

        self.destDirBox = QLineEdit()
        self.destDirBox.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
        self.destDirBox.setText(self.config[PATHS]['save_path'])
        l.addWidget(self.destDirBox, row, 1)

        destDirSelectBtn = QPushButton('Select')
        destDirSelectBtn.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        destDirSelectBtn.clicked.connect(self.handleSelectDestDirectory)
        l.addWidget(destDirSelectBtn, row, 2)

        row += 1

        l.addWidget(QFrame(), row, 0, 1, 3)

        return f

    def handleSelectSrcDirectory(self):
        directory = QFileDialog.getExistingDirectory(
            self, 'Select Source Directory',
            self.config[PATHS]['download_path'], QFileDialog.ShowDirsOnly)
        if not directory == '':
            self.config[PATHS]['download_path'] = directory
            self.srcDirBox.setText(directory)

    def handleSelectDestDirectory(self):
        directory = QFileDialog.getExistingDirectory(
            self, 'Select Destination Directory',
            self.config[PATHS]['save_path'], QFileDialog.ShowDirsOnly)
        if not directory == '':
            self.config[PATHS]['save_path'] = directory
            self.destDirBox.setText(directory)

    def joinSavePath(self, path, filename, ext):
        newpath = os.path.join(path, filename)
        newpath += ext
        return newpath

    @pyqtSlot(bool)
    def handleAddDateClicked(self, checked):
        if checked:
            self.config[FORMS]['include_path'] = True
            self.prefixDateInNameBox.setEnabled(True)
        else:
            self.config[FORMS]['include_path'] = False
            self.prefixDateInNameBox.setEnabled(False)

    @pyqtSlot()
    def handlePrefixDateClicked(self):
        if self.config[FORMS]['prefix_date']:
            self.config[FORMS]['prefix_date'] = False
            self.prefixDateInNameBox.setText('Suffix date')
            self.prefixDateInNameBox.setToolTip(
                'Click to change to prefix date.')
        else:
            self.config[FORMS]['prefix_date'] = True
            self.prefixDateInNameBox.setText('Prefix date')
            self.prefixDateInNameBox.setToolTip(
                'Click to change to suffix date.')

    @pyqtSlot()
    def handleAddRouteClicked(self, checked):
        if checked:
            self.config[FORMS]['include_routes'] = True
        else:
            self.config[FORMS]['include_routes'] = False

    @pyqtSlot()
    def handleCombineRoutesClicked(self):
        if self.combineRoutesBox.isChecked():
            self.config[FORMS]['combine_routes'] = True
        else:
            self.config[FORMS]['combine_routes'] = False

        self.enableStuff()

    def enableStuff(self):
        if self.srcFilesList.model().rowCount() == 0:
            self.convertBtn.setEnabled(False)
            self.combineRoutesBox.setEnabled(False)
            self.destFilenameEdit.setEnabled(False)
        elif self.srcFilesList.model().rowCount() == 1:
            self.convertBtn.setEnabled(True)
            self.combineRoutesBox.setEnabled(False)
            self.destFilenameEdit.setEnabled(True)
        else:
            self.convertBtn.setEnabled(True)
            self.combineRoutesBox.setEnabled(True)
            if self.combineRoutesBox.isChecked():
                self.destFilenameEdit.setEnabled(True)
            else:
                self.destFilenameEdit.setEnabled(False)

    @pyqtSlot()
    def handleDestFilenameChanged(self, text):
        if len(text) > 0:
            if self.config[PATHS]['include_path']:
                year = datetime.year
                month = datetime.month
                day = datetime.day
                datestr = str(year)
                if month < 10: datestr += '0'
                datestr += str(month)
                if day < 10: datestr += '0'
                datestr += str(day)
                if self.config[FORMS]['prefix_date']:
                    name = datestr + '_' + text
                else:
                    name = text + '_' + datestr
            else:
                name = text

            self.config[FILES]['save file'] = name
            self.destPathLbl.setText(
                self.joinSavePath(self.savepath, self.savefile, self.saveext))

    def createDestinationFilePath(self):
        return self.joinSavePath(self.savepath, self.savefile, self.saveext)

    @pyqtSlot()
    def handleSrcFilesSelectionChanged(self, selected):
        if len(selected.indexes()) > 0:
            self.removeFilesBtn.setEnabled(True)
        else:
            self.removeFilesBtn.setEnabled(False)

    @pyqtSlot()
    def handleSrcFilesChanged(self):
        self.enableStuff()

    @pyqtSlot()
    def handleConvertClicked(self):
        if len(self.filenames) > 0:
            toexcel = ToExcel()

            self.currentPlugin.convert(self.filenames)
            routedata = self.currentPlugin.data

            if self.config[FORMS]['combine_routes']:
                combined = []
                for route in routedata.keys():
                    singleroute = routedata[route]
                    combined += singleroute
                path = self.joinSavePath(self.config[PATHS]['save_path'],
                                         self.config[FILES]['save_file'],
                                         self.config[FILES]['save_ext'])
                toexcel.create_workbook(combined, path)
                self.destFilesList.addItem(path)

            else:
                i = 1
                for route in routedata.keys():
                    singleroute = routedata[route]
                    if self.config[FORMS]['include_routes'] and len(route) > 0:
                        path = self.joinSavePath(
                            self.config[PATHS]['save_path'],
                            self.config[FILES]['save_file'] + '_' + route,
                            self.config[FILES]['save_ext'])
                    else:
                        path = self.joinSavePath(
                            self.config[PATHS]['save_path'],
                            self.config[FILES]['save_file'] + '(' + str(i) +
                            ')', self.config[FILES]['save_ext'])
                        i += 1

                    toexcel.create_workbook(singleroute, path)
                    self.destFilesList.addItem(path)

    @pyqtSlot()
    def handleConverterChanged(self):
        pluginName = self.currentPluginBox.currentText()
        if pluginName != self.config[NAMES]['current_plugin_name']:
            self.config[NAMES]['current_plugin_name'] = pluginName
            self.currentPlugin = self.plugins[pluginName]
            self.filetypes = self.currentPlugin.filetypes

    @pyqtSlot()
    def handleCloseClicked(self):
        self.close()

    @pyqtSlot()
    def handleSelectSrcFiles(self):
        fileDlg = QFileDialog(self, 'Select Files',
                              self.config[PATHS]['download_path'],
                              self.filetypes)
        fileDlg.setFileMode(QFileDialog.ExistingFiles)

        if fileDlg.exec_():
            self.filenames = fileDlg.selectedFiles()

        self.srcFilesList.clear()
        self.srcFilesList.addItems(self.filenames)

    @pyqtSlot()
    def handleAddSrcFiles(self):
        fileDlg = QFileDialog(self, 'Select Files',
                              self.config[PATHS]['download_path'],
                              self.filetypes)
        fileDlg.setFileMode(QFileDialog.ExistingFiles)

        if fileDlg.exec_():
            for filename in fileDlg.selectedFiles():
                if filename not in self.filenames:
                    self.filenames.append(filename)
                    self.srcFilesList.addItem(filename)

    @pyqtSlot()
    def handleRemoveSrcFiles(self):
        selectedModel = self.srcFilesList.selectionModel()
        selected = selectedModel.selectedIndexes()
        for index in selected:
            name = index.data()
            self.filenames.remove(name)
            self.srcFilesList.takeItem(index.row())
        selectedModel.clear()

    @pyqtSlot(str)
    def selectPlugin(self, name):
        # activate the current plugin
        #             self.pluginManager.activatePluginByName(name)
        self.config[NAMES]['current_plugin_name'] = name
        self.currentPlugin = self.plugins[name]
        self.filetypes = self.currentPlugin.filetypes

    def center(self):
        qr = self.frameGeometry()
        cp = QDesktopWidget().availableGeometry().center()
        qr.moveCenter(cp)
        self.move(qr.topLeft())

    def exportConfig(self):
        with open(self.cfgFileName, 'w') as configFile:
            yaml.dump(self.config, configFile)

    def createUserConfig(self):
        """
        Create the user's config file in OS specific location
        """
        os.makedirs(os.path.dirname(self.cfgFileName), exist_ok=True)
        self.exportConfig()

    def closeEvent(self, event):
        buttonReply = QMessageBox.warning(
            self, 'Close Application',
            'You are about to close the Application,'
            ' Press Yes to continue or Cancel to return to the Application',
            QMessageBox.Yes | QMessageBox.Cancel, QMessageBox.Cancel)
        if buttonReply == QMessageBox.Yes:
            self.exportConfig()
            exit()