예제 #1
0
 def __init__(self, upload_config_widget, image_list, *arg, **kw):
     super(PhotiniUploader, self).__init__(*arg, **kw)
     self.app = QtWidgets.QApplication.instance()
     self.app.aboutToQuit.connect(self.shutdown)
     logger.debug('using %s', keyring.get_keyring().__module__)
     self.image_list = image_list
     self.setLayout(QtWidgets.QGridLayout())
     self.session = self.session_factory()
     self.session.connection_changed.connect(self.connection_changed)
     self.upload_worker = None
     # user details
     self.user = {}
     user_group = QtWidgets.QGroupBox(translate('UploaderTabsAll', 'User'))
     user_group.setLayout(QtWidgets.QVBoxLayout())
     self.user_photo = QtWidgets.QLabel()
     self.user_photo.setAlignment(Qt.AlignHCenter | Qt.AlignTop)
     user_group.layout().addWidget(self.user_photo)
     self.user_name = QtWidgets.QLabel()
     self.user_name.setWordWrap(True)
     self.user_name.setFixedWidth(80)
     user_group.layout().addWidget(self.user_name)
     user_group.layout().addStretch(1)
     self.layout().addWidget(user_group, 0, 0, 1, 2)
     # connect / disconnect button
     self.user_connect = StartStopButton(
         translate('UploaderTabsAll', 'Log in'),
         translate('UploaderTabsAll', 'Log out'))
     self.user_connect.click_start.connect(self.log_in)
     self.user_connect.click_stop.connect(self.session.log_out)
     self.layout().addWidget(self.user_connect, 1, 0, 1, 2)
     # 'service' specific widget
     self.layout().addWidget(upload_config_widget, 0, 2, 2, 2)
     # upload button
     self.upload_button = StartStopButton(
         translate('UploaderTabsAll', 'Start upload'),
         translate('UploaderTabsAll', 'Stop upload'))
     self.upload_button.setEnabled(False)
     self.upload_button.click_start.connect(self.start_upload)
     self.upload_button.click_stop.connect(self.stop_upload)
     self.layout().addWidget(self.upload_button, 2, 3)
     # progress bar
     self.layout().addWidget(
         QtWidgets.QLabel(translate('UploaderTabsAll', 'Progress')), 2, 0)
     self.total_progress = QtWidgets.QProgressBar()
     self.layout().addWidget(self.total_progress, 2, 1, 1, 2)
     # adjust spacing
     self.layout().setColumnStretch(2, 1)
     self.layout().setRowStretch(0, 1)
     # initialise as not connected
     self.connection_changed(False)
예제 #2
0
 def __init__(self, upload_config_widget, image_list, *arg, **kw):
     super(PhotiniUploader, self).__init__(*arg, **kw)
     QtWidgets.QApplication.instance().aboutToQuit.connect(self.shutdown)
     self.logger = logging.getLogger(self.__class__.__name__)
     self.image_list = image_list
     self.setLayout(QtWidgets.QGridLayout())
     self.session = self.session_factory()
     self.upload_worker = None
     self.connected = False
     # user details
     self.user = {}
     user_group = QtWidgets.QGroupBox(self.tr('User'))
     user_group.setLayout(QtWidgets.QVBoxLayout())
     self.user_photo = QtWidgets.QLabel()
     self.user_photo.setAlignment(Qt.AlignHCenter | Qt.AlignTop)
     user_group.layout().addWidget(self.user_photo)
     self.user_name = QtWidgets.QLabel()
     self.user_name.setWordWrap(True)
     self.user_name.setFixedWidth(80)
     user_group.layout().addWidget(self.user_name)
     user_group.layout().addStretch(1)
     self.layout().addWidget(user_group, 0, 0, 1, 2)
     # connect / disconnect button
     self.user_connect = QtWidgets.QPushButton()
     self.user_connect.setCheckable(True)
     self.user_connect.clicked.connect(self.connect_user)
     self.layout().addWidget(self.user_connect, 1, 0, 1, 2)
     # 'service' specific widget
     self.layout().addWidget(upload_config_widget, 0, 2, 2, 2)
     # upload button
     self.upload_button = StartStopButton(self.tr('Start upload'),
                                          self.tr('Stop upload'))
     self.upload_button.setEnabled(False)
     self.upload_button.click_start.connect(self.start_upload)
     self.upload_button.click_stop.connect(self.stop_upload)
     self.layout().addWidget(self.upload_button, 2, 3)
     # progress bar
     self.layout().addWidget(QtWidgets.QLabel(self.tr('Progress')), 2, 0)
     self.total_progress = QtWidgets.QProgressBar()
     self.layout().addWidget(self.total_progress, 2, 1, 1, 2)
     # adjust spacing
     self.layout().setColumnStretch(2, 1)
     self.layout().setRowStretch(0, 1)
예제 #3
0
 def __init__(self, image_list, parent=None):
     super(TabWidget, self).__init__(parent)
     app = QtWidgets.QApplication.instance()
     app.aboutToQuit.connect(self.shutdown)
     if gp and app.test_mode:
         self.gp_log = gp.check_result(gp.use_python_logging())
     self.config_store = app.config_store
     self.image_list = image_list
     self.setLayout(QtWidgets.QGridLayout())
     form = QtWidgets.QFormLayout()
     form.setFieldGrowthPolicy(QtWidgets.QFormLayout.AllNonFixedFieldsGrow)
     self.nm = NameMangler()
     self.file_data = {}
     self.file_list = []
     self.source = None
     self.file_reader = None
     self.file_writer = None
     # source selector
     box = QtWidgets.QHBoxLayout()
     box.setContentsMargins(0, 0, 0, 0)
     self.source_selector = QtWidgets.QComboBox()
     self.source_selector.currentIndexChanged.connect(self.new_source)
     box.addWidget(self.source_selector)
     refresh_button = QtWidgets.QPushButton(self.tr('refresh'))
     refresh_button.clicked.connect(self.refresh)
     box.addWidget(refresh_button)
     box.setStretch(0, 1)
     form.addRow(self.tr('Source'), box)
     # path format
     self.path_format = QtWidgets.QLineEdit()
     self.path_format.setValidator(PathFormatValidator())
     self.path_format.textChanged.connect(self.nm.new_format)
     self.path_format.editingFinished.connect(self.path_format_finished)
     form.addRow(self.tr('Target format'), self.path_format)
     # path example
     self.path_example = QtWidgets.QLabel()
     self.nm.new_example.connect(self.path_example.setText)
     form.addRow('=>', self.path_example)
     self.layout().addLayout(form, 0, 0)
     # file list
     self.file_list_widget = QtWidgets.QListWidget()
     self.file_list_widget.setSelectionMode(
         QtWidgets.QAbstractItemView.ExtendedSelection)
     self.file_list_widget.itemSelectionChanged.connect(self.selection_changed)
     self.layout().addWidget(self.file_list_widget, 1, 0)
     # selection buttons
     buttons = QtWidgets.QVBoxLayout()
     buttons.addStretch(1)
     self.selected_count = QtWidgets.QLabel()
     self.selection_changed()
     buttons.addWidget(self.selected_count)
     select_all = QtWidgets.QPushButton(self.tr('Select\nall'))
     select_all.clicked.connect(self.select_all)
     buttons.addWidget(select_all)
     select_new = QtWidgets.QPushButton(self.tr('Select\nnew'))
     select_new.clicked.connect(self.select_new)
     buttons.addWidget(select_new)
     self.copy_button = StartStopButton(self.tr('Copy\nphotos'),
                                        self.tr('Stop\nimport'))
     self.copy_button.click_start.connect(self.copy_selected)
     self.copy_button.click_stop.connect(self.stop_copy)
     buttons.addWidget(self.copy_button)
     self.layout().addLayout(buttons, 0, 1, 2, 1)
     # final initialisation
     self.image_list.sort_order_changed.connect(self.sort_file_list)
     path = os.path.expanduser('~/Pictures')
     if not os.path.isdir(path) and sys.platform == 'win32':
         try:
             import win32com.shell as ws
             path = ws.shell.SHGetFolderPath(
                 0, ws.shellcon.CSIDL_MYPICTURES, None, 0)
         except ImportError:
             pass
     self.path_format.setText(
         os.path.join(path, '%Y', '%Y_%m_%d', '{name}'))
     self.refresh()
     self.list_files()
