class ConditionEditor(QWidget): # {{{ ACTION_MAP = { 'bool' : ( (_('is true'), 'is true',), (_('is false'), 'is false'), (_('is undefined'), 'is undefined') ), 'ondevice' : ( (_('is true'), 'is set',), (_('is false'), 'is not set'), ), 'identifiers' : ( (_('has id'), 'has id'), (_('does not have id'), 'does not have id'), ), 'int' : ( (_('is equal to'), 'eq'), (_('is less than'), 'lt'), (_('is greater than'), 'gt') ), 'datetime' : ( (_('is equal to'), 'eq'), (_('is less than'), 'lt'), (_('is greater than'), 'gt'), (_('is set'), 'is set'), (_('is not set'), 'is not set'), (_('is more days ago than'), 'older count days'), (_('is fewer days ago than'), 'count_days'), (_('is more days from now than'), 'newer future days'), (_('is fewer days from now than'), 'older future days') ), 'multiple' : ( (_('has'), 'has'), (_('does not have'), 'does not have'), (_('has pattern'), 'has pattern'), (_('does not have pattern'), 'does not have pattern'), (_('is set'), 'is set'), (_('is not set'), 'is not set'), ), 'single' : ( (_('is'), 'is'), (_('is not'), 'is not'), (_('contains'), 'contains'), (_('does not contain'), 'does not contain'), (_('matches pattern'), 'matches pattern'), (_('does not match pattern'), 'does not match pattern'), (_('is set'), 'is set'), (_('is not set'), 'is not set'), ), } for x in ('float', 'rating'): ACTION_MAP[x] = ACTION_MAP['int'] def __init__(self, fm, parent=None): QWidget.__init__(self, parent) self.fm = fm self.action_map = self.ACTION_MAP self.l = l = QGridLayout(self) self.setLayout(l) texts = _('If the ___ column ___ values') try: one, two, three = texts.split('___') except: one, two, three = 'If the ', ' column ', ' value ' self.l1 = l1 = QLabel(one) l.addWidget(l1, 0, 0) self.column_box = QComboBox(self) l.addWidget(self.column_box, 0, 1) self.l2 = l2 = QLabel(two) l.addWidget(l2, 0, 2) self.action_box = QComboBox(self) l.addWidget(self.action_box, 0, 3) self.l3 = l3 = QLabel(three) l.addWidget(l3, 0, 4) self.value_box = QLineEdit(self) l.addWidget(self.value_box, 0, 5) self.column_box.addItem('', '') for key in sorted( conditionable_columns(fm), key=lambda key: sort_key(fm[key]['name'])): self.column_box.addItem(fm[key]['name'], key) self.column_box.setCurrentIndex(0) self.column_box.currentIndexChanged.connect(self.init_action_box) self.action_box.currentIndexChanged.connect(self.init_value_box) for b in (self.column_box, self.action_box): b.setSizeAdjustPolicy(b.AdjustToMinimumContentsLengthWithIcon) b.setMinimumContentsLength(20) @dynamic_property def current_col(self): def fget(self): idx = self.column_box.currentIndex() return unicode_type(self.column_box.itemData(idx) or '') def fset(self, val): for idx in range(self.column_box.count()): c = unicode_type(self.column_box.itemData(idx) or '') if c == val: self.column_box.setCurrentIndex(idx) return raise ValueError('Column %r not found'%val) return property(fget=fget, fset=fset) @dynamic_property def current_action(self): def fget(self): idx = self.action_box.currentIndex() return unicode_type(self.action_box.itemData(idx) or '') def fset(self, val): for idx in range(self.action_box.count()): c = unicode_type(self.action_box.itemData(idx) or '') if c == val: self.action_box.setCurrentIndex(idx) return raise ValueError('Action %r not valid for current column'%val) return property(fget=fget, fset=fset) @property def current_val(self): ans = unicode_type(self.value_box.text()).strip() if self.current_col == 'languages': rmap = {lower(v):k for k, v in lang_map().iteritems()} ans = rmap.get(lower(ans), ans) return ans @dynamic_property def condition(self): def fget(self): c, a, v = (self.current_col, self.current_action, self.current_val) if not c or not a: return None return (c, a, v) def fset(self, condition): c, a, v = condition if not v: v = '' v = v.strip() self.current_col = c self.current_action = a self.value_box.setText(v) return property(fget=fget, fset=fset) def init_action_box(self): self.action_box.blockSignals(True) self.action_box.clear() self.action_box.addItem('', '') col = self.current_col if col: m = self.fm[col] dt = m['datatype'] if dt in self.action_map: actions = self.action_map[dt] else: if col == 'ondevice': k = 'ondevice' elif col == 'identifiers': k = 'identifiers' else: k = 'multiple' if m['is_multiple'] else 'single' actions = self.action_map[k] for text, key in actions: self.action_box.addItem(text, key) self.action_box.setCurrentIndex(0) self.action_box.blockSignals(False) self.init_value_box() def init_value_box(self): self.value_box.setEnabled(True) self.value_box.setText('') self.value_box.setInputMask('') self.value_box.setValidator(None) col = self.current_col if not col: return action = self.current_action if not action: return m = self.fm[col] dt = m['datatype'] tt = '' if col == 'identifiers': tt = _('Enter either an identifier type or an ' 'identifier type and value of the form identifier:value') elif col == 'languages': tt = _('Enter a 3 letter ISO language code, like fra for French' ' or deu for German or eng for English. You can also use' ' the full language name, in which case calibre will try to' ' automatically convert it to the language code.') elif dt in ('int', 'float', 'rating'): tt = _('Enter a number') v = QIntValidator if dt == 'int' else QDoubleValidator self.value_box.setValidator(v(self.value_box)) elif dt == 'datetime': if action == 'count_days': self.value_box.setValidator(QIntValidator(self.value_box)) tt = _('Enter the maximum days old the item can be. Zero is today. ' 'Dates in the future always match') elif action == 'older count days': self.value_box.setValidator(QIntValidator(self.value_box)) tt = _('Enter the minimum days old the item can be. Zero is today. ' 'Dates in the future never match') elif action == 'older future days': self.value_box.setValidator(QIntValidator(self.value_box)) tt = _('Enter the maximum days in the future the item can be. ' 'Zero is today. Dates in the past always match') elif action == 'newer future days': self.value_box.setValidator(QIntValidator(self.value_box)) tt = _('Enter the minimum days in the future the item can be. ' 'Zero is today. Dates in the past never match') else: self.value_box.setInputMask('9999-99-99') tt = _('Enter a date in the format YYYY-MM-DD') else: tt = _('Enter a string.') if 'pattern' in action: tt = _('Enter a regular expression') elif m.get('is_multiple', False): tt += '\n' + _('You can match multiple values by separating' ' them with %s')%m['is_multiple']['ui_to_list'] self.value_box.setToolTip(tt) if action in ('is set', 'is not set', 'is true', 'is false', 'is undefined'): self.value_box.setEnabled(False)
def handle_server(self): """ Handle for server button """ server_dialog = QDialog() server_dialog.setWindowTitle('Server Configuration') server_dialog.setWindowFlags(Qt.FramelessWindowHint) server_dialog.setStyleSheet(get_css()) server_dialog.setFixedSize(300, 300) main_layout = QVBoxLayout(server_dialog) main_layout.setContentsMargins(0, 0, 0, 0) main_layout.addWidget(self.get_logo_widget(server_dialog)) server_widget = QWidget(self) server_widget.setObjectName('login') server_layout = QGridLayout(server_widget) # Description desc_label = QLabel( '<h3>Alignak Backend</h3><p>Here you can define alignak settings.</p>' '<b>Be sure to enter a valid address</b>') desc_label.setWordWrap(True) server_layout.addWidget(desc_label, 0, 0, 1, 3) # Server URL server_layout.addWidget(QLabel('Server'), 1, 0, 1, 1) server_url = QLineEdit() server_url.setPlaceholderText('alignak backend url') server_url.setText(get_app_config('Alignak', 'url')) server_layout.addWidget(server_url, 1, 1, 1, 2) # Server Port server_layout.addWidget(QLabel('Port'), 2, 0, 1, 1) server_port = QLineEdit() server_port.setPlaceholderText('alignak backend port') cur_port = get_app_config('Alignak', 'backend').split(':')[2] server_port.setText(cur_port) server_layout.addWidget(server_port, 2, 1, 1, 2) # Server Processes server_layout.addWidget(QLabel('Processes'), 3, 0, 1, 1) server_proc = QLineEdit() if 'win32' in sys.platform: server_proc.setEnabled(False) server_proc.setPlaceholderText('alignak backend processes') cur_proc = get_app_config('Alignak', 'processes') server_proc.setText(cur_proc) server_layout.addWidget(server_proc, 3, 1, 1, 2) # Valid Button valid_btn = QPushButton('Valid') valid_btn.clicked.connect(server_dialog.accept) server_layout.addWidget(valid_btn, 4, 0, 1, 3) main_layout.addWidget(server_widget) self.center(server_widget) if server_dialog.exec_() == QDialog.Accepted: backend_url = '%(url)s:' + str(server_port.text()).rstrip() set_app_config('Alignak', 'backend', backend_url) set_app_config('Alignak', 'url', str(server_url.text()).rstrip()) set_app_config('Alignak', 'processes', str(server_proc.text()).rstrip())
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)
class ConditionEditor(QWidget): # {{{ ACTION_MAP = { 'bool' : ( (_('is true'), 'is true',), (_('is false'), 'is false'), (_('is undefined'), 'is undefined') ), 'ondevice' : ( (_('is true'), 'is set',), (_('is false'), 'is not set'), ), 'identifiers' : ( (_('has id'), 'has id'), (_('does not have id'), 'does not have id'), ), 'int' : ( (_('is equal to'), 'eq'), (_('is less than'), 'lt'), (_('is greater than'), 'gt') ), 'datetime' : ( (_('is equal to'), 'eq'), (_('is less than'), 'lt'), (_('is greater than'), 'gt'), (_('is set'), 'is set'), (_('is not set'), 'is not set'), (_('is more days ago than'), 'older count days'), (_('is fewer days ago than'), 'count_days'), (_('is more days from now than'), 'newer future days'), (_('is fewer days from now than'), 'older future days') ), 'multiple' : ( (_('has'), 'has'), (_('does not have'), 'does not have'), (_('has pattern'), 'has pattern'), (_('does not have pattern'), 'does not have pattern'), (_('is set'), 'is set'), (_('is not set'), 'is not set'), ), 'single' : ( (_('is'), 'is'), (_('is not'), 'is not'), (_('matches pattern'), 'matches pattern'), (_('does not match pattern'), 'does not match pattern'), (_('is set'), 'is set'), (_('is not set'), 'is not set'), ), } for x in ('float', 'rating'): ACTION_MAP[x] = ACTION_MAP['int'] def __init__(self, fm, parent=None): QWidget.__init__(self, parent) self.fm = fm self.action_map = self.ACTION_MAP self.l = l = QGridLayout(self) self.setLayout(l) texts = _('If the ___ column ___ values') try: one, two, three = texts.split('___') except: one, two, three = 'If the ', ' column ', ' value ' self.l1 = l1 = QLabel(one) l.addWidget(l1, 0, 0) self.column_box = QComboBox(self) l.addWidget(self.column_box, 0, 1) self.l2 = l2 = QLabel(two) l.addWidget(l2, 0, 2) self.action_box = QComboBox(self) l.addWidget(self.action_box, 0, 3) self.l3 = l3 = QLabel(three) l.addWidget(l3, 0, 4) self.value_box = QLineEdit(self) l.addWidget(self.value_box, 0, 5) self.column_box.addItem('', '') for key in sorted( conditionable_columns(fm), key=lambda(key): sort_key(fm[key]['name'])): self.column_box.addItem(fm[key]['name'], key) self.column_box.setCurrentIndex(0) self.column_box.currentIndexChanged.connect(self.init_action_box) self.action_box.currentIndexChanged.connect(self.init_value_box) for b in (self.column_box, self.action_box): b.setSizeAdjustPolicy(b.AdjustToMinimumContentsLengthWithIcon) b.setMinimumContentsLength(20) @dynamic_property def current_col(self): def fget(self): idx = self.column_box.currentIndex() return unicode(self.column_box.itemData(idx) or '') def fset(self, val): for idx in range(self.column_box.count()): c = unicode(self.column_box.itemData(idx) or '') if c == val: self.column_box.setCurrentIndex(idx) return raise ValueError('Column %r not found'%val) return property(fget=fget, fset=fset) @dynamic_property def current_action(self): def fget(self): idx = self.action_box.currentIndex() return unicode(self.action_box.itemData(idx) or '') def fset(self, val): for idx in range(self.action_box.count()): c = unicode(self.action_box.itemData(idx) or '') if c == val: self.action_box.setCurrentIndex(idx) return raise ValueError('Action %r not valid for current column'%val) return property(fget=fget, fset=fset) @property def current_val(self): ans = unicode(self.value_box.text()).strip() if self.current_col == 'languages': rmap = {lower(v):k for k, v in lang_map().iteritems()} ans = rmap.get(lower(ans), ans) return ans @dynamic_property def condition(self): def fget(self): c, a, v = (self.current_col, self.current_action, self.current_val) if not c or not a: return None return (c, a, v) def fset(self, condition): c, a, v = condition if not v: v = '' v = v.strip() self.current_col = c self.current_action = a self.value_box.setText(v) return property(fget=fget, fset=fset) def init_action_box(self): self.action_box.blockSignals(True) self.action_box.clear() self.action_box.addItem('', '') col = self.current_col if col: m = self.fm[col] dt = m['datatype'] if dt in self.action_map: actions = self.action_map[dt] else: if col == 'ondevice': k = 'ondevice' elif col == 'identifiers': k = 'identifiers' else: k = 'multiple' if m['is_multiple'] else 'single' actions = self.action_map[k] for text, key in actions: self.action_box.addItem(text, key) self.action_box.setCurrentIndex(0) self.action_box.blockSignals(False) self.init_value_box() def init_value_box(self): self.value_box.setEnabled(True) self.value_box.setText('') self.value_box.setInputMask('') self.value_box.setValidator(None) col = self.current_col if not col: return action = self.current_action if not action: return m = self.fm[col] dt = m['datatype'] tt = '' if col == 'identifiers': tt = _('Enter either an identifier type or an ' 'identifier type and value of the form identifier:value') elif col == 'languages': tt = _('Enter a 3 letter ISO language code, like fra for French' ' or deu for German or eng for English. You can also use' ' the full language name, in which case calibre will try to' ' automatically convert it to the language code.') elif dt in ('int', 'float', 'rating'): tt = _('Enter a number') v = QIntValidator if dt == 'int' else QDoubleValidator self.value_box.setValidator(v(self.value_box)) elif dt == 'datetime': if action == 'count_days': self.value_box.setValidator(QIntValidator(self.value_box)) tt = _('Enter the maximum days old the item can be. Zero is today. ' 'Dates in the future always match') elif action == 'older count days': self.value_box.setValidator(QIntValidator(self.value_box)) tt = _('Enter the minimum days old the item can be. Zero is today. ' 'Dates in the future never match') elif action == 'older future days': self.value_box.setValidator(QIntValidator(self.value_box)) tt = _('Enter the maximum days in the future the item can be. ' 'Zero is today. Dates in the past always match') elif action == 'newer future days': self.value_box.setValidator(QIntValidator(self.value_box)) tt = _('Enter the minimum days in the future the item can be. ' 'Zero is today. Dates in the past never match') else: self.value_box.setInputMask('9999-99-99') tt = _('Enter a date in the format YYYY-MM-DD') else: tt = _('Enter a string.') if 'pattern' in action: tt = _('Enter a regular expression') elif m.get('is_multiple', False): tt += '\n' + _('You can match multiple values by separating' ' them with %s')%m['is_multiple']['ui_to_list'] self.value_box.setToolTip(tt) if action in ('is set', 'is not set', 'is true', 'is false', 'is undefined'): self.value_box.setEnabled(False)
class LLT_AddWord(QMainWindow): def __init__(self): super(LLT_AddWord, self).__init__() self.w = QWidget() self.setCentralWidget(self.w) self.verbDic = [] self.nounDic = [] self.adjDic = [] self.phraseDic = [] self.wordList = [] self.w.setWindowTitle("Add Word") self.w.setGeometry(0, 0, 500, 500) self.vRad = QRadioButton("Verb") self.vRad.setChecked(True) self.nRad = QRadioButton("Noun") self.aRad = QRadioButton("Adjective") self.pRad = QRadioButton("Phrase") self.okBut = QPushButton("OK") self.okBut.clicked.connect(self.OK) self.entryLab = QLabel("Word: ") self.wordEntry = QLineEdit() self.wordEntry.setEnabled(False) self.tranLab = QLabel("Translation: ") self.tranEntry = QLineEdit() self.tranEntry.setEnabled(False) self.checkBut = QPushButton("Check") self.checkBut.clicked.connect(self.check) self.saveBut = QPushButton("Save") self.saveBut.clicked.connect(self.save) self.clearBut = QPushButton("Clear") self.clearBut.clicked.connect(self.clear) self.newBut = QPushButton("New word") self.newBut.clicked.connect(self.new) self.exitBut = QPushButton("Exit") self.exitBut.clicked.connect(self.exit) grid = QGridLayout() grid.addWidget(self.vRad, 0, 0) grid.addWidget(self.nRad, 0, 1) grid.addWidget(self.aRad, 0, 2) grid.addWidget(self.pRad, 0, 3) grid.addWidget(self.okBut, 0, 4) grid.addWidget(self.entryLab, 1, 0) grid.addWidget(self.wordEntry, 1, 1, 1, 3) grid.addWidget(self.tranLab, 2, 0) grid.addWidget(self.tranEntry, 2, 1, 1, 3) grid.addWidget(self.checkBut, 3, 0) grid.addWidget(self.saveBut, 3, 1) grid.addWidget(self.clearBut, 3, 2) grid.addWidget(self.newBut, 3, 3) grid.addWidget(self.exitBut, 3, 4) self.getDics() self.setLists() self.w.setLayout(grid) self.w.show() def OK(self): self.vRad.setEnabled(False) self.nRad.setEnabled(False) self.aRad.setEnabled(False) self.pRad.setEnabled(False) self.okBut.setEnabled(False) self.wordEntry.setEnabled(True) self.tranEntry.setEnabled(True) def check(self): word = self.wordEntry.text().upper() if word in self.wordList: msgBox = QMessageBox() msgBox.setText(word + ' already in dictionary') msgBox.exec_() else: msgBox = QMessageBox() msgBox.setText(word + ' not in dictionary yet') msgBox.exec_() def save(self): spanWord = self.wordEntry.text().upper() tranWord = self.tranEntry.text().upper() if spanWord in self.wordList: msgBox = QMessageBox() msgBox.setText(spanWord + ' already in dictionary') msgBox.exec_() else: self.wordList.append(spanWord) newWord = [spanWord, tranWord] self.wordEntry.setEnabled(False) self.tranEntry.setEnabled(False) if self.vRad.isChecked(): self.verbDic.append(newWord) v = open('verb.txt', 'w') json.dump(self.verbDic, v) v.close() elif self.nRad.isChecked(): self.nounDic.append(newWord) n = open('noun.txt', 'w') json.dump(self.nounDic, n) n.close() elif self.aRad.isChecked(): self.adjDic.append(newWord) a = open('adj.txt', 'w') json.dump(self.adjDic, a) a.close() elif self.pRad.isChecked(): self.phraseDic.append(newWord) p = open('phrase.txt', 'w') json.dump(self.phraseDic, p) p.close() else: msgBox = QMessageBox() msgBox.setText( "You must select a dictionary before saving word.") msgBox.exec_() def clear(self): self.vRad.setEnabled(True) self.nRad.setEnabled(True) self.aRad.setEnabled(True) self.pRad.setEnabled(True) self.okBut.setEnabled(True) self.wordEntry.setEnabled(False) self.tranEntry.setEnabled(False) self.wordEntry.clear() self.tranEntry.clear() def new(self): self.vRad.setEnabled(True) self.nRad.setEnabled(True) self.aRad.setEnabled(True) self.pRad.setEnabled(True) self.okBut.setEnabled(True) self.wordEntry.setEnabled(False) self.tranEntry.setEnabled(False) self.wordEntry.clear() self.tranEntry.clear() def exit(self): confirm = QMessageBox.question(self.w, 'Quit', 'Are you sure you want to exit?', QMessageBox.Yes | QMessageBox.No) if confirm == QMessageBox.Yes: self.close() else: pass def getDics(self): try: v = open('verb.txt', 'r') self.verbDic = json.load(v) v.close() except: self.verbDic = [] try: n = open('noun.txt', 'r') self.nounDic = json.load(n) n.close() except: self.nounDic = [] try: p = open('phrase.txt', 'r') self.phraseDic = json.load(p) p.close() except: self.phraseDic = [] try: a = open('adj.txt', 'r') self.adjDic = json.load(a) a.close() except: self.phraseDic = [] def setLists(self): for item in self.verbDic: self.wordList.append(item[0]) for item in self.nounDic: self.wordList.append(item[0]) for item in self.phraseDic: self.wordList.append(item[0]) for item in self.adjDic: self.wordList.append(item[0])
class Window(QWidget): ext_list = ['mkv', 'mp4', 'avi'] def __init__(self): QWidget.__init__(self) self.v_box = QVBoxLayout() self.v_dir = QVBoxLayout() self.v_ext = QVBoxLayout() self.v_av = QVBoxLayout() self.H_default_save_dir = QHBoxLayout() self.H_default_ext = QHBoxLayout() self.H_av = QHBoxLayout() self.H_save = QHBoxLayout() self.settings = xml.ParseXMLSettings() self.txt_default_dir = QLineEdit(self) self.drpdwn_av = QComboBox(self) self.drpdwn_ext = QComboBox(self) self.shortcut = QShortcut(QKeySequence('Ctrl+W'), self) self.shortcut.activated.connect(self.close) self.initUI() self.v_box.addSpacing(12) self.H_av.addSpacing(12) self.H_default_ext.addSpacing(12) self.H_default_save_dir.addSpacing(12) self.H_save.addSpacing(12) self.setLayout(self.v_box) self.setGeometry(200, 200, 250, 150) self.setWindowTitle('Global Settings') def initUI(self): self.txt_default_dir.setText(self.settings.settings_list['save_dir']) self.txt_default_dir.setEnabled(False) lbl_default_dir = QLabel(self) lbl_default_dir.setText('Default Directory: ') btn_default_dir = QPushButton(self) btn_default_dir.setText('Change...') self.drpdwn_av.addItem(self.settings.settings_list['av']) lbl_av = QLabel(self) lbl_av.setText('Record Audio and Video? ') self.drpdwn_ext.addItem(self.settings.settings_list['ext']) lbl_ext = QLabel(self) lbl_ext.setText('Video Extension: ') btn_save = QPushButton(self) btn_save.setText('Save changes...') btn_default_dir.clicked.connect(lambda: self.change_dir()) btn_save.clicked.connect(self.save) if self.settings.settings_list['av'] == 'True': self.drpdwn_av.addItem('False') else: self.drpdwn_av.addItem('True') for ext in self.ext_list: if ext != self.settings.settings_list['ext']: self.drpdwn_ext.addItem(ext) self.v_dir.addWidget(lbl_default_dir) self.H_default_save_dir.addWidget(self.txt_default_dir) self.H_default_save_dir.addWidget(btn_default_dir) self.v_dir.addLayout(self.H_default_save_dir) self.v_av.addWidget(lbl_av) self.H_av.addWidget(self.drpdwn_av) self.v_av.addLayout(self.H_av) self.v_ext.addWidget(lbl_ext) self.H_default_ext.addWidget(self.drpdwn_ext) self.v_ext.addLayout(self.H_default_ext) self.H_save.addWidget(btn_save) self.v_ext.addStretch(1) self.v_av.addStretch(1) self.v_dir.addStretch(1) self.v_box.addLayout(self.v_dir) self.v_box.addLayout(self.v_av) self.v_box.addLayout(self.v_ext) self.v_box.addLayout(self.H_save) self.v_box.addStretch(1) def save(self): if self.drpdwn_ext.currentText( ) != self.settings.settings_list['ext'] or self.drpdwn_av.currentText( ) != self.settings.settings_list['av']: dlg = dialog.QuestionDialog('Are you ready to save changes?') if dlg.clickedButton() == dlg.yes: if self.drpdwn_ext.currentText( ) != self.settings.settings_list['ext']: self.settings.edit('ext', self.drpdwn_ext.currentText()) SETTINGS.settings_list[ 'ext'] = self.drpdwn_ext.currentText() if self.drpdwn_av.currentText( ) != self.settings.settings_list['av']: self.settings.edit('av', self.drpdwn_av.currentText()) SETTINGS.settings_list['av'] = self.drpdwn_av.currentText() else: pass def change_dir(self): dlg = str(QFileDialog.getExistingDirectory(self, "Select Directory")) if dlg != "": self.settings.edit('save_dir', dlg) self.txt_default_dir.setText(dlg) SETTINGS.settings_list['save_dir'] = dlg
class LLT_Lookup(QMainWindow): def __init__(self): super(LLT_Lookup, self).__init__() self.w = QWidget() self.setCentralWidget(self.w) self.verbDic = [] self.nounDic = [] self.adjDic = [] self.phraseDic = [] self.wordList = [] self.wordDic = [] self.index = int(0) self.w.setWindowTitle("Word Lookup") self.w.setGeometry(0,0,500, 500) self.lookLab = QLabel("Lookup: ") self.lookEntry = QLineEdit() self.lookBut = QPushButton("Search") self.lookBut.clicked.connect(self.search) self.wordLab = QLabel("Word: ") self.resultWord = QLabel() self.tranLab = QLabel("Translation: ") self.resultTran = QLabel() self.newBut = QPushButton("New Search") self.newBut.clicked.connect(self.new) self.exitBut = QPushButton("Exit") self.exitBut.clicked.connect(self.exit) grid = QGridLayout() grid.addWidget(self.lookLab, 0, 0) grid.addWidget(self.lookEntry, 0, 1, 1, 2) grid.addWidget(self.lookBut, 0, 3) grid.addWidget(self.wordLab, 1, 0) grid.addWidget(self.resultWord, 1, 1, 1, 3) grid.addWidget(self.tranLab, 2, 0) grid.addWidget(self.resultTran, 2, 1, 1, 3) grid.addWidget(self.newBut, 3, 0) grid.addWidget(self.exitBut, 3, 3) self.getDics() self.setLists() self.w.setLayout(grid) self.w.show() def search(self): searchWord = self.lookEntry.text().upper().strip() if searchWord in self.wordList: self.lookEntry.setEnabled(False) self.lookBut.setEnabled(False) self.index = self.wordList.index(searchWord) foundWord = self.wordDic[self.index] self.resultWord.setText(foundWord[0]) self.resultTran.setText(foundWord[1]) else: msgBox = QMessageBox() msgBox.setText("Word not currently saved in dictionary.") msgBox.exec_(); def new(self): self.lookEntry.setEnabled(True) self.lookBut.setEnabled(True) self.resultWord.setText('') self.resultTran.setText('') self.lookEntry.clear() def exit(self): confirm = QMessageBox.question(self.w, 'Quit', 'Are you sure you want to exit?', QMessageBox.Yes | QMessageBox.No) if confirm == QMessageBox.Yes: self.close() else: pass def getDics(self): try: v=open('verb.txt','r') self.verbDic=json.load(v) v.close() except: self.verbDic = [] try: n=open('noun.txt','r') self.nounDic=json.load(n) n.close() except: self.nounDic = [] try: p=open('phrase.txt','r') self.phraseDic=json.load(p) p.close() except: self.phraseDic = [] try: a=open('adj.txt','r') self.adjDic=json.load(a) a.close() except: self.phraseDic = [] def setLists(self): for item in self.verbDic: self.wordList.append(item[0]) self.wordDic.append(item) for item in self.nounDic: self.wordList.append(item[0]) self.wordDic.append(item) for item in self.adjDic: self.wordList.append(item[0]) self.wordDic.append(item) for item in self.phraseDic: self.wordList.append(item[0]) self.wordDic.append(item)
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()
class LLT_EditWord(QMainWindow): def __init__(self): super(LLT_EditWord, self).__init__() self.w = QWidget() self.setCentralWidget(self.w) self.verbDic = [] self.nounDic = [] self.adjDic = [] self.phraseDic = [] self.wordList = [] self.verbList = [] self.nounList = [] self.adjList = [] self.phraseList = [] self.index = int(0) self.w.setWindowTitle("Edit Word") self.w.setGeometry(0,0,500, 500) self.vRad = QRadioButton("Verb") self.vRad.setChecked(True) self.nRad = QRadioButton("Noun") self.aRad = QRadioButton("Adjective") self.pRad = QRadioButton("Phrase") self.okBut = QPushButton("OK") self.okBut.clicked.connect(self.OK) self.lookLab = QLabel("Lookup: ") self.lookEntry = QLineEdit() self.lookEntry.setEnabled(False) self.lookBut = QPushButton("Search") self.lookBut.clicked.connect(self.search) self.lookBut.setEnabled(False) self.entryLab = QLabel("Word: ") self.wordEntry = QLineEdit() self.wordEntry.setEnabled(False) self.tranLab = QLabel("Translation: ") self.tranEntry = QLineEdit() self.tranEntry.setEnabled(False) self.saveBut = QPushButton("Save") self.saveBut.clicked.connect(self.save) self.clearBut = QPushButton("Reset") self.clearBut.clicked.connect(self.reset) self.newBut = QPushButton("New word") self.newBut.clicked.connect(self.new) self.exitBut = QPushButton("Exit") self.exitBut.clicked.connect(self.exit) grid = QGridLayout() grid.addWidget(self.vRad, 0, 0) grid.addWidget(self.nRad, 0, 1) grid.addWidget(self.aRad, 0, 2) grid.addWidget(self.pRad, 0, 3) grid.addWidget(self.okBut, 0, 4) grid.addWidget(self.lookLab, 1, 0) grid.addWidget(self.lookEntry, 1, 1, 1, 2) grid.addWidget(self.lookBut, 1, 4) grid.addWidget(self.entryLab, 2, 0) grid.addWidget(self.wordEntry, 2, 1, 1, 3) grid.addWidget(self.tranLab, 3, 0) grid.addWidget(self.tranEntry, 3, 1, 1, 3) grid.addWidget(self.saveBut, 4, 0) grid.addWidget(self.clearBut, 4, 1) grid.addWidget(self.newBut, 4, 2) grid.addWidget(self.exitBut, 4, 3) self.getDics() self.setLists() self.w.setLayout(grid) self.w.show() def search(self): searchWord = self.lookEntry.text().upper() currentDic = [] currentList = [] if self.vRad.isChecked(): currentDic = self.verbDic currentList = self.verbList elif self.nRad.isChecked(): currentDic = self.nounDic currentList = self.nounList elif self.aRad.isChecked(): currentDic = self.adjDic currentList = self.adjList elif self.pRad.isChecked(): currentDic = self.phraseDic currentList = self.phraseList else: msgBox = QMessageBox() msgBox.setText("You must select a dictionary before continuing.") msgBox.exec_() self.vRad.setEnabled(True) self.nRad.setEnabled(True) self.aRad.setEnabled(True) self.pRad.setEnabled(True) self.lookEntry.setEnabled(False) self.lookBut.setEnabled(False) self.wordEntry.setEnabled(False) self.tranEntry.setEnabled(False) if searchWord in self.wordList: self.lookEntry.setEnabled(False) self.lookBut.setEnabled(False) self.wordEntry.setEnabled(True) self.tranEntry.setEnabled(True) self.index = currentList.index(searchWord) oldWord = currentDic[self.index] self.wordEntry.setPlaceholderText(oldWord[0]) self.tranEntry.setPlaceholderText(oldWord[1]) else: msgBox = QMessageBox() msgBox.setText("Word not currently saved in dictionary.") msgBox.exec_(); def OK(self): self.vRad.setEnabled(False) self.nRad.setEnabled(False) self.aRad.setEnabled(False) self.pRad.setEnabled(False) self.lookEntry.setEnabled(True) self.lookBut.setEnabled(True) def save(self): spanWord = self.wordEntry.text().upper() tranWord = self.tranEntry.text().upper() if spanWord in self.wordList: msgBox = QMessageBox() msgBox.setText(spanWord + ' already in dictionary') msgBox.exec_(); else: self.wordList.append(spanWord) newWord = [spanWord,tranWord] self.wordEntry.setEnabled(False) self.tranEntry.setEnabled(False) if self.vRad.isChecked(): self.verbDic[self.index]= newWord v = open('verb.txt','w') json.dump(self.verbDic, v) v.close() elif self.nRad.isChecked(): self.nounDic[self.index]= newWord n = open('noun.txt','w') json.dump(self.nounDic, n) n.close() elif self.aRad.isChecked(): self.adjDic[self.index]= newWord a = open('adj.txt','w') json.dump(self.adjDic, a) a.close() elif self.pRad.isChecked(): self.phraseDic[self.index]= newWord p = open('phrase.txt','w') json.dump(self.phraseDic, p) p.close() else: msgBox = QMessageBox() msgBox.setText("You must select a dictionary before saving word.") msgBox.exec_(); def reset(self): self.vRad.setEnabled(True) self.nRad.setEnabled(True) self.aRad.setEnabled(True) self.pRad.setEnabled(True) self.lookEntry.setEnabled(False) self.lookBut.setEnabled(False) self.wordEntry.setEnabled(False) self.tranEntry.setEnabled(False) self.wordEntry.clear() self.tranEntry.clear() self.lookEntry.setPlaceholderText('') self.wordEntry.setPlaceholderText('') self.tranEntry.setPlaceholderText('') def new(self): self.vRad.setEnabled(True) self.nRad.setEnabled(True) self.aRad.setEnabled(True) self.pRad.setEnabled(True) self.wordEntry.setEnabled(False) self.tranEntry.setEnabled(False) self.wordEntry.clear() self.tranEntry.clear() self.lookEntry.setPlaceholderText('') self.wordEntry.setPlaceholderText('') self.tranEntry.setPlaceholderText('') def exit(self): confirm = QMessageBox.question(self.w, 'Quit', 'Are you sure you want to exit?', QMessageBox.Yes | QMessageBox.No) if confirm == QMessageBox.Yes: self.close() else: pass def getDics(self): try: v=open('verb.txt','r') self.verbDic=json.load(v) v.close() except: self.verbDic = [] try: n=open('noun.txt','r') self.nounDic=json.load(n) n.close() except: self.nounDic = [] try: p=open('phrase.txt','r') self.phraseDic=json.load(p) p.close() except: self.phraseDic = [] try: a=open('adj.txt','r') self.adjDic=json.load(a) a.close() except: self.phraseDic = [] def setLists(self): for item in self.verbDic: self.wordList.append(item[0]) self.verbList.append(item[0]) for item in self.nounDic: self.wordList.append(item[0]) self.nounList.append(item[0]) for item in self.adjDic: self.wordList.append(item[0]) self.adjList.append(item[0]) for item in self.phraseDic: self.wordList.append(item[0]) self.phraseList.append(item[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)
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()