class StreamProxy(QtCore.QObject): # only the GUI thread is allowed to write messages in the # LoggerWindow, so this class acts as a proxy, passing messages # over Qt signal/slot for thread safety flush_text = QtCore.pyqtSignal() write_text = QtCore.pyqtSignal(str) def write(self, msg): msg = msg.strip() if msg: self.write_text.emit(msg) def flush(self): self.flush_text.emit()
class MapWebView(QWebView): drop_text = QtCore.pyqtSignal(int, int, six.text_type) def __init__(self, call_handler, *args, **kwds): super(MapWebView, self).__init__(*args, **kwds) # set view's page if using_qtwebengine: self.setPage(MapWebEnginePage(call_handler, parent=self)) self.settings().setAttribute( QWebSettings.Accelerated2dCanvasEnabled, False) else: self.setPage(MapWebKitPage(call_handler, parent=self)) self.settings().setAttribute( QWebSettings.LocalContentCanAccessRemoteUrls, True) self.settings().setAttribute( QWebSettings.LocalContentCanAccessFileUrls, True) @catch_all def dragEnterEvent(self, event): if not event.mimeData().hasFormat(DRAG_MIMETYPE): return super(MapWebView, self).dragEnterEvent(event) event.acceptProposedAction() @catch_all def dragMoveEvent(self, event): if not event.mimeData().hasFormat(DRAG_MIMETYPE): return super(MapWebView, self).dragMoveEvent(event) @catch_all def dropEvent(self, event): if not event.mimeData().hasFormat(DRAG_MIMETYPE): return super(MapWebView, self).dropEvent(event) text = event.mimeData().data(DRAG_MIMETYPE).data().decode('utf-8') if text: self.drop_text.emit(event.pos().x(), event.pos().y(), text)
class NumberEdit(QtWidgets.QLineEdit): new_value = QtCore.pyqtSignal(six.text_type) def __init__(self, *arg, **kw): super(NumberEdit, self).__init__(*arg, **kw) self.multiple = multiple_values() self.textEdited.connect(self.text_edited) self.editingFinished.connect(self.editing_finished) @QtCore.pyqtSlot(six.text_type) @catch_all def text_edited(self, text): self.setPlaceholderText('') @QtCore.pyqtSlot() @catch_all def editing_finished(self): if self.placeholderText() != self.multiple: self.new_value.emit(self.text()) def set_value(self, value): self.setPlaceholderText('') if value: self.setText(str(value)) else: self.clear() def set_multiple(self): self.setPlaceholderText(self.multiple) self.clear()
class FileCopier(QtCore.QObject): output = QtCore.pyqtSignal(dict, six.text_type) def __init__(self, source, copy_list, move, updating, *args, **kwds): super(FileCopier, self).__init__(*args, **kwds) self.source = source self.copy_list = copy_list self.move = move self.updating = updating self.running = True @QtCore.pyqtSlot() @catch_all def start(self): status = 'ok' try: for info in self.source.copy_files(self.copy_list, self.move): # don't display image until previous one has been displayed self.updating.lock() self.output.emit(info, status) self.updating.unlock() if not self.running: break except Exception as ex: status = str(ex) logger.error(status) self.output.emit({}, status)
class OffsetWidget(QtWidgets.QWidget): apply_offset = QtCore.pyqtSignal(timedelta, object) def __init__(self, *arg, **kw): super(OffsetWidget, self).__init__(*arg, **kw) self.config_store = QtWidgets.QApplication.instance().config_store self.setLayout(QtWidgets.QHBoxLayout()) self.layout().setContentsMargins(0, 0, 0, 0) # offset value self.offset = QtWidgets.QTimeEdit() self.offset.setDisplayFormat("'h:'hh 'm:'mm 's:'ss") self.layout().addWidget(self.offset) # time zone self.time_zone = TimeZoneWidget() self.time_zone.set_value(None) self.layout().addWidget(self.time_zone) # add offset button add_button = SquareButton('+') add_button.clicked.connect(self.add) self.layout().addWidget(add_button) # subtract offset button sub_button = SquareButton('-') sub_button.clicked.connect(self.sub) self.layout().addWidget(sub_button) self.layout().addStretch(1) # restore stored values value = eval(self.config_store.get('technical', 'offset', 'None')) if value: self.offset.setTime(QtCore.QTime(*value[0:3])) self.time_zone.set_value(value[3]) # connections self.offset.editingFinished.connect(self.new_value) self.time_zone.editingFinished.connect(self.new_value) @QtCore.pyqtSlot() def new_value(self): value = self.offset.time() value = (value.hour(), value.minute(), value.second(), self.time_zone.get_value()) self.config_store.set('technical', 'offset', str(value)) @QtCore.pyqtSlot() def add(self): value = self.offset.time() offset = timedelta(hours=value.hour(), minutes=value.minute(), seconds=value.second()) self.apply_offset.emit(offset, self.time_zone.get_value()) @QtCore.pyqtSlot() def sub(self): value = self.offset.time() offset = timedelta(hours=value.hour(), minutes=value.minute(), seconds=value.second()) tz_offset = self.time_zone.get_value() if isinstance(tz_offset, int): tz_offset = -tz_offset self.apply_offset.emit(-offset, tz_offset)
class MultiLineEdit(QtWidgets.QPlainTextEdit): editingFinished = QtCore.pyqtSignal() def __init__(self, spell_check=False, *arg, **kw): super(MultiLineEdit, self).__init__(*arg, **kw) self.multiple_values = multiple_values() self.setTabChangesFocus(True) self._is_multiple = False if spell_check: self.spell_check = SpellingHighlighter(self.document()) else: self.spell_check = None def focusOutEvent(self, event): self.editingFinished.emit() super(MultiLineEdit, self).focusOutEvent(event) def contextMenuEvent(self, event): menu = self.createStandardContextMenu() suggestion_group = QtWidgets.QActionGroup(menu) if self.spell_check: cursor = self.cursorForPosition(event.pos()) cursor.select(QtGui.QTextCursor.WordUnderCursor) word = cursor.selectedText() suggestions = self.spell_check.suggestions(word) if suggestions: sep = menu.insertSeparator(menu.actions()[0]) for suggestion in suggestions: action = QtWidgets.QAction(suggestion, suggestion_group) menu.insertAction(sep, action) action = menu.exec_(event.globalPos()) if action and action.actionGroup() == suggestion_group: cursor = self.cursorForPosition(event.pos()) cursor.select(QtGui.QTextCursor.WordUnderCursor) cursor.insertText(action.text()) def set_value(self, value): self._is_multiple = False if not value: self.clear() if qt_version_info >= (5, 3): self.setPlaceholderText('') else: self.setPlainText(six.text_type(value)) def get_value(self): return self.toPlainText() def set_multiple(self): self._is_multiple = True if qt_version_info >= (5, 3): self.setPlaceholderText(self.multiple_values) self.clear() else: self.setPlainText(self.multiple_values) def is_multiple(self): return self._is_multiple and not bool(self.get_value())
class AuthServer(QtCore.QObject): finished = QtCore.pyqtSignal() response = QtCore.pyqtSignal(dict) @QtCore.pyqtSlot() @catch_all def handle_requests(self): self.server.timeout = 10 self.server.result = None # allow user 5 minutes to finish the process timeout = time.time() + 300 while time.time() < timeout: self.server.handle_request() if self.server.result: self.response.emit(self.server.result) break self.server.server_close() self.finished.emit()
class UploadWorker(QtCore.QObject): upload_progress = QtCore.pyqtSignal(float) upload_file_done = QtCore.pyqtSignal(object, six.text_type) def __init__(self, session_factory, *args, **kwds): super(UploadWorker, self).__init__(*args, **kwds) self.session_factory = session_factory self.session = None self.fileobj = None @QtCore.pyqtSlot() @catch_all def start(self): self.session = self.session_factory() self.session.connect() def abort_upload(self): if self.fileobj: self.fileobj.close() self.fileobj = None @QtCore.pyqtSlot(object, object, object) @catch_all def upload_file(self, image, convert, params): if convert: path = convert(image) else: path = image.path with open(path, 'rb') as f: self.fileobj = FileObjWithCallback(f, self.upload_progress.emit) try: error = self.session.do_upload(self.fileobj, imghdr.what(path), image, params) except Exception as ex: error = str(ex) if convert: os.unlink(path) if self.fileobj: self.fileobj = None self.upload_file_done.emit(image, error) else: # upload was aborted self.upload_file_done.emit(None, '')
class LocationWidgets(QtCore.QObject): new_value = QtCore.pyqtSignal(str, str) def __init__(self, *args, **kw): super(LocationWidgets, self).__init__(*args, **kw) self.members = { 'sublocation': SingleLineEdit(), 'city': SingleLineEdit(), 'province_state': SingleLineEdit(), 'country_name': SingleLineEdit(), 'country_code': SingleLineEdit(), 'world_region': SingleLineEdit(), } self.members['sublocation'].editingFinished.connect( self.new_sublocation) self.members['city'].editingFinished.connect(self.new_city) self.members['province_state'].editingFinished.connect( self.new_province_state) self.members['country_name'].editingFinished.connect( self.new_country_name) self.members['country_code'].editingFinished.connect( self.new_country_code) self.members['world_region'].editingFinished.connect( self.new_world_region) self.members['country_code'].setMaximumWidth(40) def __getitem__(self, key): return self.members[key] @QtCore.pyqtSlot() def new_sublocation(self): self.send_value('sublocation') @QtCore.pyqtSlot() def new_city(self): self.send_value('city') @QtCore.pyqtSlot() def new_province_state(self): self.send_value('province_state') @QtCore.pyqtSlot() def new_country_name(self): self.send_value('country_name') @QtCore.pyqtSlot() def new_country_code(self): self.send_value('country_code') @QtCore.pyqtSlot() def new_world_region(self): self.send_value('world_region') def send_value(self, key): self.new_value.emit(key, self.members[key].get_value())
class DateAndTimeWidget(QtWidgets.QGridLayout): new_value = QtCore.pyqtSignal(object) def __init__(self, *arg, **kw): super(DateAndTimeWidget, self).__init__(*arg, **kw) self.setContentsMargins(0, 0, 0, 0) self.setColumnStretch(3, 1) # date & time self.datetime = DateTimeEdit() self.datetime.setCalendarPopup(True) self.addWidget(self.datetime, 0, 0, 1, 2) # time zone self.time_zone = TimeZoneWidget() self.addWidget(self.time_zone, 0, 2) # precision self.addWidget(QtWidgets.QLabel(self.tr('Precision:')), 1, 0) self.precision = Slider(Qt.Horizontal) self.precision.setRange(0, 7) self.precision.setPageStep(1) self.addWidget(self.precision, 1, 1) # connections self.datetime.editingFinished.connect(self.new_datetime) self.time_zone.editingFinished.connect(self.new_time_zone) self.precision.valueChanged.connect(self.datetime.set_precision) self.precision.editing_finished.connect(self.new_precision) def get_value(self): return (self.datetime.get_value(), self.precision.value(), self.time_zone.get_value()) def set_value(self, date_time, precision, tz_offset): blocked = self.precision.blockSignals(True) self.precision.setValue(precision) self.precision.blockSignals(blocked) self.datetime.set_precision(precision) self.datetime.set_value(date_time) self.time_zone.set_value(tz_offset) def set_enabled(self, enabled): self.datetime.setEnabled(enabled) self.time_zone.setEnabled(enabled) self.precision.setEnabled(enabled) @QtCore.pyqtSlot() def new_precision(self): self.new_value.emit((MULTI, self.precision.value(), MULTI)) @QtCore.pyqtSlot() def new_datetime(self): self.new_value.emit(self.get_value()) @QtCore.pyqtSlot() def new_time_zone(self): self.new_value.emit((MULTI, MULTI, self.time_zone.get_value()))
class NameMangler(QtCore.QObject): number_parser = re.compile('\D*(\d+)') new_example = QtCore.pyqtSignal(str) def __init__(self, parent=None): super(NameMangler, self).__init__(parent) self.example = None self.format_string = None @QtCore.pyqtSlot(str) def new_format(self, format_string): self.format_string = format_string # extract bracket delimited words from string self.parts = [] while format_string: parts = format_string.split('(', 1) if len(parts) > 1: parts[1:] = parts[1].split(')', 1) if len(parts) < 3: self.parts.append((format_string, '')) break self.parts.append((parts[0], parts[1])) format_string = parts[2] self.refresh_example() def set_example(self, example): self.example = example self.refresh_example() def refresh_example(self): if self.format_string and self.example: self.new_example.emit(self.transform(self.example)) def transform(self, file_data): name = file_data['name'] subst = {'name': name} match = self.number_parser.match(name) if match: subst['number'] = match.group(1) else: subst['number'] = '' subst['root'], subst['ext'] = os.path.splitext(name) subst['camera'] = file_data['camera'] or 'unknown_camera' subst['camera'] = subst['camera'].replace(' ', '_') result = '' # process (...) parts first for left, right in self.parts: result += left if right in subst: result += subst[right] else: result += right # then do timestamp return file_data['timestamp'].strftime(result)
class DateLink(QtWidgets.QCheckBox): new_link = QtCore.pyqtSignal(six.text_type) def __init__(self, name, *arg, **kw): super(DateLink, self).__init__(*arg, **kw) self.name = name self.clicked.connect(self._clicked) @QtCore.pyqtSlot() @catch_all def _clicked(self): self.new_link.emit(self.name)
class GoogleUploadConfig(QtWidgets.QWidget): new_set = QtCore.pyqtSignal() def __init__(self, *arg, **kw): super(GoogleUploadConfig, self).__init__(*arg, **kw) self.setLayout(QtWidgets.QGridLayout()) self.layout().setContentsMargins(0, 0, 0, 0) # create new set new_set_button = QtWidgets.QPushButton( translate('GooglePhotosTab', 'New album')) new_set_button.clicked.connect(self.new_set) self.layout().addWidget(new_set_button, 2, 1) # list of sets widget sets_group = QtWidgets.QGroupBox( translate('GooglePhotosTab', 'Add to albums')) sets_group.setLayout(QtWidgets.QVBoxLayout()) scrollarea = QtWidgets.QScrollArea() scrollarea.setFrameStyle(QtWidgets.QFrame.NoFrame) scrollarea.setStyleSheet( "QScrollArea { background-color: transparent }") self.sets_widget = QtWidgets.QWidget() self.sets_widget.setLayout(QtWidgets.QVBoxLayout()) self.sets_widget.layout().setSpacing(0) self.sets_widget.layout().setSizeConstraint( QtWidgets.QLayout.SetMinAndMaxSize) scrollarea.setWidget(self.sets_widget) self.sets_widget.setAutoFillBackground(False) sets_group.layout().addWidget(scrollarea) self.layout().addWidget(sets_group, 0, 2, 3, 1) self.layout().setColumnStretch(2, 1) def clear_sets(self): for child in self.sets_widget.children(): if child.isWidgetType(): self.sets_widget.layout().removeWidget(child) child.setParent(None) def checked_albums(self): result = [] for child in self.sets_widget.children(): if child.isWidgetType() and child.isChecked(): result.append(child.property('id')) return result def add_album(self, album, index=-1): widget = QtWidgets.QCheckBox(album['title'].replace('&', '&&')) widget.setProperty('id', album['id']) widget.setEnabled(album['isWriteable']) if index >= 0: self.sets_widget.layout().insertWidget(index, widget) else: self.sets_widget.layout().addWidget(widget) return widget
class FileWriter(QtCore.QObject): output = QtCore.pyqtSignal(dict, object, six.text_type) @QtCore.pyqtSlot(dict, object, six.text_type) @catch_all def input(self, info, camera_file, status): if camera_file: try: camera_file.save(info['dest_path']) except gp.GPhoto2Error as ex: logger.error(str(ex)) status = str(ex) self.output.emit(info, camera_file, status)
class UploaderSession(QtCore.QObject): connection_changed = QtCore.pyqtSignal(bool) @QtCore.pyqtSlot() @catch_all def log_out(self): keyring.delete_password('photini', self.name) self.disconnect() def get_password(self): return keyring.get_password('photini', self.name) def set_password(self, password): keyring.set_password('photini', self.name, password)
class DropdownEdit(ComboBox): new_value = QtCore.pyqtSignal(object) def __init__(self, *arg, **kw): super(DropdownEdit, self).__init__(*arg, **kw) self.addItem(self.tr('<clear>'), None) self.addItem('', None) self.setItemData(1, 0, Qt.UserRole - 1) self.addItem(multiple_values(), None) self.setItemData(2, 0, Qt.UserRole - 1) self.currentIndexChanged.connect(self.current_index_changed) @QtCore.pyqtSlot(int) @catch_all def current_index_changed(self, int): self.new_value.emit(self.get_value()) def add_item(self, text, data): blocked = self.blockSignals(True) self.insertItem(self.count() - 3, text, six.text_type(data)) self.set_dropdown_width() self.blockSignals(blocked) def remove_item(self, data): blocked = self.blockSignals(True) self.removeItem(self.findData(six.text_type(data))) self.set_dropdown_width() self.blockSignals(blocked) def known_value(self, value): if not value: return True return self.findData(six.text_type(value)) >= 0 def set_value(self, value): blocked = self.blockSignals(True) if not value: self.setCurrentIndex(self.count() - 2) else: self.setCurrentIndex(self.findData(six.text_type(value))) self.blockSignals(blocked) def get_value(self): return self.itemData(self.currentIndex()) def set_multiple(self): blocked = self.blockSignals(True) self.setCurrentIndex(self.count() - 1) self.blockSignals(blocked)
class DateAndTimeWidget(QtWidgets.QGridLayout): new_value = QtCore.pyqtSignal(six.text_type, dict) def __init__(self, name, *arg, **kw): super(DateAndTimeWidget, self).__init__(*arg, **kw) self.name = name self.setVerticalSpacing(0) self.setColumnStretch(3, 1) self.members = {} # date & time self.members['datetime'] = DateTimeEdit() self.addWidget(self.members['datetime'], 0, 0, 1, 2) # time zone self.members['tz_offset'] = TimeZoneWidget() self.addWidget(self.members['tz_offset'], 0, 2) # precision self.addWidget( QtWidgets.QLabel(translate('TechnicalTab', 'Precision:')), 1, 0) self.members['precision'] = Slider(Qt.Horizontal) self.members['precision'].setRange(1, 7) self.members['precision'].setPageStep(1) self.addWidget(self.members['precision'], 1, 1) # connections self.members['precision'].valueChanged.connect( self.members['datetime'].set_precision) self.members['datetime'].editingFinished.connect(self.editing_finished) self.members['tz_offset'].editingFinished.connect( self.editing_finished) self.members['precision'].editing_finished.connect( self.editing_finished) def set_enabled(self, enabled): for widget in self.members.values(): widget.setEnabled(enabled) def get_value(self): new_value = {} for key in self.members: if self.members[key].is_multiple(): continue new_value[key] = self.members[key].get_value() if key == 'datetime' and new_value[key]: new_value[key] = new_value[key].toPyDateTime() return new_value @QtCore.pyqtSlot() @catch_all def editing_finished(self): self.new_value.emit(self.name, self.get_value())
class UploadWorker(QtCore.QObject): upload_progress = QtCore.pyqtSignal(float) upload_file_done = QtCore.pyqtSignal(object, str) def __init__(self, session_factory, params): super(UploadWorker, self).__init__() self.session = session_factory(auto_refresh=False) self.params = params self.fileobj = None self.thread = QtCore.QThread(self) self.moveToThread(self.thread) def abort_upload(self): if self.fileobj: self.fileobj.close() self.fileobj = None @QtCore.pyqtSlot(object, object) def upload_file(self, image, convert): if not self.session.permitted('write'): self.upload_file_done.emit(image, 'not permitted') return if convert: path = convert(image) else: path = image.path with open(path, 'rb') as f: self.fileobj = FileObjWithCallback(f, self.upload_progress.emit) error = self.session.do_upload(self.fileobj, imghdr.what(path), image, self.params) if convert: os.unlink(path) if self.fileobj: self.fileobj = None # upload wasn't aborted self.upload_file_done.emit(image, error)
class DropdownEdit(QtWidgets.QComboBox): new_value = QtCore.pyqtSignal() def __init__(self, *arg, **kw): super(DropdownEdit, self).__init__(*arg, **kw) self.addItem(self.tr('<clear>'), '<clear>') self.addItem('', None) self.setItemData(1, 0, Qt.UserRole - 1) self.addItem(multiple(), None) self.setItemData(2, 0, Qt.UserRole - 1) self.currentIndexChanged.connect(self._new_value) @QtCore.pyqtSlot(int) def _new_value(self, index): if index >= 0: self.new_value.emit() def add_item(self, text, data): blocked = self.blockSignals(True) self.insertItem(self.count() - 3, text, str(data)) self.blockSignals(blocked) def remove_item(self, data): blocked = self.blockSignals(True) self.removeItem(self.findData(str(data))) self.blockSignals(blocked) def known_value(self, value): if not value: return True return self.findData(str(value)) >= 0 def set_value(self, value): if not value: self.setCurrentIndex(self.count() - 2) else: self.setCurrentIndex(self.findData(str(value))) def get_value(self): return self.itemData(self.currentIndex()) def set_multiple(self): self.setCurrentIndex(self.count() - 1) def is_multiple(self): return self.currentIndex() == self.count() - 1
class LatLongDisplay(SingleLineEdit): changed = QtCore.pyqtSignal() def __init__(self, image_list, *args, **kwds): super(LatLongDisplay, self).__init__(*args, **kwds) self.image_list = image_list self.label = QtWidgets.QLabel(translate('MapTabsAll', 'Lat, long')) self.label.setAlignment(Qt.AlignRight) self.setFixedWidth(170) self.setEnabled(False) self.editingFinished.connect(self.editing_finished) @QtCore.pyqtSlot() @catch_all def editing_finished(self): text = self.get_value().strip() if text: try: new_value = list(map(float, text.split(','))) except Exception: # user typed in an invalid value self.refresh() return else: new_value = None for image in self.image_list.get_selected_images(): image.metadata.latlong = new_value self.refresh() self.changed.emit() def refresh(self): images = self.image_list.get_selected_images() if not images: self.set_value(None) self.setEnabled(False) return values = [] for image in images: value = image.metadata.latlong if value not in values: values.append(value) if len(values) > 1: self.set_multiple(choices=filter(None, values)) else: self.set_value(values[0]) self.setEnabled(True)
class AuthServer(QtCore.QObject): response = QtCore.pyqtSignal(dict) def __init__(self, *args, **kwds): super(AuthServer, self).__init__(*args, **kwds) self.server = HTTPServer(('127.0.0.1', 0), RequestHandler) self.port = self.server.server_port self.server.timeout = 1 self.server.response = self.response self.running = True @QtCore.pyqtSlot() @catch_all def start(self): while self.running: self.server.handle_request() self.server.server_close()
class FileReader(QtCore.QObject): output = QtCore.pyqtSignal(dict, object, six.text_type) def __init__(self, source, copy_list, *args, **kwds): super(FileReader, self).__init__(*args, **kwds) self.source = source self.copy_list = copy_list self.running = True @QtCore.pyqtSlot() @catch_all def start(self): status = 'ok' for info, camera_file, status in self.source.read_files(self.copy_list): if status != 'ok' or not self.running: break self.output.emit(info, camera_file, status) self.output.emit({}, None, status)
class WebView(WebViewBase): drop_text = QtCore.pyqtSignal(int, int, six.text_type) def dragEnterEvent(self, event): if not event.mimeData().hasFormat(DRAG_MIMETYPE): return super(WebView, self).dragEnterEvent(event) event.acceptProposedAction() def dragMoveEvent(self, event): if not event.mimeData().hasFormat(DRAG_MIMETYPE): return super(WebView, self).dragMoveEvent(event) def dropEvent(self, event): if not event.mimeData().hasFormat(DRAG_MIMETYPE): return super(WebView, self).dropEvent(event) text = event.mimeData().data(DRAG_MIMETYPE).data().decode('utf-8') if text: self.drop_text.emit(event.pos().x(), event.pos().y(), text)
class LocationInfo(QtWidgets.QWidget): new_value = QtCore.pyqtSignal(object, dict) def __init__(self, *args, **kw): super(LocationInfo, self).__init__(*args, **kw) layout = QtWidgets.QGridLayout() self.setLayout(layout) layout.setContentsMargins(0, 0, 0, 0) self.members = {} for key in ('sublocation', 'city', 'province_state', 'country_name', 'country_code', 'world_region'): self.members[key] = SingleLineEdit() self.members[key].editingFinished.connect(self.editing_finished) self.members['country_code'].setMaximumWidth(40) for j, text in enumerate(( translate('AddressTab', 'Street'), translate('AddressTab', 'City'), translate('AddressTab', 'Province'), translate('AddressTab', 'Country'), translate('AddressTab', 'Region'), )): label = QtWidgets.QLabel(text) label.setAlignment(Qt.AlignRight) layout.addWidget(label, j, 0) layout.addWidget(self.members['sublocation'], 0, 1, 1, 2) layout.addWidget(self.members['city'], 1, 1, 1, 2) layout.addWidget(self.members['province_state'], 2, 1, 1, 2) layout.addWidget(self.members['country_name'], 3, 1) layout.addWidget(self.members['country_code'], 3, 2) layout.addWidget(self.members['world_region'], 4, 1, 1, 2) layout.setRowStretch(5, 1) def get_value(self): new_value = {} for key in self.members: if self.members[key].is_multiple(): continue new_value[key] = self.members[key].get_value().strip() or None return new_value @QtCore.pyqtSlot() @catch_all def editing_finished(self): self.new_value.emit(self, self.get_value())
class SpellCheck(QtCore.QObject): new_dict = QtCore.pyqtSignal() def __init__(self, *arg, **kw): super(SpellCheck, self).__init__(*arg, **kw) self.config_store = QtWidgets.QApplication.instance().config_store self.enable(eval(self.config_store.get('spelling', 'enabled', 'True'))) self.set_dict(self.config_store.get('spelling', 'language')) @staticmethod def available_languages(): if not enchant: return [] result = enchant.list_languages() result.sort() return result def current_language(self): if self.dict: return self.dict.tag return '' @QtCore.pyqtSlot(bool) def enable(self, enabled): self.enabled = bool(enchant) and enabled self.config_store.set('spelling', 'enabled', str(self.enabled)) self.new_dict.emit() @QtCore.pyqtSlot(QtWidgets.QAction) def set_language(self, action): self.set_dict(action.text().replace('&', '')) def set_dict(self, tag): if tag: logger.info('Setting dictionary %s', tag) if tag and enchant and enchant.dict_exists(tag): self.dict = enchant.Dict(tag) else: if tag: logger.warning('Failed to set dictionary %s', tag) self.dict = None self.config_store.set('spelling', 'language', self.current_language()) self.new_dict.emit()
class ScrollArea(QtWidgets.QScrollArea): dropped_images = QtCore.pyqtSignal(list) def __init__(self, parent=None): super(ScrollArea, self).__init__(parent) self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn) self.setWidgetResizable(True) self.setAcceptDrops(True) def dropEvent(self, event): file_list = [] for uri in event.mimeData().urls(): file_list.append(uri.toLocalFile()) if file_list: self.dropped_images.emit(file_list) def dragEnterEvent(self, event): if event.mimeData().hasFormat('text/uri-list'): event.acceptProposedAction()
class NameMangler(QtCore.QObject): number_parser = re.compile('(\d+)') new_example = QtCore.pyqtSignal(str) def __init__(self, parent=None): super(NameMangler, self).__init__(parent) self.example = None self.format_string = None @QtCore.pyqtSlot(str) @catch_all def new_format(self, format_string): self.format_string = format_string self.refresh_example() def set_example(self, example): self.example = example self.refresh_example() def refresh_example(self): if self.format_string and self.example: self.new_example.emit(self.transform(self.example)) def transform(self, file_data): name = file_data['name'] subst = {'name': name} numbers = self.number_parser.findall(name) if numbers: subst['number'] = numbers[-1] else: subst['number'] = '' subst['root'], subst['ext'] = os.path.splitext(name) subst['camera'] = file_data['camera'] or 'unknown_camera' subst['camera'] = subst['camera'].replace(' ', '_') # process {...} parts first try: result = self.format_string.format(**subst) except (KeyError, ValueError): result = self.format_string # then do timestamp return file_data['timestamp'].strftime(result)
class FileCopier(QtCore.QObject): output = QtCore.pyqtSignal(dict, six.text_type) def __init__(self, source, copy_list, move, *args, **kwds): super(FileCopier, self).__init__(*args, **kwds) self.source = source self.copy_list = copy_list self.move = move self.running = True @QtCore.pyqtSlot() @catch_all def start(self): status = 'ok' try: for info in self.source.copy_files(self.copy_list, self.move): self.output.emit(info, status) if not self.running: break except Exception as ex: status = str(ex) logger.error(status) self.output.emit({}, status)
class OffsetWidget(QtWidgets.QWidget): apply_offset = QtCore.pyqtSignal(timedelta) def __init__(self, *arg, **kw): super(OffsetWidget, self).__init__(*arg, **kw) self.setLayout(QtWidgets.QHBoxLayout()) self.layout().setContentsMargins(0, 0, 0, 0) # offset value self.offset = QtWidgets.QTimeEdit() self.offset.setDisplayFormat("'h:'hh 'm:'mm 's:'ss") self.layout().addWidget(self.offset) # add offset button add_button = SquareButton('+') add_button.clicked.connect(self.add) self.layout().addWidget(add_button) # subtract offset button sub_button = SquareButton('-') sub_button.clicked.connect(self.sub) self.layout().addWidget(sub_button) self.layout().addStretch(1) @QtCore.pyqtSlot() def add(self): value = self.offset.time() offset = timedelta(hours=value.hour(), minutes=value.minute(), seconds=value.second()) self.apply_offset.emit(offset) @QtCore.pyqtSlot() def sub(self): value = self.offset.time() offset = timedelta(hours=value.hour(), minutes=value.minute(), seconds=value.second()) self.apply_offset.emit(-offset)
class SpellCheck(QtCore.QObject): new_dict = QtCore.pyqtSignal() def __init__(self, *arg, **kw): super(SpellCheck, self).__init__(*arg, **kw) self.config_store = QtWidgets.QApplication.instance().config_store self.enable(eval(self.config_store.get('spelling', 'enabled', 'True'))) self.set_language(self.config_store.get('spelling', 'language')) @staticmethod def available_languages(): result = [] if Gspell: for lang in Gspell.Language.get_available(): result.append((lang.get_name(), lang.get_code())) elif enchant: languages = enchant.list_languages() languages.sort() for lang in languages: result.append((lang, lang)) else: return None return result def current_language(self): if not self.dict: return '' if Gspell: language = self.dict.get_language() if language: return language.get_code() elif enchant: return self.dict.tag return '' @QtCore.pyqtSlot(bool) @catch_all def enable(self, enabled): self.enabled = enabled and bool(Gspell or enchant) self.config_store.set('spelling', 'enabled', str(self.enabled)) self.new_dict.emit() def set_language(self, code): if code: logger.debug('Setting dictionary %s', code) self.dict = None if Gspell: if code: self.dict = Gspell.Checker.new(Gspell.Language.lookup(code)) elif enchant: if code and enchant.dict_exists(code): self.dict = enchant.Dict(code) if code and not self.dict: logger.warning('Failed to set dictionary %s', code) self.config_store.set('spelling', 'language', self.current_language()) self.new_dict.emit() words = re.compile(r"\w+([-'’]\w+)*", flags=re.IGNORECASE | re.UNICODE) def find_words(self, text): for word in self.words.finditer(text): yield word.group(), word.start(), word.end() def check(self, word): if not (word and self.enabled and self.dict): return True if Gspell: return self.dict.check_word(word, -1) if enchant: return self.dict.check(word) return True def suggest(self, word): if self.check(word): return [] if Gspell: return GSListPtr_to_list(self.dict.get_suggestions(word, -1)) if enchant: return self.dict.suggest(word) return []