예제 #4
0
class TabWidget(QtWidgets.QWidget):
    @staticmethod
    def tab_name():
        return QtCore.QCoreApplication.translate('TabWidget', '&Import photos')

    def __init__(self, image_list, parent=None):
        super(TabWidget, self).__init__(parent)
        app = QtWidgets.QApplication.instance()
        app.aboutToQuit.connect(self.shutdown)
        if gp and app.test_mode:
            self.gp_log = gp.check_result(gp.use_python_logging())
        self.config_store = app.config_store
        self.image_list = image_list
        self.setLayout(QtWidgets.QGridLayout())
        form = QtWidgets.QFormLayout()
        form.setFieldGrowthPolicy(QtWidgets.QFormLayout.AllNonFixedFieldsGrow)
        self.nm = NameMangler()
        self.file_data = {}
        self.file_list = []
        self.source = None
        self.file_reader = None
        self.file_writer = None
        # source selector
        box = QtWidgets.QHBoxLayout()
        box.setContentsMargins(0, 0, 0, 0)
        self.source_selector = QtWidgets.QComboBox()
        self.source_selector.currentIndexChanged.connect(self.new_source)
        box.addWidget(self.source_selector)
        refresh_button = QtWidgets.QPushButton(self.tr('refresh'))
        refresh_button.clicked.connect(self.refresh)
        box.addWidget(refresh_button)
        box.setStretch(0, 1)
        form.addRow(self.tr('Source'), box)
        # path format
        self.path_format = QtWidgets.QLineEdit()
        self.path_format.setValidator(PathFormatValidator())
        self.path_format.textChanged.connect(self.nm.new_format)
        self.path_format.editingFinished.connect(self.path_format_finished)
        form.addRow(self.tr('Target format'), self.path_format)
        # path example
        self.path_example = QtWidgets.QLabel()
        self.nm.new_example.connect(self.path_example.setText)
        form.addRow('=>', self.path_example)
        self.layout().addLayout(form, 0, 0)
        # file list
        self.file_list_widget = QtWidgets.QListWidget()
        self.file_list_widget.setSelectionMode(
            QtWidgets.QAbstractItemView.ExtendedSelection)
        self.file_list_widget.itemSelectionChanged.connect(self.selection_changed)
        self.layout().addWidget(self.file_list_widget, 1, 0)
        # selection buttons
        buttons = QtWidgets.QVBoxLayout()
        buttons.addStretch(1)
        self.selected_count = QtWidgets.QLabel()
        self.selection_changed()
        buttons.addWidget(self.selected_count)
        select_all = QtWidgets.QPushButton(self.tr('Select\nall'))
        select_all.clicked.connect(self.select_all)
        buttons.addWidget(select_all)
        select_new = QtWidgets.QPushButton(self.tr('Select\nnew'))
        select_new.clicked.connect(self.select_new)
        buttons.addWidget(select_new)
        self.copy_button = StartStopButton(self.tr('Copy\nphotos'),
                                           self.tr('Stop\nimport'))
        self.copy_button.click_start.connect(self.copy_selected)
        self.copy_button.click_stop.connect(self.stop_copy)
        buttons.addWidget(self.copy_button)
        self.layout().addLayout(buttons, 0, 1, 2, 1)
        # final initialisation
        self.image_list.sort_order_changed.connect(self.sort_file_list)
        path = os.path.expanduser('~/Pictures')
        if not os.path.isdir(path) and sys.platform == 'win32':
            try:
                import win32com.shell as ws
                path = ws.shell.SHGetFolderPath(
                    0, ws.shellcon.CSIDL_MYPICTURES, None, 0)
            except ImportError:
                pass
        self.path_format.setText(
            os.path.join(path, '%Y', '%Y_%m_%d', '{name}'))
        self.refresh()
        self.list_files()

    @QtCore.pyqtSlot(int)
    @catch_all
    def new_source(self, idx):
        self.source = None
        item_data = self.source_selector.itemData(idx)
        if callable(item_data):
            # a special 'source' that's actually a method to call
            (item_data)()
            return
        # select new source
        self.source, self.config_section = item_data
        path_format = self.path_format.text()
        path_format = self.config_store.get(
            self.config_section, 'path_format', path_format)
        path_format = path_format.replace('(', '{').replace(')', '}')
        self.path_format.setText(path_format)
        self.file_list_widget.clear()
        # allow 100ms for display to update before getting file list
        QtCore.QTimer.singleShot(100, self.list_files)

    def add_folder(self):
        folders = eval(self.config_store.get('importer', 'folders', '[]'))
        if folders:
            directory = folders[0]
        else:
            directory = ''
        root = QtWidgets.QFileDialog.getExistingDirectory(
            self, self.tr("Select root folder"), directory)
        if not root:
            self._fail()
            return
        if root in folders:
            folders.remove(root)
        folders.insert(0, root)
        if len(folders) > 5:
            del folders[-1]
        self.config_store.set('importer', 'folders', repr(folders))
        self.refresh()
        idx = self.source_selector.count() - (1 + len(folders))
        self.source_selector.setCurrentIndex(idx)

    @QtCore.pyqtSlot()
    @catch_all
    def path_format_finished(self):
        if self.source:
            self.config_store.set(
                self.config_section, 'path_format', self.nm.format_string)
        self.show_file_list()

    @QtCore.pyqtSlot()
    @catch_all
    def refresh(self):
        was_blocked = self.source_selector.blockSignals(True)
        # save current selection
        idx = self.source_selector.currentIndex()
        if idx >= 0:
            old_item_data = self.source_selector.itemData(idx)
        else:
            old_item_data = None
        # rebuild list
        self.source_selector.clear()
        self.source_selector.addItem(
            self.tr('<select source>'), self._new_file_list)
        for model, port_name in get_camera_list():
            self.source_selector.addItem(
                self.tr('camera: {0}').format(model),
                (CameraSource(model, port_name), 'importer ' + model))
        for root in eval(self.config_store.get('importer', 'folders', '[]')):
            if os.path.isdir(root):
                self.source_selector.addItem(
                    self.tr('folder: {0}').format(root),
                    (FolderSource(root), 'importer folder ' + root))
        self.source_selector.addItem(self.tr('<add a folder>'), self.add_folder)
        # restore saved selection
        new_idx = -1
        for idx in range(self.source_selector.count()):
            item_data = self.source_selector.itemData(idx)
            if item_data == old_item_data:
                new_idx = idx
                self.source_selector.setCurrentIndex(idx)
                break
        self.source_selector.blockSignals(was_blocked)
        if new_idx < 0:
            self.source_selector.setCurrentIndex(0)

    def do_not_close(self):
        if not self.file_reader:
            return False
        dialog = QtWidgets.QMessageBox()
        dialog.setWindowTitle(self.tr('Photini: import in progress'))
        dialog.setText(self.tr('<h3>Importing photos has not finished.</h3>'))
        dialog.setInformativeText(
            self.tr('Closing now will terminate the import.'))
        dialog.setIcon(QtWidgets.QMessageBox.Warning)
        dialog.setStandardButtons(
            QtWidgets.QMessageBox.Close | QtWidgets.QMessageBox.Cancel)
        dialog.setDefaultButton(QtWidgets.QMessageBox.Cancel)
        result = dialog.exec_()
        return result == QtWidgets.QMessageBox.Cancel

    @QtCore.pyqtSlot(list)
    def new_selection(self, selection):
        pass

    def list_files(self):
        file_data = {}
        if self.source:
            with Busy():
                file_data = self.source.get_file_data()
                if file_data is None:
                    self._fail()
                    return
        self._new_file_list(file_data)

    def _fail(self):
        self.source_selector.setCurrentIndex(0)
        self.refresh()

    def _new_file_list(self, file_data={}):
        self.file_list = list(file_data.keys())
        self.file_data = file_data
        self.sort_file_list()

    @QtCore.pyqtSlot()
    @catch_all
    def sort_file_list(self):
        if eval(self.config_store.get('controls', 'sort_date', 'False')):
            self.file_list.sort(key=lambda x: self.file_data[x]['timestamp'])
        else:
            self.file_list.sort()
        self.show_file_list()
        if self.file_list:
            example = self.file_data[self.file_list[-1]]
        else:
            example = {
                'camera'    : None,
                'name'      : 'IMG_9999.JPG',
                'timestamp' : datetime.now(),
                }
        self.nm.set_example(example)

    def show_file_list(self):
        self.file_list_widget.clear()
        first_active = None
        item = None
        for name in self.file_list:
            file_data = self.file_data[name]
            dest_path = self.nm.transform(file_data)
            file_data['dest_path'] = dest_path
            item = QtWidgets.QListWidgetItem(name + ' -> ' + dest_path)
            item.setData(Qt.UserRole, name)
            if os.path.exists(dest_path):
                item.setFlags(Qt.NoItemFlags)
            else:
                if not first_active:
                    first_active = item
                item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
            self.file_list_widget.addItem(item)
        if not first_active:
            first_active = item
        self.file_list_widget.scrollToItem(
            first_active, QtWidgets.QAbstractItemView.PositionAtTop)

    @QtCore.pyqtSlot()
    @catch_all
    def selection_changed(self):
        count = len(self.file_list_widget.selectedItems())
        self.selected_count.setText(self.tr('%n file(s)\nselected', '', count))

    @QtCore.pyqtSlot()
    @catch_all
    def select_all(self):
        self.select_files(datetime.min)

    @QtCore.pyqtSlot()
    @catch_all
    def select_new(self):
        since = datetime.min
        if self.source:
            since = self.config_store.get(
                self.config_section, 'last_transfer', since.isoformat(' '))
            if len(since) > 19:
                since = datetime.strptime(since, '%Y-%m-%d %H:%M:%S.%f')
            else:
                since = datetime.strptime(since, '%Y-%m-%d %H:%M:%S')
        self.select_files(since)

    def select_files(self, since):
        count = self.file_list_widget.count()
        if not count:
            return
        self.file_list_widget.clearSelection()
        first_active = None
        for row in range(count):
            item = self.file_list_widget.item(row)
            if not (item.flags() & Qt.ItemIsSelectable):
                continue
            name = item.data(Qt.UserRole)
            timestamp = self.file_data[name]['timestamp']
            if timestamp > since:
                if not first_active:
                    first_active = item
                item.setSelected(True)
        if not first_active:
            first_active = item
        self.file_list_widget.scrollToItem(
            first_active, QtWidgets.QAbstractItemView.PositionAtTop)

    @QtCore.pyqtSlot()
    @catch_all
    def copy_selected(self):
        copy_list = []
        for item in self.file_list_widget.selectedItems():
            name = item.data(Qt.UserRole)
            copy_list.append(self.file_data[name])
        if not copy_list:
            return
        self.copy_button.set_checked(True)
        self.last_file_copied = None, datetime.min
        # start file writer in a thread
        self.file_writer = FileWriter()
        self.file_writer_thread = QtCore.QThread(self)
        self.file_writer.moveToThread(self.file_writer_thread)
        self.file_writer.output.connect(self.file_copied)
        self.file_writer_thread.start()
        # start file reader in another thread
        self.file_reader = FileReader(self.source, copy_list)
        self.file_reader_thread = QtCore.QThread(self)
        self.file_reader.moveToThread(self.file_reader_thread)
        self.file_reader.output.connect(self.file_writer.input)
        self.file_reader_thread.started.connect(self.file_reader.start)
        self.file_reader_thread.start()

    @QtCore.pyqtSlot(dict, object, six.text_type)
    @catch_all
    def file_copied(self, info, camera_file, status):
        if info:
            self.image_list.open_file(info['dest_path'])
            if self.last_file_copied[1] < info['timestamp']:
                self.last_file_copied = info['dest_path'], info['timestamp']
            return
        self.copy_button.set_checked(False)
        self.file_reader = None
        self.file_writer = None
        self.file_reader_thread.quit()
        self.file_writer_thread.quit()
        if self.last_file_copied[0]:
            self.config_store.set(self.config_section, 'last_transfer',
                                  self.last_file_copied[1].isoformat(' '))
            self.image_list.done_opening(self.last_file_copied[0])
        if status != 'ok':
            self._fail()
        self.show_file_list()

    @QtCore.pyqtSlot()
    @catch_all
    def stop_copy(self):
        if self.file_reader:
            self.file_reader.running = False

    @QtCore.pyqtSlot()
    @catch_all
    def shutdown(self):
        if self.file_reader:
            self.file_reader.running = False
            self.file_reader_thread.quit()
            self.file_writer_thread.quit()
            self.file_reader_thread.wait()
            self.file_writer_thread.wait()
예제 #5
0
class PhotiniUploader(QtWidgets.QWidget):
    upload_file = QtCore.pyqtSignal(object, object)

    def __init__(self, upload_config_widget, image_list, *arg, **kw):
        super(PhotiniUploader, self).__init__(*arg, **kw)
        QtWidgets.QApplication.instance().aboutToQuit.connect(self.shutdown)
        self.logger = logging.getLogger(self.__class__.__name__)
        self.image_list = image_list
        self.setLayout(QtWidgets.QGridLayout())
        self.session = self.session_factory()
        self.upload_worker = None
        self.connected = False
        # user details
        self.user = {}
        user_group = QtWidgets.QGroupBox(self.tr('User'))
        user_group.setLayout(QtWidgets.QVBoxLayout())
        self.user_photo = QtWidgets.QLabel()
        self.user_photo.setAlignment(Qt.AlignHCenter | Qt.AlignTop)
        user_group.layout().addWidget(self.user_photo)
        self.user_name = QtWidgets.QLabel()
        self.user_name.setWordWrap(True)
        self.user_name.setFixedWidth(80)
        user_group.layout().addWidget(self.user_name)
        user_group.layout().addStretch(1)
        self.layout().addWidget(user_group, 0, 0, 1, 2)
        # connect / disconnect button
        self.user_connect = QtWidgets.QPushButton()
        self.user_connect.setCheckable(True)
        self.user_connect.clicked.connect(self.connect_user)
        self.layout().addWidget(self.user_connect, 1, 0, 1, 2)
        # 'service' specific widget
        self.layout().addWidget(upload_config_widget, 0, 2, 2, 2)
        # upload button
        self.upload_button = StartStopButton(self.tr('Start upload'),
                                             self.tr('Stop upload'))
        self.upload_button.setEnabled(False)
        self.upload_button.click_start.connect(self.start_upload)
        self.upload_button.click_stop.connect(self.stop_upload)
        self.layout().addWidget(self.upload_button, 2, 3)
        # progress bar
        self.layout().addWidget(QtWidgets.QLabel(self.tr('Progress')), 2, 0)
        self.total_progress = QtWidgets.QProgressBar()
        self.layout().addWidget(self.total_progress, 2, 1, 1, 2)
        # adjust spacing
        self.layout().setColumnStretch(2, 1)
        self.layout().setRowStretch(0, 1)

    @QtCore.pyqtSlot()
    def shutdown(self):
        if self.upload_worker:
            self.upload_worker.abort_upload()
            self.upload_worker.thread.quit()
            self.upload_worker.thread.wait()

    def refresh(self, force=False):
        with Busy():
            self.connected = (self.user_connect.isChecked()
                              and self.session.permitted('read'))
            if self.connected:
                self.user_connect.setText(self.tr('Log out'))
                if force:
                    # load_user_data can be slow, so only do it when forced
                    try:
                        self.load_user_data()
                    except Exception as ex:
                        self.logger.error(ex)
                        self.connected = False
            if not self.connected:
                self.user_connect.setText(self.tr('Connect'))
                # clearing user data is quick so do it anyway
                self.load_user_data()
            self.user_connect.setChecked(self.connected)
            self.upload_config.setEnabled(self.connected
                                          and not self.upload_worker)
            self.user_connect.setEnabled(not self.upload_worker)
            # enable or disable upload button
            self.new_selection(self.image_list.get_selected_images())

    @QtCore.pyqtSlot(bool)
    def connect_user(self, connect):
        if connect:
            self.authorise('read')
        else:
            self.session.log_out()
        self.refresh(force=True)

    def do_not_close(self):
        if not self.upload_worker:
            return False
        dialog = QtWidgets.QMessageBox(parent=self)
        dialog.setWindowTitle(self.tr('Photini: upload in progress'))
        dialog.setText(
            self.tr('<h3>Upload to {} has not finished.</h3>').format(
                self.service_name))
        dialog.setInformativeText(
            self.tr('Closing now will terminate the upload.'))
        dialog.setIcon(QtWidgets.QMessageBox.Warning)
        dialog.setStandardButtons(QtWidgets.QMessageBox.Close
                                  | QtWidgets.QMessageBox.Cancel)
        dialog.setDefaultButton(QtWidgets.QMessageBox.Cancel)
        result = dialog.exec_()
        return result == QtWidgets.QMessageBox.Cancel

    def show_user(self, name, picture):
        if name:
            self.user_name.setText(
                self.tr('Connected to {0} on {1}').format(
                    name, self.service_name))
        else:
            self.user_name.setText(
                self.tr('Not connected to {}').format(self.service_name))
        pixmap = QtGui.QPixmap()
        if picture:
            pixmap.loadFromData(picture)
        self.user_photo.setPixmap(pixmap)

    def get_temp_filename(self, image, ext='.jpg'):
        temp_dir = appdirs.user_cache_dir('photini')
        if not os.path.isdir(temp_dir):
            os.makedirs(temp_dir)
        return os.path.join(temp_dir, os.path.basename(image.path) + ext)

    def copy_metadata(self, image, path):
        # copy metadata, forcing IPTC creation
        md = Metadata(path, None)
        md.copy(image.metadata)
        md.save(True, 'none', True)

    def convert_to_jpeg(self, image):
        im = QtGui.QImage(image.path)
        path = self.get_temp_filename(image)
        im.save(path, format='jpeg', quality=95)
        self.copy_metadata(image, path)
        return path

    def copy_file_and_metadata(self, image):
        path = self.get_temp_filename(image, ext='')
        shutil.copyfile(image.path, path)
        self.copy_metadata(image, path)
        return path

    def is_convertible(self, image):
        file_type = image.file_type.split('/')
        if file_type[0] != 'image':
            # can only convert images
            return False
        if 'raw' in file_type[1]:
            # can't convert raw files
            return False
        if image.pixmap.isNull():
            # if Qt can't read it, we can't convert it
            return False
        return True

    def get_conversion_function(self, image):
        if image.file_type in self.image_types['accepted']:
            if image.metadata._sc or not image.metadata.has_iptc():
                # need to create file without sidecar and with IPTC
                return self.copy_file_and_metadata
            return None
        if not self.is_convertible(image):
            msg = self.tr('File "{0}" is of type "{1}", which {2} does not' +
                          ' accept and Photini cannot convert.')
            buttons = QtWidgets.QMessageBox.Ignore
        elif (self.image_types['rejected'] == '*'
              or image.file_type in self.image_types['rejected']):
            msg = self.tr('File "{0}" is of type "{1}", which {2} does not' +
                          ' accept. Would you like to convert it to JPEG?')
            buttons = QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.Ignore
        else:
            msg = self.tr(
                'File "{0}" is of type "{1}", which {2} may not' +
                ' handle correctly. Would you like to convert it to JPEG?')
            buttons = QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No
        dialog = QtWidgets.QMessageBox(parent=self)
        dialog.setWindowTitle(self.tr('Photini: incompatible type'))
        dialog.setText(self.tr('<h3>Incompatible image type.</h3>'))
        dialog.setInformativeText(
            msg.format(os.path.basename(image.path), image.file_type,
                       self.service_name))
        dialog.setIcon(QtWidgets.QMessageBox.Warning)
        dialog.setStandardButtons(buttons)
        dialog.setDefaultButton(QtWidgets.QMessageBox.Yes)
        result = dialog.exec_()
        if result == QtWidgets.QMessageBox.Ignore:
            return 'omit'
        if result == QtWidgets.QMessageBox.Yes:
            return self.convert_to_jpeg
        return None

    @QtCore.pyqtSlot()
    def stop_upload(self):
        if self.upload_worker:
            # invoke worker method in this thread as worker thread is busy
            self.upload_worker.abort_upload()
            # reset GUI
            self.upload_file_done(None, '')

    @QtCore.pyqtSlot()
    def start_upload(self):
        if not self.image_list.unsaved_files_dialog(with_discard=False):
            self.upload_button.setChecked(False)
            return
        # make list of items to upload
        self.upload_list = []
        for image in self.image_list.get_selected_images():
            convert = self.get_conversion_function(image)
            if convert == 'omit':
                continue
            self.upload_list.append((image, convert))
        if not self.upload_list:
            self.upload_button.setChecked(False)
            return
        if not self.authorise('write'):
            self.refresh(force=True)
            self.upload_button.setChecked(False)
            return
        # start uploading in separate thread, so GUI can continue
        self.upload_worker = UploadWorker(self.session_factory,
                                          self.get_upload_params())
        self.upload_file.connect(self.upload_worker.upload_file)
        self.upload_worker.upload_progress.connect(
            self.total_progress.setValue)
        self.upload_worker.upload_file_done.connect(self.upload_file_done)
        self.upload_worker.thread.start()
        self.upload_config.setEnabled(False)
        self.user_connect.setEnabled(False)
        self.uploads_done = 0
        self.next_upload()

    def next_upload(self):
        image, convert = self.upload_list[self.uploads_done]
        self.total_progress.setFormat('{} ({}/{}) %p%'.format(
            os.path.basename(image.path), 1 + self.uploads_done,
            len(self.upload_list)))
        self.total_progress.setValue(0)
        QtWidgets.QApplication.processEvents()
        self.upload_file.emit(image, convert)

    @QtCore.pyqtSlot(object, str)
    def upload_file_done(self, image, error):
        if error:
            dialog = QtWidgets.QMessageBox(self)
            dialog.setWindowTitle(self.tr('Photini: upload error'))
            dialog.setText(
                self.tr('<h3>File "{}" upload failed.</h3>').format(
                    os.path.basename(image.path)))
            dialog.setInformativeText(error)
            dialog.setIcon(QtWidgets.QMessageBox.Warning)
            dialog.setStandardButtons(QtWidgets.QMessageBox.Abort
                                      | QtWidgets.QMessageBox.Retry)
            dialog.setDefaultButton(QtWidgets.QMessageBox.Retry)
            if dialog.exec_() == QtWidgets.QMessageBox.Abort:
                self.upload_button.setChecked(False)
        else:
            self.uploads_done += 1
        if (self.upload_button.isChecked()
                and self.uploads_done < len(self.upload_list)):
            # start uploading next file (or retry same file)
            self.next_upload()
            return
        self.upload_button.setChecked(False)
        self.total_progress.setValue(0)
        self.total_progress.setFormat('%p%')
        self.upload_config.setEnabled(True)
        self.user_connect.setEnabled(True)
        self.upload_finished()
        self.upload_file.disconnect()
        self.upload_worker.upload_progress.disconnect()
        self.upload_worker.upload_file_done.disconnect()
        self.upload_worker.thread.quit()
        self.upload_worker.thread.wait()
        self.upload_worker = None
        # enable or disable upload button
        self.new_selection(self.image_list.get_selected_images())

    def auth_dialog(self, auth_url):
        if webbrowser.open(auth_url, new=2, autoraise=0):
            info_text = self.tr('use your web browser')
        else:
            info_text = self.tr('open "{0}" in a web browser').format(auth_url)
        auth_code, OK = QtWidgets.QInputDialog.getText(
            self,
            self.tr('Photini: authorise {}').format(self.service_name),
            self.tr("""Please {0} to grant access to Photini,
then enter the verification code:""").format(info_text))
        if OK:
            return six.text_type(auth_code).strip()
        return None

    def authorise(self, level):
        with Busy():
            if self.session.permitted(level):
                return True
            # do full authentication procedure
            auth_url = self.session.get_auth_url(level)
        auth_code = self.auth_dialog(auth_url)
        if not auth_code:
            return False
        with Busy():
            self.session.get_access_token(auth_code)
            return self.session.permitted(level)

    @QtCore.pyqtSlot(list)
    def new_selection(self, selection):
        self.upload_button.setEnabled(self.upload_button.isChecked() or
                                      (len(selection) > 0 and self.connected))
예제 #6
0
 def __init__(self, image_list, parent=None):
     super(TabWidget, self).__init__(parent)
     self.app = QtWidgets.QApplication.instance()
     self.app.aboutToQuit.connect(self.stop_copy)
     if gp and self.app.options.test:
         self.gp_log = gp.check_result(gp.use_python_logging())
     self.config_store = self.app.config_store
     self.image_list = image_list
     self.setLayout(QtWidgets.QGridLayout())
     form = QtWidgets.QFormLayout()
     form.setFieldGrowthPolicy(QtWidgets.QFormLayout.AllNonFixedFieldsGrow)
     self.nm = NameMangler()
     self.file_data = {}
     self.file_list = []
     self.source = None
     self.file_copier = None
     self.updating = QtCore.QMutex()
     # source selector
     box = QtWidgets.QHBoxLayout()
     box.setContentsMargins(0, 0, 0, 0)
     self.source_selector = QtWidgets.QComboBox()
     self.source_selector.currentIndexChanged.connect(self.new_source)
     self.source_selector.setContextMenuPolicy(Qt.CustomContextMenu)
     self.source_selector.customContextMenuRequested.connect(
         self.remove_folder)
     box.addWidget(self.source_selector)
     refresh_button = QtWidgets.QPushButton(
         translate('ImporterTab', 'refresh'))
     refresh_button.clicked.connect(self.refresh)
     box.addWidget(refresh_button)
     box.setStretch(0, 1)
     form.addRow(translate('ImporterTab', 'Source'), box)
     # update config
     self.config_store.delete('importer', 'folders')
     for section in self.config_store.config.sections():
         if not section.startswith('importer'):
             continue
         path_format = self.config_store.get(section, 'path_format')
         if not (path_format and '(' in path_format):
             continue
         path_format = path_format.replace('(', '{').replace(')', '}')
         self.config_store.set(section, 'path_format', path_format)
     # path format
     self.path_format = QtWidgets.QLineEdit()
     self.path_format.setValidator(PathFormatValidator())
     self.path_format.textChanged.connect(self.nm.new_format)
     self.path_format.editingFinished.connect(self.path_format_finished)
     form.addRow(translate('ImporterTab', 'Target format'),
                 self.path_format)
     # path example
     self.path_example = QtWidgets.QLabel()
     self.nm.new_example.connect(self.path_example.setText)
     form.addRow('=>', self.path_example)
     self.layout().addLayout(form, 0, 0)
     # file list
     self.file_list_widget = QtWidgets.QListWidget()
     self.file_list_widget.setSelectionMode(
         QtWidgets.QAbstractItemView.ExtendedSelection)
     self.file_list_widget.itemSelectionChanged.connect(
         self.selection_changed)
     self.layout().addWidget(self.file_list_widget, 1, 0)
     # selection buttons
     buttons = QtWidgets.QVBoxLayout()
     buttons.addStretch(1)
     self.selected_count = QtWidgets.QLabel()
     buttons.addWidget(self.selected_count)
     select_all = QtWidgets.QPushButton(
         translate('ImporterTab', 'Select\nall'))
     select_all.clicked.connect(self.select_all)
     buttons.addWidget(select_all)
     select_new = QtWidgets.QPushButton(
         translate('ImporterTab', 'Select\nnew'))
     select_new.clicked.connect(self.select_new)
     buttons.addWidget(select_new)
     # copy buttons
     self.move_button = StartStopButton(
         translate('ImporterTab', 'Move\nphotos'),
         translate('ImporterTab', 'Stop\nmove'))
     self.move_button.click_start.connect(self.move_selected)
     self.move_button.click_stop.connect(self.stop_copy)
     buttons.addWidget(self.move_button)
     self.copy_button = StartStopButton(
         translate('ImporterTab', 'Copy\nphotos'),
         translate('ImporterTab', 'Stop\ncopy'))
     self.copy_button.click_start.connect(self.copy_selected)
     self.copy_button.click_stop.connect(self.stop_copy)
     buttons.addWidget(self.copy_button)
     self.layout().addLayout(buttons, 0, 1, 2, 1)
     self.selection_changed()
     # final initialisation
     self.image_list.sort_order_changed.connect(self.sort_file_list)
     path = QtCore.QStandardPaths.writableLocation(
         QtCore.QStandardPaths.PicturesLocation)
     self.path_format.setText(os.path.join(path, '%Y', '%Y_%m_%d',
                                           '{name}'))
예제 #7
0
class TabWidget(QtWidgets.QWidget):
    @staticmethod
    def tab_name():
        return translate('ImporterTab', '&Import photos')

    def __init__(self, image_list, parent=None):
        super(TabWidget, self).__init__(parent)
        self.app = QtWidgets.QApplication.instance()
        self.app.aboutToQuit.connect(self.stop_copy)
        if gp and self.app.options.test:
            self.gp_log = gp.check_result(gp.use_python_logging())
        self.config_store = self.app.config_store
        self.image_list = image_list
        self.setLayout(QtWidgets.QGridLayout())
        form = QtWidgets.QFormLayout()
        form.setFieldGrowthPolicy(QtWidgets.QFormLayout.AllNonFixedFieldsGrow)
        self.nm = NameMangler()
        self.file_data = {}
        self.file_list = []
        self.source = None
        self.file_copier = None
        self.updating = QtCore.QMutex()
        # source selector
        box = QtWidgets.QHBoxLayout()
        box.setContentsMargins(0, 0, 0, 0)
        self.source_selector = QtWidgets.QComboBox()
        self.source_selector.currentIndexChanged.connect(self.new_source)
        self.source_selector.setContextMenuPolicy(Qt.CustomContextMenu)
        self.source_selector.customContextMenuRequested.connect(
            self.remove_folder)
        box.addWidget(self.source_selector)
        refresh_button = QtWidgets.QPushButton(
            translate('ImporterTab', 'refresh'))
        refresh_button.clicked.connect(self.refresh)
        box.addWidget(refresh_button)
        box.setStretch(0, 1)
        form.addRow(translate('ImporterTab', 'Source'), box)
        # update config
        self.config_store.delete('importer', 'folders')
        for section in self.config_store.config.sections():
            if not section.startswith('importer'):
                continue
            path_format = self.config_store.get(section, 'path_format')
            if not (path_format and '(' in path_format):
                continue
            path_format = path_format.replace('(', '{').replace(')', '}')
            self.config_store.set(section, 'path_format', path_format)
        # path format
        self.path_format = QtWidgets.QLineEdit()
        self.path_format.setValidator(PathFormatValidator())
        self.path_format.textChanged.connect(self.nm.new_format)
        self.path_format.editingFinished.connect(self.path_format_finished)
        form.addRow(translate('ImporterTab', 'Target format'),
                    self.path_format)
        # path example
        self.path_example = QtWidgets.QLabel()
        self.nm.new_example.connect(self.path_example.setText)
        form.addRow('=>', self.path_example)
        self.layout().addLayout(form, 0, 0)
        # file list
        self.file_list_widget = QtWidgets.QListWidget()
        self.file_list_widget.setSelectionMode(
            QtWidgets.QAbstractItemView.ExtendedSelection)
        self.file_list_widget.itemSelectionChanged.connect(
            self.selection_changed)
        self.layout().addWidget(self.file_list_widget, 1, 0)
        # selection buttons
        buttons = QtWidgets.QVBoxLayout()
        buttons.addStretch(1)
        self.selected_count = QtWidgets.QLabel()
        buttons.addWidget(self.selected_count)
        select_all = QtWidgets.QPushButton(
            translate('ImporterTab', 'Select\nall'))
        select_all.clicked.connect(self.select_all)
        buttons.addWidget(select_all)
        select_new = QtWidgets.QPushButton(
            translate('ImporterTab', 'Select\nnew'))
        select_new.clicked.connect(self.select_new)
        buttons.addWidget(select_new)
        # copy buttons
        self.move_button = StartStopButton(
            translate('ImporterTab', 'Move\nphotos'),
            translate('ImporterTab', 'Stop\nmove'))
        self.move_button.click_start.connect(self.move_selected)
        self.move_button.click_stop.connect(self.stop_copy)
        buttons.addWidget(self.move_button)
        self.copy_button = StartStopButton(
            translate('ImporterTab', 'Copy\nphotos'),
            translate('ImporterTab', 'Stop\ncopy'))
        self.copy_button.click_start.connect(self.copy_selected)
        self.copy_button.click_stop.connect(self.stop_copy)
        buttons.addWidget(self.copy_button)
        self.layout().addLayout(buttons, 0, 1, 2, 1)
        self.selection_changed()
        # final initialisation
        self.image_list.sort_order_changed.connect(self.sort_file_list)
        path = QtCore.QStandardPaths.writableLocation(
            QtCore.QStandardPaths.PicturesLocation)
        self.path_format.setText(os.path.join(path, '%Y', '%Y_%m_%d',
                                              '{name}'))

    @QtSlot(int)
    @catch_all
    def new_source(self, idx):
        self.source = None
        item_data = self.source_selector.itemData(idx)
        if not item_data:
            return
        if callable(item_data):
            # a special 'source' that's actually a method to call
            (item_data)()
            return
        # select new source
        self.source, self.config_section = item_data
        path_format = self.path_format.text()
        path_format = self.config_store.get(self.config_section, 'path_format',
                                            path_format)
        self.path_format.setText(path_format)
        self.file_list_widget.clear()
        # allow 100ms for display to update before getting file list
        QtCore.QTimer.singleShot(100, self.list_files)

    @QtSlot(QtCore.QPoint)
    @catch_all
    def remove_folder(self, pos):
        menu = QtWidgets.QMenu()
        roots = []
        for section in self.config_store.config.sections():
            if not section.startswith('importer folder '):
                continue
            roots.append((section[16:],
                          self.config_store.get(section, 'last_transfer',
                                                datetime.min.isoformat(' '))))
        roots.sort(key=lambda x: x[1], reverse=True)
        for root, last_transfer in roots:
            name = translate('ImporterTab', 'folder: {0}').format(root)
            action = QtGui2.QAction(translate('TechnicalTab',
                                              'Remove "{}"').format(name),
                                    parent=self)
            action.setData('importer folder ' + root)
            menu.addAction(action)
        if menu.isEmpty():
            return
        action = menu.exec_(self.mapToGlobal(pos))
        if not action:
            return
        self.config_store.remove_section(action.data())
        self.refresh()

    def add_folder(self):
        directory = ''
        for idx in range(self.source_selector.count()):
            item_data = self.source_selector.itemData(idx)
            if callable(item_data):
                continue
            source, section = item_data
            if section.startswith('importer folder '):
                directory = section[16:]
                break
        root = QtWidgets.QFileDialog.getExistingDirectory(
            self, translate('ImporterTab', "Select root folder"), directory)
        if not root:
            self._fail()
            return
        section = 'importer folder ' + root
        self.config_store.set(section, 'last_transfer',
                              datetime.min.isoformat(' '))
        self.source_selector.addItem(
            translate('ImporterTab', 'folder: {0}').format(root),
            (FolderSource(root), section))
        idx = self.source_selector.count() - 1
        self.source_selector.setCurrentIndex(idx)
        self.refresh()

    @QtSlot()
    @catch_all
    def path_format_finished(self):
        if self.source:
            self.config_store.set(self.config_section, 'path_format',
                                  self.nm.format_string)
        self.show_file_list()

    @QtSlot()
    @catch_all
    def refresh(self):
        was_blocked = self.source_selector.blockSignals(True)
        # save current selection
        idx = self.source_selector.currentIndex()
        if idx >= 0:
            old_item_text = self.source_selector.itemText(idx)
        else:
            old_item_text = None
        # rebuild list
        self.source_selector.clear()
        self.source_selector.addItem(
            translate('ImporterTab', '<select source>'), self._new_file_list)
        for model, port_name in get_camera_list():
            self.source_selector.addItem(
                translate('ImporterTab', 'camera: {0}').format(model),
                (CameraSource(model, port_name), 'importer ' + model))
        roots = []
        for section in self.config_store.config.sections():
            if not section.startswith('importer folder '):
                continue
            roots.append((section[16:],
                          self.config_store.get(section, 'last_transfer',
                                                datetime.min.isoformat(' '))))
        roots.sort(key=lambda x: x[1], reverse=True)
        for root, last_transfer in roots:
            if os.path.isdir(root):
                self.source_selector.addItem(
                    translate('ImporterTab', 'folder: {0}').format(root),
                    (FolderSource(root), 'importer folder ' + root))
        self.source_selector.addItem(
            translate('ImporterTab', '<add a folder>'), self.add_folder)
        # restore saved selection
        new_idx = -1
        for idx in range(self.source_selector.count()):
            item_text = self.source_selector.itemText(idx)
            if item_text == old_item_text:
                new_idx = idx
                self.source_selector.setCurrentIndex(idx)
                break
        self.source_selector.blockSignals(was_blocked)
        if new_idx < 0:
            self.source_selector.setCurrentIndex(0)
            self.new_source(0)

    def do_not_close(self):
        if not self.file_copier:
            return False
        dialog = QtWidgets.QMessageBox(parent=self)
        dialog.setWindowTitle(
            translate('ImporterTab', 'Photini: import in progress'))
        dialog.setText(
            translate('ImporterTab',
                      '<h3>Importing photos has not finished.</h3>'))
        dialog.setInformativeText(
            translate('ImporterTab', 'Closing now will terminate the import.'))
        dialog.setIcon(QtWidgets.QMessageBox.Warning)
        dialog.setStandardButtons(QtWidgets.QMessageBox.Close
                                  | QtWidgets.QMessageBox.Cancel)
        dialog.setDefaultButton(QtWidgets.QMessageBox.Cancel)
        result = dialog.exec_()
        if result == QtWidgets.QMessageBox.Close:
            self.stop_copy()
        return result == QtWidgets.QMessageBox.Cancel

    def new_selection(self, selection):
        pass

    @QtSlot()
    @catch_all
    def list_files(self):
        file_data = {}
        if self.source:
            with Busy():
                file_data = self.source.get_file_data()
                if file_data is None:
                    self._fail()
                    return
        self._new_file_list(file_data)

    def _fail(self):
        self.source_selector.setCurrentIndex(0)
        self.refresh()

    def _new_file_list(self, file_data={}):
        self.file_list = list(file_data.keys())
        self.file_data = file_data
        self.sort_file_list()

    @QtSlot()
    @catch_all
    def sort_file_list(self):
        if self.config_store.get('controls', 'sort_date', False):
            self.file_list.sort(key=lambda x: self.file_data[x]['timestamp'])
        else:
            self.file_list.sort()
        self.show_file_list()
        if self.file_list:
            example = self.file_data[self.file_list[-1]]
        else:
            example = {
                'camera': None,
                'name': 'IMG_9999.JPG',
                'timestamp': datetime.now(),
            }
        self.nm.set_example(example)

    def show_file_list(self):
        self.file_list_widget.clear()
        first_active = None
        item = None
        for name in self.file_list:
            file_data = self.file_data[name]
            dest_path = self.nm.transform(file_data)
            file_data['dest_path'] = dest_path
            item = QtWidgets.QListWidgetItem(name + ' -> ' + dest_path)
            item.setData(Qt.UserRole, name)
            if os.path.exists(dest_path):
                item.setFlags(Qt.NoItemFlags)
            else:
                if not first_active:
                    first_active = item
                item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
            self.file_list_widget.addItem(item)
        if not first_active:
            first_active = item
        self.file_list_widget.scrollToItem(
            first_active, QtWidgets.QAbstractItemView.PositionAtTop)

    @QtSlot()
    @catch_all
    def selection_changed(self):
        count = len(self.file_list_widget.selectedItems())
        self.selected_count.setText(
            translate('ImporterTab', '%n file(s)\nselected', '', count))
        if not self.file_copier:
            self.move_button.setEnabled(count > 0)
            self.copy_button.setEnabled(count > 0)

    @QtSlot()
    @catch_all
    def select_all(self):
        self.select_files(datetime.min)

    @QtSlot()
    @catch_all
    def select_new(self):
        since = datetime.min
        if self.source:
            since = self.config_store.get(self.config_section, 'last_transfer',
                                          since.isoformat(' '))
            if len(since) > 19:
                since = datetime.strptime(since, '%Y-%m-%d %H:%M:%S.%f')
            else:
                since = datetime.strptime(since, '%Y-%m-%d %H:%M:%S')
        self.select_files(since)

    def select_files(self, since):
        count = self.file_list_widget.count()
        if not count:
            return
        self.file_list_widget.clearSelection()
        first_active = None
        for row in range(count):
            item = self.file_list_widget.item(row)
            if not (item.flags() & Qt.ItemIsSelectable):
                continue
            name = item.data(Qt.UserRole)
            timestamp = self.file_data[name]['timestamp']
            if timestamp > since:
                if not first_active:
                    first_active = item
                item.setSelected(True)
        if not first_active:
            first_active = item
        self.file_list_widget.scrollToItem(
            first_active, QtWidgets.QAbstractItemView.PositionAtTop)

    @QtSlot()
    @catch_all
    def move_selected(self):
        self.copy_selected(move=True)

    @QtSlot()
    @catch_all
    def copy_selected(self, move=False):
        copy_list = []
        for item in self.file_list_widget.selectedItems():
            name = item.data(Qt.UserRole)
            info = self.file_data[name]
            if (move and 'path' in info
                    and self.image_list.get_image(info['path'])):
                # don't rename an open file
                logger.warning('Please close image %s before moving it',
                               info['name'])
            else:
                copy_list.append(info)
        if not copy_list:
            return
        if move:
            self.move_button.set_checked(True)
            self.copy_button.setEnabled(False)
        else:
            self.copy_button.set_checked(True)
            self.move_button.setEnabled(False)
        last_file_copied = None, datetime.min
        copier_result = deque()
        # start file copier in a separate thread
        self.file_copier = FileCopier(self.source, copy_list, move,
                                      copier_result)
        copier_thread = QtCore.QThread(self)
        self.file_copier.moveToThread(copier_thread)
        copier_thread.started.connect(self.file_copier.start)
        copier_thread.start()
        # show files as they're copied
        while self.file_copier.running:
            if copier_result:
                info, status = copier_result.popleft()
                if not info:
                    # copier thread has finished
                    break
                if status != 'ok':
                    self._fail()
                    break
                if last_file_copied[1] < info['timestamp']:
                    last_file_copied = info['dest_path'], info['timestamp']
                for n in range(self.file_list_widget.count()):
                    item = self.file_list_widget.item(n)
                    if item.data(Qt.UserRole) == info['name']:
                        item.setFlags(Qt.NoItemFlags)
                        self.file_list_widget.scrollToItem(
                            item, QtWidgets.QAbstractItemView.PositionAtTop)
                        self.selection_changed()
                        break
                self.image_list.open_file(info['dest_path'])
            else:
                # wait for copier result
                self.app.processEvents()
        self.move_button.set_checked(False)
        self.copy_button.set_checked(False)
        self.file_copier = None
        copier_thread.quit()
        copier_thread.wait()
        if last_file_copied[0]:
            self.config_store.set(self.config_section, 'last_transfer',
                                  last_file_copied[1].isoformat(' '))
            self.image_list.done_opening(last_file_copied[0])
        self.list_files()

    @QtSlot()
    @catch_all
    def stop_copy(self):
        if self.file_copier:
            self.file_copier.running = False
예제 #8
0
class Importer(QtWidgets.QWidget):
    def __init__(self, image_list, parent=None):
        super(Importer, self).__init__(parent)
        app = QtWidgets.QApplication.instance()
        self.config_store = app.config_store
        self.image_list = image_list
        self.setLayout(QtWidgets.QGridLayout())
        form = QtWidgets.QFormLayout()
        form.setFieldGrowthPolicy(QtWidgets.QFormLayout.AllNonFixedFieldsGrow)
        self.nm = NameMangler()
        self.file_data = {}
        self.file_list = []
        self.session_factory = None
        self.import_in_progress = False
        # source selector
        box = QtWidgets.QHBoxLayout()
        box.setContentsMargins(0, 0, 0, 0)
        self.source_selector = QtWidgets.QComboBox()
        self.source_selector.currentIndexChanged.connect(self.new_source)
        box.addWidget(self.source_selector)
        refresh_button = QtWidgets.QPushButton(self.tr('refresh'))
        refresh_button.clicked.connect(self.refresh)
        box.addWidget(refresh_button)
        box.setStretch(0, 1)
        form.addRow(self.tr('Source'), box)
        # path format
        self.path_format = QtWidgets.QLineEdit()
        self.path_format.setValidator(PathFormatValidator())
        self.path_format.textChanged.connect(self.nm.new_format)
        self.path_format.editingFinished.connect(self.path_format_finished)
        form.addRow(self.tr('Target format'), self.path_format)
        # path example
        self.path_example = QtWidgets.QLabel()
        self.nm.new_example.connect(self.path_example.setText)
        form.addRow('=>', self.path_example)
        self.layout().addLayout(form, 0, 0)
        # file list
        self.file_list_widget = QtWidgets.QListWidget()
        self.file_list_widget.setSelectionMode(
            QtWidgets.QAbstractItemView.ExtendedSelection)
        self.file_list_widget.itemSelectionChanged.connect(
            self.selection_changed)
        self.layout().addWidget(self.file_list_widget, 1, 0)
        # selection buttons
        buttons = QtWidgets.QVBoxLayout()
        buttons.addStretch(1)
        self.selected_count = QtWidgets.QLabel()
        self.selection_changed()
        buttons.addWidget(self.selected_count)
        select_all = QtWidgets.QPushButton(self.tr('Select\nall'))
        select_all.clicked.connect(self.select_all)
        buttons.addWidget(select_all)
        select_new = QtWidgets.QPushButton(self.tr('Select\nnew'))
        select_new.clicked.connect(self.select_new)
        buttons.addWidget(select_new)
        self.copy_button = StartStopButton(self.tr('Copy\nphotos'),
                                           self.tr('Stop\nimport'))
        self.copy_button.click_start.connect(self.copy_selected)
        buttons.addWidget(self.copy_button)
        self.layout().addLayout(buttons, 0, 1, 2, 1)
        # final initialisation
        self.image_list.sort_order_changed.connect(self.sort_file_list)
        if sys.platform == 'win32':
            import win32com.shell as ws
            path = ws.shell.SHGetFolderPath(0, ws.shellcon.CSIDL_MYPICTURES,
                                            None, 0)
        else:
            path = os.path.expanduser('~/Pictures')
        self.path_format.setText(os.path.join(path, '%Y', '%Y_%m_%d',
                                              '(name)'))
        self.refresh()
        self.list_files()

    @QtCore.pyqtSlot(int)
    def new_source(self, idx):
        self.session_factory = None
        item_data = self.source_selector.itemData(idx)
        if callable(item_data):
            # a special 'source' that's actually a method to call
            (item_data)()
            return
        # select new source
        (self.session_factory, self.session_params,
         self.config_section) = item_data
        path_format = self.path_format.text()
        path_format = self.config_store.get(self.config_section, 'path_format',
                                            path_format)
        self.path_format.setText(path_format)
        self.file_list_widget.clear()
        # allow 100ms for display to update before getting file list
        QtCore.QTimer.singleShot(100, self.list_files)

    def add_folder(self):
        folders = eval(self.config_store.get('importer', 'folders', '[]'))
        if folders:
            directory = folders[0]
        else:
            directory = ''
        root = QtWidgets.QFileDialog.getExistingDirectory(
            self, self.tr("Select root folder"), directory)
        if not root:
            self._fail()
            return
        if root in folders:
            folders.remove(root)
        folders.insert(0, root)
        if len(folders) > 5:
            del folders[-1]
        self.config_store.set('importer', 'folders', repr(folders))
        self.refresh()
        idx = self.source_selector.count() - (1 + len(folders))
        self.source_selector.setCurrentIndex(idx)

    @QtCore.pyqtSlot()
    def path_format_finished(self):
        if self.session_factory:
            self.config_store.set(self.config_section, 'path_format',
                                  self.nm.format_string)
        self.show_file_list()

    @QtCore.pyqtSlot()
    def refresh(self):
        was_blocked = self.source_selector.blockSignals(True)
        # save current selection
        idx = self.source_selector.currentIndex()
        if idx >= 0:
            old_item_data = self.source_selector.itemData(idx)
        else:
            old_item_data = None
        # rebuild list
        self.source_selector.clear()
        self.source_selector.addItem(self.tr('<select source>'),
                                     self._new_file_list)
        for model, port_name in get_camera_list():
            self.source_selector.addItem(
                self.tr('camera: {0}').format(model),
                (CameraSource, (model, port_name), 'importer ' + model))
        for root in eval(self.config_store.get('importer', 'folders', '[]')):
            if os.path.isdir(root):
                self.source_selector.addItem(
                    self.tr('folder: {0}').format(root),
                    (FolderSource, (root, ), 'importer folder ' + root))
        self.source_selector.addItem(self.tr('<add a folder>'),
                                     self.add_folder)
        # restore saved selection
        new_idx = -1
        for idx in range(self.source_selector.count()):
            item_data = self.source_selector.itemData(idx)
            if item_data == old_item_data:
                new_idx = idx
                self.source_selector.setCurrentIndex(idx)
                break
        self.source_selector.blockSignals(was_blocked)
        if new_idx < 0:
            self.source_selector.setCurrentIndex(0)

    def do_not_close(self):
        if not self.import_in_progress:
            return False
        dialog = QtWidgets.QMessageBox()
        dialog.setWindowTitle(self.tr('Photini: import in progress'))
        dialog.setText(self.tr('<h3>Importing photos has not finished.</h3>'))
        dialog.setInformativeText(
            self.tr('Closing now will terminate the import.'))
        dialog.setIcon(QtWidgets.QMessageBox.Warning)
        dialog.setStandardButtons(QtWidgets.QMessageBox.Close
                                  | QtWidgets.QMessageBox.Cancel)
        dialog.setDefaultButton(QtWidgets.QMessageBox.Cancel)
        result = dialog.exec_()
        return result == QtWidgets.QMessageBox.Cancel

    @QtCore.pyqtSlot(list)
    def new_selection(self, selection):
        pass

    @contextmanager
    def session(self):
        session = (self.session_factory)(*self.session_params)
        yield session
        session.close()

    def list_files(self):
        file_data = {}
        if self.session_factory:
            with self.session() as session:
                with Busy():
                    try:
                        file_list = session.list_files()
                    except gp.GPhoto2Error:
                        # camera is no longer visible
                        self._fail()
                        return
                    for path in file_list:
                        try:
                            info = session.get_file_info(path)
                        except gp.GPhoto2Error:
                            self._fail()
                            return
                        file_data[info['name']] = info
        self._new_file_list(file_data)

    def _fail(self):
        self.source_selector.setCurrentIndex(0)
        self.refresh()

    def _new_file_list(self, file_data={}):
        self.file_list = list(file_data.keys())
        self.file_data = file_data
        self.sort_file_list()

    @QtCore.pyqtSlot()
    def sort_file_list(self):
        if eval(self.config_store.get('controls', 'sort_date', 'False')):
            self.file_list.sort(key=lambda x: self.file_data[x]['timestamp'])
        else:
            self.file_list.sort()
        self.show_file_list()
        if self.file_list:
            example = self.file_data[self.file_list[-1]]
        else:
            example = {
                'camera': None,
                'name': 'IMG_9999.JPG',
                'timestamp': datetime.now(),
            }
        self.nm.set_example(example)

    def show_file_list(self):
        self.file_list_widget.clear()
        first_active = None
        item = None
        for name in self.file_list:
            file_data = self.file_data[name]
            dest_path = self.nm.transform(file_data)
            file_data['dest_path'] = dest_path
            item = QtWidgets.QListWidgetItem(name + ' -> ' + dest_path)
            if os.path.exists(dest_path):
                item.setFlags(Qt.NoItemFlags)
            else:
                if not first_active:
                    first_active = item
                item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
            self.file_list_widget.addItem(item)
        if not first_active:
            first_active = item
        self.file_list_widget.scrollToItem(
            first_active, QtWidgets.QAbstractItemView.PositionAtTop)

    @QtCore.pyqtSlot()
    def selection_changed(self):
        count = len(self.file_list_widget.selectedItems())
        self.selected_count.setText(self.tr('%n file(s)\nselected', '', count))

    @QtCore.pyqtSlot()
    def select_all(self):
        self.select_files(datetime.min)

    @QtCore.pyqtSlot()
    def select_new(self):
        since = datetime.min
        if self.session_factory:
            since = self.config_store.get(self.config_section, 'last_transfer',
                                          since.isoformat(' '))
            if len(since) > 19:
                since = datetime.strptime(since, '%Y-%m-%d %H:%M:%S.%f')
            else:
                since = datetime.strptime(since, '%Y-%m-%d %H:%M:%S')
        self.select_files(since)

    def select_files(self, since):
        count = self.file_list_widget.count()
        if not count:
            return
        self.file_list_widget.clearSelection()
        first_active = None
        for row in range(count):
            item = self.file_list_widget.item(row)
            if not (item.flags() & Qt.ItemIsSelectable):
                continue
            name = item.text().split()[0]
            timestamp = self.file_data[name]['timestamp']
            if timestamp > since:
                if not first_active:
                    first_active = item
                item.setSelected(True)
        if not first_active:
            first_active = item
        self.file_list_widget.scrollToItem(
            first_active, QtWidgets.QAbstractItemView.PositionAtTop)

    @QtCore.pyqtSlot()
    def copy_selected(self):
        if self.import_in_progress:
            # user has clicked while import is still cancelling
            self.copy_button.setChecked(False)
            return
        self.import_in_progress = True
        copy_list = []
        for item in self.file_list_widget.selectedItems():
            name = item.text().split()[0]
            copy_list.append(self.file_data[name])
        last_item = None, datetime.min
        with self.session() as session:
            with Busy():
                for item in copy_list:
                    dest_path = item['dest_path']
                    dest_dir = os.path.dirname(dest_path)
                    if self.abort_copy():
                        break
                    if not os.path.isdir(dest_dir):
                        os.makedirs(dest_dir)
                    try:
                        camera_file = session.copy_file(item, dest_path)
                        if self.abort_copy():
                            break
                        if camera_file:
                            camera_file.save(dest_path)
                    except gp.GPhoto2Error as ex:
                        self.logger.error(str(ex))
                        self._fail()
                        break
                    timestamp = item['timestamp']
                    if last_item[1] < timestamp:
                        last_item = dest_path, timestamp
                    if self.abort_copy():
                        break
                    self.image_list.open_file(dest_path)
                    if self.abort_copy():
                        break
                    QtCore.QCoreApplication.flush()
        if last_item[0]:
            self.config_store.set(self.config_section, 'last_transfer',
                                  last_item[1].isoformat(' '))
            self.image_list.done_opening(last_item[0])
        self.show_file_list()
        self.copy_button.setChecked(False)
        self.import_in_progress = False

    def abort_copy(self):
        # test if user has stopped copy or quit program
        QtCore.QCoreApplication.processEvents()
        return not (self.copy_button.isChecked() and self.isVisible())
예제 #9
0
class PhotiniUploader(QtWidgets.QWidget):
    abort_upload = QtCore.Signal(bool)

    def __init__(self, upload_config_widget, image_list, *arg, **kw):
        super(PhotiniUploader, self).__init__(*arg, **kw)
        self.app = QtWidgets.QApplication.instance()
        self.app.aboutToQuit.connect(self.shutdown)
        logger.debug('using %s', keyring.get_keyring().__module__)
        self.image_list = image_list
        self.setLayout(QtWidgets.QGridLayout())
        self.session = self.session_factory()
        self.session.connection_changed.connect(self.connection_changed)
        self.upload_worker = None
        # user details
        self.user = {}
        user_group = QtWidgets.QGroupBox(translate('UploaderTabsAll', 'User'))
        user_group.setLayout(QtWidgets.QVBoxLayout())
        self.user_photo = QtWidgets.QLabel()
        self.user_photo.setAlignment(Qt.AlignHCenter | Qt.AlignTop)
        user_group.layout().addWidget(self.user_photo)
        self.user_name = QtWidgets.QLabel()
        self.user_name.setWordWrap(True)
        self.user_name.setFixedWidth(80)
        user_group.layout().addWidget(self.user_name)
        user_group.layout().addStretch(1)
        self.layout().addWidget(user_group, 0, 0, 1, 2)
        # connect / disconnect button
        self.user_connect = StartStopButton(
            translate('UploaderTabsAll', 'Log in'),
            translate('UploaderTabsAll', 'Log out'))
        self.user_connect.click_start.connect(self.log_in)
        self.user_connect.click_stop.connect(self.session.log_out)
        self.layout().addWidget(self.user_connect, 1, 0, 1, 2)
        # 'service' specific widget
        self.layout().addWidget(upload_config_widget, 0, 2, 2, 2)
        # upload button
        self.upload_button = StartStopButton(
            translate('UploaderTabsAll', 'Start upload'),
            translate('UploaderTabsAll', 'Stop upload'))
        self.upload_button.setEnabled(False)
        self.upload_button.click_start.connect(self.start_upload)
        self.upload_button.click_stop.connect(self.stop_upload)
        self.layout().addWidget(self.upload_button, 2, 3)
        # progress bar
        self.layout().addWidget(
            QtWidgets.QLabel(translate('UploaderTabsAll', 'Progress')), 2, 0)
        self.total_progress = QtWidgets.QProgressBar()
        self.layout().addWidget(self.total_progress, 2, 1, 1, 2)
        # adjust spacing
        self.layout().setColumnStretch(2, 1)
        self.layout().setRowStretch(0, 1)
        # initialise as not connected
        self.connection_changed(False)

    def tr(self, *arg, **kw):
        return QtCore.QCoreApplication.translate('UploaderTabsAll', *arg, **kw)

    @QtCore.Slot()
    @catch_all
    def shutdown(self):
        self.session.disconnect()

    @QtCore.Slot(bool)
    @catch_all
    def connection_changed(self, connected):
        if connected:
            with Busy():
                self.show_user(*self.session.get_user())
                self.show_album_list(self.session.get_albums())
        else:
            self.show_user(None, None)
            self.show_album_list([])
        self.user_connect.set_checked(connected)
        self.upload_config.setEnabled(connected and not self.upload_worker)
        self.user_connect.setEnabled(not self.upload_worker)
        self.enable_upload_button()

    def refresh(self):
        if not self.user_connect.is_checked():
            self.log_in(do_auth=False)
        self.enable_upload_button()

    def do_not_close(self):
        if not self.upload_worker:
            return False
        dialog = QtWidgets.QMessageBox(parent=self)
        dialog.setWindowTitle(translate(
            'UploaderTabsAll', 'Photini: upload in progress'))
        dialog.setText(translate(
            'UploaderTabsAll',
            '<h3>Upload to {} has not finished.</h3>').format(self.service_name))
        dialog.setInformativeText(
            translate('UploaderTabsAll', 'Closing now will terminate the upload.'))
        dialog.setIcon(QtWidgets.QMessageBox.Warning)
        dialog.setStandardButtons(
            QtWidgets.QMessageBox.Close | QtWidgets.QMessageBox.Cancel)
        dialog.setDefaultButton(QtWidgets.QMessageBox.Cancel)
        result = dialog.exec_()
        return result == QtWidgets.QMessageBox.Cancel

    def show_user(self, name, picture):
        if name:
            self.user_name.setText(translate(
                'UploaderTabsAll',
                'Logged in as {0} on {1}').format(name, self.service_name))
        else:
            self.user_name.setText(translate(
                'UploaderTabsAll',
                'Not logged in to {}').format(self.service_name))
        pixmap = QtGui.QPixmap()
        if picture:
            pixmap.loadFromData(picture)
        self.user_photo.setPixmap(pixmap)

    def get_temp_filename(self, image, ext='.jpg'):
        temp_dir = appdirs.user_cache_dir('photini')
        if not os.path.isdir(temp_dir):
            os.makedirs(temp_dir)
        return os.path.join(temp_dir, os.path.basename(image.path) + ext)

    def copy_metadata(self, image, path):
        # copy metadata
        md = Metadata.clone(path, image.metadata)
        # save metedata, forcing IPTC creation
        md.dirty = True
        md.save(if_mode=True, sc_mode='none', force_iptc=True)

    def convert_to_jpeg(self, image):
        im = QtGui.QImage(image.path)
        path = self.get_temp_filename(image)
        im.save(path, format='jpeg', quality=95)
        self.copy_metadata(image, path)
        return path

    def copy_file_and_metadata(self, image):
        path = self.get_temp_filename(image, ext='')
        shutil.copyfile(image.path, path)
        self.copy_metadata(image, path)
        return path

    def is_convertible(self, image):
        if not image.file_type.startswith('image'):
            # can only convert images
            return False
        return QtGui.QImageReader(image.path).canRead()

    def get_conversion_function(self, image, params):
        if image.file_type in self.image_types['accepted']:
            if image.file_type.startswith('video'):
                # don't try to write metadata to videos
                return None
            if image.metadata._sc or not image.metadata._if.has_iptc():
                # need to create file without sidecar and with IPTC
                return self.copy_file_and_metadata
            return None
        if not self.is_convertible(image):
            msg = translate(
                'UploaderTabsAll',
                'File "{0}" is of type "{1}", which {2} does not' +
                ' accept and Photini cannot convert.')
            buttons = QtWidgets.QMessageBox.Ignore
        elif (self.image_types['rejected'] == '*' or
              image.file_type in self.image_types['rejected']):
            msg = translate(
                'UploaderTabsAll',
                'File "{0}" is of type "{1}", which {2} does not' +
                ' accept. Would you like to convert it to JPEG?')
            buttons = QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.Ignore
        else:
            msg = translate(
                'UploaderTabsAll',
                'File "{0}" is of type "{1}", which {2} may not' +
                ' handle correctly. Would you like to convert it to JPEG?')
            buttons = QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No
        dialog = QtWidgets.QMessageBox(parent=self)
        dialog.setWindowTitle(
            translate('UploaderTabsAll', 'Photini: incompatible type'))
        dialog.setText(
            translate('UploaderTabsAll', '<h3>Incompatible image type.</h3>'))
        dialog.setInformativeText(msg.format(os.path.basename(image.path),
                                             image.file_type, self.service_name))
        dialog.setIcon(QtWidgets.QMessageBox.Warning)
        dialog.setStandardButtons(buttons)
        dialog.setDefaultButton(QtWidgets.QMessageBox.Yes)
        result = dialog.exec_()
        if result == QtWidgets.QMessageBox.Ignore:
            return 'omit'
        if result == QtWidgets.QMessageBox.Yes:
            return self.convert_to_jpeg
        return None

    @QtCore.Slot()
    @catch_all
    def stop_upload(self):
        self.abort_upload.emit(False)

    @QtCore.Slot()
    @catch_all
    def start_upload(self):
        if not self.image_list.unsaved_files_dialog(with_discard=False):
            return
        # make list of items to upload
        upload_list = []
        for image in self.image_list.get_selected_images():
            params = self.get_upload_params(image)
            if not params:
                continue
            convert = self.get_conversion_function(image, params)
            if convert == 'omit':
                continue
            upload_list.append((image, convert, params))
        if not upload_list:
            self.upload_button.setChecked(False)
            return
        self.upload_button.set_checked(True)
        self.upload_config.setEnabled(False)
        self.user_connect.setEnabled(False)
        # do uploading in separate thread, so GUI can continue
        self.upload_worker = UploadWorker(self.session_factory, upload_list)
        thread = QtCore.QThread(self)
        self.upload_worker.moveToThread(thread)
        self.upload_worker.upload_error.connect(
            self.upload_error, Qt.BlockingQueuedConnection)
        self.abort_upload.connect(
            self.upload_worker.abort_upload, Qt.DirectConnection)
        self.upload_worker.upload_progress.connect(self.upload_progress)
        thread.started.connect(self.upload_worker.start)
        self.upload_worker.finished.connect(self.uploader_finished)
        self.upload_worker.finished.connect(thread.quit)
        self.upload_worker.finished.connect(self.upload_worker.deleteLater)
        thread.finished.connect(thread.deleteLater)
        thread.start()

    @QtCore.Slot(float, six.text_type)
    @catch_all
    def upload_progress(self, value, format_):
        self.total_progress.setValue(value)
        if format_:
            self.total_progress.setFormat(format_)

    @QtCore.Slot(six.text_type, six.text_type)
    @catch_all
    def upload_error(self, name, error):
        dialog = QtWidgets.QMessageBox(self)
        dialog.setWindowTitle(translate(
            'UploaderTabsAll', 'Photini: upload error'))
        dialog.setText(translate(
            'UploaderTabsAll', '<h3>File "{}" upload failed.</h3>').format(
                name))
        dialog.setInformativeText(error)
        dialog.setIcon(QtWidgets.QMessageBox.Warning)
        dialog.setStandardButtons(QtWidgets.QMessageBox.Abort |
                                  QtWidgets.QMessageBox.Retry)
        dialog.setDefaultButton(QtWidgets.QMessageBox.Retry)
        self.abort_upload.emit(dialog.exec_() == QtWidgets.QMessageBox.Retry)

    @QtCore.Slot()
    @catch_all
    def uploader_finished(self):
        self.upload_button.set_checked(False)
        self.upload_config.setEnabled(True)
        self.user_connect.setEnabled(True)
        self.upload_worker = None
        self.enable_upload_button()

    @QtCore.Slot()
    @catch_all
    def log_in(self, do_auth=True):
        with DisableWidget(self.user_connect):
            with Busy():
                connect = self.session.connect()
            if connect is None:
                # can't reach server
                return
            if do_auth and not connect:
                self.authorise()

    def authorise(self):
        with Busy():
            # do full authentication procedure
            http_server = HTTPServer(('127.0.0.1', 0), AuthRequestHandler)
            redirect_uri = 'http://127.0.0.1:' + str(http_server.server_port)
            auth_url = self.session.get_auth_url(redirect_uri)
            if not auth_url:
                logger.error('Failed to get auth URL')
                http_server.server_close()
                return
            server = AuthServer()
            thread = QtCore.QThread(self)
            server.moveToThread(thread)
            server.server = http_server
            server.response.connect(self.auth_response)
            thread.started.connect(server.handle_requests)
            server.finished.connect(thread.quit)
            server.finished.connect(server.deleteLater)
            thread.finished.connect(thread.deleteLater)
            thread.start()
            if QtGui.QDesktopServices.openUrl(QtCore.QUrl(auth_url)):
                return
            logger.error('Failed to open web browser')

    @QtCore.Slot(dict)
    @catch_all
    def auth_response(self, result):
        with Busy():
            self.session.get_access_token(result)

    @QtCore.Slot(list)
    @catch_all
    def new_selection(self, selection):
        self.enable_upload_button(selection=selection)

    def enable_upload_button(self, selection=None):
        if self.upload_button.is_checked():
            # can always cancel upload in progress
            self.upload_button.setEnabled(True)
            return
        if not self.user_connect.is_checked():
            # can't upload if not logged in
            self.upload_button.setEnabled(False)
            return
        if selection is None:
            selection = self.image_list.get_selected_images()
        self.upload_button.setEnabled(len(selection) > 0)
예제 #10
0
 def __init__(self, image_list, parent=None):
     super(TabWidget, self).__init__(parent)
     app = QtWidgets.QApplication.instance()
     app.aboutToQuit.connect(self.shutdown)
     if gp and app.test_mode:
         self.gp_log = gp.check_result(gp.use_python_logging())
     self.config_store = app.config_store
     self.image_list = image_list
     self.setLayout(QtWidgets.QGridLayout())
     form = QtWidgets.QFormLayout()
     form.setFieldGrowthPolicy(QtWidgets.QFormLayout.AllNonFixedFieldsGrow)
     self.nm = NameMangler()
     self.file_data = {}
     self.file_list = []
     self.source = None
     self.file_copier = None
     # source selector
     box = QtWidgets.QHBoxLayout()
     box.setContentsMargins(0, 0, 0, 0)
     self.source_selector = QtWidgets.QComboBox()
     self.source_selector.currentIndexChanged.connect(self.new_source)
     box.addWidget(self.source_selector)
     refresh_button = QtWidgets.QPushButton(self.tr('refresh'))
     refresh_button.clicked.connect(self.refresh)
     box.addWidget(refresh_button)
     box.setStretch(0, 1)
     form.addRow(self.tr('Source'), box)
     # path format
     self.path_format = QtWidgets.QLineEdit()
     self.path_format.setValidator(PathFormatValidator())
     self.path_format.textChanged.connect(self.nm.new_format)
     self.path_format.editingFinished.connect(self.path_format_finished)
     form.addRow(self.tr('Target format'), self.path_format)
     # path example
     self.path_example = QtWidgets.QLabel()
     self.nm.new_example.connect(self.path_example.setText)
     form.addRow('=>', self.path_example)
     self.layout().addLayout(form, 0, 0)
     # file list
     self.file_list_widget = QtWidgets.QListWidget()
     self.file_list_widget.setSelectionMode(
         QtWidgets.QAbstractItemView.ExtendedSelection)
     self.file_list_widget.itemSelectionChanged.connect(
         self.selection_changed)
     self.layout().addWidget(self.file_list_widget, 1, 0)
     # selection buttons
     buttons = QtWidgets.QVBoxLayout()
     buttons.addStretch(1)
     self.selected_count = QtWidgets.QLabel()
     buttons.addWidget(self.selected_count)
     select_all = QtWidgets.QPushButton(self.tr('Select\nall'))
     select_all.clicked.connect(self.select_all)
     buttons.addWidget(select_all)
     select_new = QtWidgets.QPushButton(self.tr('Select\nnew'))
     select_new.clicked.connect(self.select_new)
     buttons.addWidget(select_new)
     # copy buttons
     self.move_button = StartStopButton(self.tr('Move\nphotos'),
                                        self.tr('Stop\nmove'))
     self.move_button.click_start.connect(self.move_selected)
     self.move_button.click_stop.connect(self.stop_copy)
     buttons.addWidget(self.move_button)
     self.copy_button = StartStopButton(self.tr('Copy\nphotos'),
                                        self.tr('Stop\ncopy'))
     self.copy_button.click_start.connect(self.copy_selected)
     self.copy_button.click_stop.connect(self.stop_copy)
     buttons.addWidget(self.copy_button)
     self.layout().addLayout(buttons, 0, 1, 2, 1)
     self.selection_changed()
     # final initialisation
     self.image_list.sort_order_changed.connect(self.sort_file_list)
     if qt_version_info >= (5, 0):
         path = QtCore.QStandardPaths.writableLocation(
             QtCore.QStandardPaths.PicturesLocation)
     else:
         path = QtGui.QDesktopServices.storageLocation(
             QtGui.QDesktopServices.PicturesLocation)
     self.path_format.setText(os.path.join(path, '%Y', '%Y_%m_%d',
                                           '{name}'))
     self.refresh()
     self.list_files()