Example #1
0
    def __init__(self):
        super(Watcher, self).__init__()

        self._meta_files_mgr = MetaFilesManager()

        self._img_ext_filter = settings.get_allowed_image_formats()

        print(self._img_ext_filter)
Example #2
0
    def __init__(self, sd_id, img_serial, parent=None):
        super(SlideshowWindow, self).__init__(parent)
        self.setupUi(self)

        self._curr_sd_id = sd_id
        self._curr_img_serial = img_serial
        self._is_slideshow_looped = settings.get(SettingType.SLIDESHOW_LOOP,
                                                 False, 'bool')
        self._slideshow_iterval = settings.get(SettingType.SLIDESHOW_INTERVAL,
                                               1000, 'int')
        self._mouse_idle_interval = 1000
        self._is_slideshow = True
        self._gfx_item = None
        self._last_mouse_pos = QtCore.QPoint()
        self._curr_mouse_pos = QtCore.QPoint()
        self._is_mouse_inside_controls = False

        self._shortcut_exit = QShortcut(QKeySequence(QtCore.Qt.Key_Escape),
                                        self)
        self._shortcut_exit.activated.connect(self.closeWindow)

        self._timer = QTimer()
        self._timer.setSingleShot(True)
        self._timer.timeout.connect(self.slide_next_img)

        self._idle_timer = QTimer()
        self._timer.setSingleShot(True)
        self._idle_timer.timeout.connect(self.hide_slideshow_controls)

        self.gfx_slide = SlideshowGraphicsView()
        self.gfx_slide.mouse_moved.connect(self.on_mouse_moved)
        self.horizontalLayout.addWidget(self.gfx_slide)

        self._gfx_scene = QtWidgets.QGraphicsScene()
        self.gfx_slide.setScene(self._gfx_scene)
        self.gfx_slide.setAlignment(QtCore.Qt.AlignCenter)

        self._control_widget = SlideshowControlWidget(self._slideshow_iterval)
        self._control_widget.stop_slideshow.connect(self.closeWindow)
        self._control_widget.start_slideshow.connect(self.slideshow_start)
        self._control_widget.next_slide.connect(self.slide_next_img)
        self._control_widget.prev_slide.connect(self.slide_prev_img)
        self._control_widget.slideshow_interval_changed.connect(
            self.on_slideshow_interval_changed)

        self._meta_files_mgr = MetaFilesManager()
        self._meta_files_mgr.connect()
Example #3
0
class ImageLoader(QObject):
    """
    <TODO>
    """
    load_scandir_success = Signal(object, object)
    load_scan_dir_info_success = Signal(object)
    load_images_success = Signal(object, object)

    batch_size = 50

    def __init__(self):
        super(ImageLoader, self).__init__()

        self._meta_files_mgr = MetaFilesManager()
        self._img_ext_filter = settings.get_allowed_image_formats()

    @Slot(int, int, int, object)
    def load_scandir(self, sd_id, serial, load_count, scrollDirection):
        """
        <TODO>
        """
        self._img_integrity_ts = start_time = time.time()
        self._meta_files_mgr.connect()

        LOGGER.debug('Load Scan Dir started.')

        dir_info = self._meta_files_mgr.get_scan_dir(sd_id)
        self.load_scan_dir_info_success.emit(dir_info)

        reverse = True if scrollDirection == ScrollDirection.Up else False
        images = self._meta_files_mgr.get_scan_dir_images_by_serial(
            sd_id, serial, load_count, reverse)

        self.load_images_success.emit(images, scrollDirection)

        # images = self._meta_files_mgr.get_scan_dir_images_by_serial(
        #     sd_id, serial, load_count)

        # # total_img_count = len(images)
        # if total_img_count < self.batch_size:
        #     self.load_images_success.emit(images)
        # else:
        #     batch_start_index = 0
        #     curr_img_count = 0
        #     for img in images:
        #         # print(batch_start_index)
        #         curr_img_count += 1
        #         if curr_img_count % 50 == 0 or curr_img_count == total_img_count:
        #             self.load_images_success.emit(
        #                 images[batch_start_index:curr_img_count])
        #             batch_start_index = curr_img_count

        # elapsed = round(time.time() - start_time, 2)
        # suffix = 'seconds'
        # if elapsed > 60:
        #     elapsed /= 60
        #     suffix = 'minutes'
        # LOGGER.debug('Loading Scan Dir completed in %.2f %s.' %
        #              (elapsed, suffix))

        # self.load_scandir_success.emit(elapsed, suffix)
        # self.free_resources()

    @Slot()
    def free_resources(self):
        self._meta_files_mgr.disconnect()
Example #4
0
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        self.setupUi(self)

        self.actiongrp_thumbs_size = QtWidgets.QActionGroup(self)
        self.actiongrp_thumbs_size.addAction(self.action_small_thumbs)
        self.actiongrp_thumbs_size.addAction(self.action_normal_thumbs)

        self.actiongrp_thumbs_caption = QtWidgets.QActionGroup(self)
        self.actiongrp_thumbs_caption.addAction(self.action_caption_none)
        self.actiongrp_thumbs_caption.addAction(self.action_caption_filename)
        self.action_caption_none.setChecked(True)

        # threads
        self._dir_watcher_thread = QThread()
        self._img_loader_thread = QThread()

        # helpers
        self._meta_files_mgr = MetaFilesManager()
        self._meta_files_mgr.connect()
        self._watch = Watcher()
        self._img_loader = ImageLoader()

        self.listView_thumbs = ThumbsListView(self.frame_thumbs)
        self.listView_thumbs.setFrameShape(QtWidgets.QFrame.NoFrame)
        self.vlayout_frame_thumbs.addWidget(self.listView_thumbs)

        # connections
        self._setup_connections()

        # Populates the folder list
        self._setup_scan_dir_list_model()

        # Properties widget
        self.vlayout_properties = QtWidgets.QVBoxLayout(
            self.toolbox_metadata_properties)
        self.vlayout_properties.setContentsMargins(0, 0, 0, 0)
        self.vlayout_properties.setSpacing(0)
        self.properties_widget = PropertiesWidget(
            self.toolbox_metadata_properties)
        self.vlayout_properties.addWidget(self.properties_widget)

        self.statusBar().showMessage("Ready")

        # settings
        self.hslider_thumb_size.setValue(
            settings.get(SettingType.UI_THUMBS_SIZE, 128, 'int'))

        thumb_caption_type = settings.get(
            SettingType.UI_THUMBS_CAPTION_DISPLAY_MODE,
            Thumb_Caption_Type.NoCaption.name)
        if thumb_caption_type == Thumb_Caption_Type.FileName.name:
            self.action_caption_filename.setChecked(True)

        if settings.get(SettingType.UI_METADATA_SHOW_PROPS, False, 'bool'):
            self.toolBox_metadata.setCurrentIndex(0)
            self.toolbutton_properties.setChecked(True)
            self.toolbutton_tags.setChecked(False)
            self.action_properties.setChecked(True)
            self.action_tags.setChecked(False)
            self.frame_metadata.show()
        elif settings.get(SettingType.UI_METADATA_SHOW_TAGS, False, 'bool'):
            self.toolBox_metadata.setCurrentIndex(1)
            self.toolbutton_properties.setChecked(False)
            self.toolbutton_tags.setChecked(True)
            self.action_properties.setChecked(False)
            self.action_tags.setChecked(True)
            self.frame_metadata.show()
        else:
            self.frame_metadata.hide()

        # Set event filters
        self.btn_slideshow.installEventFilter(self)

        self._curr_img_serial = 1
Example #5
0
class MainWindow(QMainWindow, Ui_MainWindow):

    _TV_FOLDERS_ITEM_MAP = {}

    # signals
    _dir_load_start = Signal(object)
    _dir_watcher_start = Signal()

    _loader_load_scandir = Signal(int, int, int, object)

    _is_watcher_running = False
    _update_mgr = None

    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        self.setupUi(self)

        self.actiongrp_thumbs_size = QtWidgets.QActionGroup(self)
        self.actiongrp_thumbs_size.addAction(self.action_small_thumbs)
        self.actiongrp_thumbs_size.addAction(self.action_normal_thumbs)

        self.actiongrp_thumbs_caption = QtWidgets.QActionGroup(self)
        self.actiongrp_thumbs_caption.addAction(self.action_caption_none)
        self.actiongrp_thumbs_caption.addAction(self.action_caption_filename)
        self.action_caption_none.setChecked(True)

        # threads
        self._dir_watcher_thread = QThread()
        self._img_loader_thread = QThread()

        # helpers
        self._meta_files_mgr = MetaFilesManager()
        self._meta_files_mgr.connect()
        self._watch = Watcher()
        self._img_loader = ImageLoader()

        self.listView_thumbs = ThumbsListView(self.frame_thumbs)
        self.listView_thumbs.setFrameShape(QtWidgets.QFrame.NoFrame)
        self.vlayout_frame_thumbs.addWidget(self.listView_thumbs)

        # connections
        self._setup_connections()

        # Populates the folder list
        self._setup_scan_dir_list_model()

        # Properties widget
        self.vlayout_properties = QtWidgets.QVBoxLayout(
            self.toolbox_metadata_properties)
        self.vlayout_properties.setContentsMargins(0, 0, 0, 0)
        self.vlayout_properties.setSpacing(0)
        self.properties_widget = PropertiesWidget(
            self.toolbox_metadata_properties)
        self.vlayout_properties.addWidget(self.properties_widget)

        self.statusBar().showMessage("Ready")

        # settings
        self.hslider_thumb_size.setValue(
            settings.get(SettingType.UI_THUMBS_SIZE, 128, 'int'))

        thumb_caption_type = settings.get(
            SettingType.UI_THUMBS_CAPTION_DISPLAY_MODE,
            Thumb_Caption_Type.NoCaption.name)
        if thumb_caption_type == Thumb_Caption_Type.FileName.name:
            self.action_caption_filename.setChecked(True)

        if settings.get(SettingType.UI_METADATA_SHOW_PROPS, False, 'bool'):
            self.toolBox_metadata.setCurrentIndex(0)
            self.toolbutton_properties.setChecked(True)
            self.toolbutton_tags.setChecked(False)
            self.action_properties.setChecked(True)
            self.action_tags.setChecked(False)
            self.frame_metadata.show()
        elif settings.get(SettingType.UI_METADATA_SHOW_TAGS, False, 'bool'):
            self.toolBox_metadata.setCurrentIndex(1)
            self.toolbutton_properties.setChecked(False)
            self.toolbutton_tags.setChecked(True)
            self.action_properties.setChecked(False)
            self.action_tags.setChecked(True)
            self.frame_metadata.show()
        else:
            self.frame_metadata.hide()

        # Set event filters
        self.btn_slideshow.installEventFilter(self)

        self._curr_img_serial = 1

    def resizeEvent(self, event):
        if event.spontaneous():
            # Begin loading the currently selected dir
            self._make_default_dir_list_selection()
            cur_sel_ids = self.get_current_selection_ids()
            if 'sd_id' in cur_sel_ids:
                self._load_dir_images(cur_sel_ids['sd_id'])
            # Start the dir watcher thread
            self.init_watch_thread()
            # Start the loader thread
            self.init_loader_thread()

    def closeEvent(self, event):
        LOGGER.debug('Shutting down gracefully....')
        self._meta_files_mgr.disconnect()
        settings.persist_to_disk()

        self._dir_watcher_thread.quit()
        self._dir_watcher_thread.wait()

        self._img_loader_thread.quit()
        self._img_loader_thread.wait()

    def _make_default_dir_list_selection(self):
        folders_index = self._dirs_list_model.indexFromItem(
            self._TV_FOLDERS_ITEM_MAP[0])
        if self._dirs_list_model.rowCount(folders_index) > 0:
            self._dirs_list_selection_model.select(
                self._dirs_list_model.index(0, 0).child(0, 0),
                QItemSelectionModel.Select | QItemSelectionModel.Rows)

    def _setup_connections(self):
        # Menu
        # File
        self.action_add_folder.triggered.connect(
            self.action_folder_manager_clicked)
        self.action_rescan.triggered.connect(self._run_watcher)
        self.action_file_locate.triggered.connect(
            self.handle_action_file_locate_triggered)
        self.action_exit.triggered.connect(self.action_exit_clicked)
        # View
        self.action_small_thumbs.triggered.connect(
            self.handle_action_small_thumbs_triggered)
        self.action_normal_thumbs.triggered.connect(
            self.handle_action_normal_thumbs_triggered)
        self.action_properties.triggered.connect(
            self.action_properties_clicked)
        self.action_tags.triggered.connect(self.action_tags_clicked)
        self.action_slideshow.triggered.connect(self.start_slideshow)
        self.action_caption_none.triggered.connect(
            self.handle_action_thumbnail_caption_none_triggered)
        self.action_caption_filename.triggered.connect(
            self.handle_action_thumbnail_caption_filename_triggered)
        # Folder
        self.action_folder_slideshow.triggered.connect(self.start_slideshow)
        self.action_folder_locate.triggered.connect(
            self.handle_action_folder_locate_triggered)
        # Picture
        self.action_picture_properties.triggered.connect(
            self.show_image_properties)
        # Tools
        self.action_settings.triggered.connect(
            self.handle_action_settings_triggered)
        self.action_folder_manager.triggered.connect(
            self.action_folder_manager_clicked)
        # Help
        self.action_check_updates.triggered.connect(
            self._handle_check_for_updates_clicked)

        # Btns
        self.btn_slideshow.clicked.connect(self.start_slideshow)
        self.hslider_thumb_size.valueChanged.connect(
            self.on_hslider_thumb_size_value_changed)

        # Watcher
        self._dir_watcher_start.connect(self._watch.watch_all)
        self._watch.new_img_found.connect(self.on_new_img_found)
        self._watch.watch_all_done.connect(self.on_watch_all_done)
        self._watch.dir_added_or_updated.connect(self.on_dir_added_or_updated)
        self._watch.dir_empty_or_deleted.connect(self.on_dir_empty_deleted)
        self._watch.watch_empty_or_deleted_done.connect(
            self.on_watch_dir_empty_deleted_done)

        # Loader
        self._loader_load_scandir.connect(self._img_loader.load_scandir)
        # self._loader_load_scanimg.connect(self._img_loader.load_scanimg)
        self._img_loader.load_scan_dir_info_success.connect(
            self._handle_load_scan_dir_info_success)
        # self._img_loader.load_images_success.connect(
        # self._handle_load_images_sucess)
        self._img_loader.load_images_success.connect(
            self.listView_thumbs.render_thumbs)

        # Tree View
        self.treeView_scandirs.clicked.connect(
            self.on_scan_dir_treeView_clicked)

        # Thumbs view
        self.listView_thumbs.clicked.connect(self.on_thumb_clicked)
        self.listView_thumbs.empty_area_clicked.connect(
            self.on_thumb_listview_empty_area_clicked)
        self.listView_thumbs.load_dir_images_for_scroll_up.connect(
            self.on_load_dir_images_for_scroll_up)
        self.listView_thumbs.load_dir_images_for_scroll_down.connect(
            self.on_load_dir_images_for_scroll_down)

        self.buttonGroup_metadata.buttonClicked.connect(
            self.on_buttongroup_metadata_clicked)

        self.txtbox_search.textEdited.connect(self.handle_search)

    def init_watch_thread(self):
        self._watch.moveToThread(self._dir_watcher_thread)
        self._dir_watcher_thread.start()
        self._run_watcher()
        LOGGER.debug('Watcher thread started.')

    def init_loader_thread(self):
        self._img_loader.moveToThread(self._img_loader_thread)
        self._img_loader_thread.start()
        LOGGER.debug('Loader thread started.')

    def _run_watcher(self):
        self._is_watcher_running = True
        self.action_rescan.setEnabled(False)
        self._dir_watcher_start.emit()

    def _populate_dirs_tree_view(self, parent_key, folders):
        parent_item = self._TV_FOLDERS_ITEM_MAP[parent_key]
        if folders:
            for idx, dir in enumerate(folders):
                item_title = "%s(%s)" % (dir['name'], dir['img_count'])
                item = QStandardItem(item_title)
                item.setData(dir['id'], QtCore.Qt.UserRole + 1)
                item.setSizeHint(QSize(item.sizeHint().width(), 24))
                item.setIcon(QIcon(':/images/icon_folder'))
                parent_item.appendRow(item)
                if parent_key == 0:
                    self._TV_FOLDERS_ITEM_MAP[dir['id']] = item
        self.treeView_scandirs.expandAll()

    def _setup_scan_dir_list_model(self):
        self._dirs_list_model = QStandardItemModel()
        self._dirs_list_selection_model = QItemSelectionModel(
            self._dirs_list_model)
        self._thumbs_view_model = QStandardItemModel()
        self._thumbs_selection_model = QItemSelectionModel(
            self._thumbs_view_model)

        self._dirs_list_model.setColumnCount(1)
        # self._dirs_list_model.setRowCount(len(scan_dirs))

        self._root_tree_item = self._dirs_list_model.invisibleRootItem()
        # FOLDERS item
        folder_item = QStandardItem("Folders")
        folder_item_font = QFont()
        folder_item_font.setBold(True)
        folder_item.setFont(folder_item_font)
        folder_item.setSizeHint(QSize(folder_item.sizeHint().width(), 24))
        self._root_tree_item.appendRow(folder_item)
        self._TV_FOLDERS_ITEM_MAP[0] = folder_item

        self.treeView_scandirs.setModel(self._dirs_list_model)
        self.treeView_scandirs.setSelectionModel(
            self._dirs_list_selection_model)
        # self.treeView_scandirs.setRootIsDecorated(False)

        self.listView_thumbs.setModel(self._thumbs_view_model)
        self.listView_thumbs.setSelectionModel(self._thumbs_selection_model)

        scan_dirs = self._meta_files_mgr.get_scan_dirs()
        self._populate_dirs_tree_view(0, scan_dirs)

    def _repopulate_scan_dir_list_model(self):
        self._clear_folders_tree_view()
        scan_dirs = self._meta_files_mgr.get_scan_dirs()
        self._populate_dirs_tree_view(0, scan_dirs)

    def _populate_search_tree_view(self, results):
        if 'search' not in self._TV_FOLDERS_ITEM_MAP:
            self._root_tree_item = self._dirs_list_model.invisibleRootItem()
            # FOLDERS item
            search_item = QStandardItem("Search")
            search_item_font = QFont()
            search_item_font.setBold(True)
            search_item.setFont(search_item_font)
            search_item.setSizeHint(QSize(search_item.sizeHint().width(), 24))
            self._root_tree_item.insertRow(0, search_item)
            self._TV_FOLDERS_ITEM_MAP['search'] = search_item
        self._populate_dirs_tree_view('search', results)

    def eventFilter(self, widget, event):
        if widget.objectName() == 'btn_slideshow':
            if event.type() == QtCore.QEvent.Enter:
                self.label_thumbs_toolbar_tooltip.setText(
                    "Play Fullscreen Slideshow")
            elif event.type() == QtCore.QEvent.Leave:
                self.label_thumbs_toolbar_tooltip.setText("")
        return QtWidgets.QWidget.eventFilter(self, widget, event)

    def handle_search(self, search_term):
        self._clear_search()
        if search_term != '':
            searches = self._meta_files_mgr.search_scan_dirs(search_term)
            self._populate_search_tree_view(searches)
        else:
            self._remove_search_tree_view()

    def handle_action_file_locate_triggered(self):
        curr_sel_ids = self.get_current_selection_ids()
        if 'sd_id' in curr_sel_ids and 'si_id' in curr_sel_ids:
            dr_img = self._meta_files_mgr.get_image_from_id(
                curr_sel_ids['si_id'], curr_sel_ids['sd_id'])
            explorer_process = QtCore.QProcess()
            explorer_process.setProgram('explorer.exe')
            explorer_process.setArguments([
                '/select,%s' %
                QtCore.QDir.toNativeSeparators(dr_img['abspath'])
            ])
            explorer_process.startDetached()

    def handle_action_folder_locate_triggered(self):
        curr_sel_ids = self.get_current_selection_ids()
        if 'sd_id' in curr_sel_ids:
            dr_sd = self._meta_files_mgr.get_scan_dir(curr_sel_ids['sd_id'])
            explorer_process = QtCore.QProcess()
            explorer_process.setProgram('explorer.exe')
            explorer_process.setArguments([
                '/select,%s' % QtCore.QDir.toNativeSeparators(dr_sd['abspath'])
            ])
            explorer_process.startDetached()

    def handle_action_small_thumbs_triggered(self):
        if self.action_small_thumbs.isChecked():
            self.hslider_thumb_size.triggerAction(
                self.hslider_thumb_size.SliderToMinimum)

    def handle_action_normal_thumbs_triggered(self):
        if self.action_normal_thumbs.isChecked():
            slider_value = self.hslider_thumb_size.value()
            if slider_value < 128:
                self.hslider_thumb_size.triggerAction(
                    self.hslider_thumb_size.SliderPageStepAdd)
            elif slider_value > 128 and slider_value <= 192:
                self.hslider_thumb_size.triggerAction(
                    self.hslider_thumb_size.SliderPageStepSub)
            elif slider_value > 192 and slider_value <= 256:
                self.hslider_thumb_size.triggerAction(
                    self.hslider_thumb_size.SliderPageStepSub)
                self.hslider_thumb_size.triggerAction(
                    self.hslider_thumb_size.SliderPageStepSub)

    def handle_action_thumbnail_caption_none_triggered(self):
        if self.action_caption_none.isChecked():
            settings.save(SettingType.UI_THUMBS_CAPTION_DISPLAY_MODE,
                          Thumb_Caption_Type.NoCaption.name)
            curr_sel_ids = self.get_current_selection_ids()
            if 'sd_id' in curr_sel_ids:
                self._load_dir_images(curr_sel_ids['sd_id'])

    def handle_action_thumbnail_caption_filename_triggered(self):
        if self.action_caption_filename.isChecked():
            settings.save(SettingType.UI_THUMBS_CAPTION_DISPLAY_MODE,
                          Thumb_Caption_Type.FileName.name)
            curr_sel_ids = self.get_current_selection_ids()
            if 'sd_id' in curr_sel_ids:
                self._load_dir_images(curr_sel_ids['sd_id'])

    def action_folder_manager_clicked(self):
        self.folder_mgr_window = FolderManagerWindow(self)
        self.folder_mgr_window.accepted.connect(
            self._on_folder_manager_window_accepted)
        self.folder_mgr_window.setModal(True)
        self.folder_mgr_window.show()

    def _on_folder_manager_window_accepted(self):
        self._repopulate_scan_dir_list_model()
        self._run_watcher()

    def action_properties_clicked(self):
        if self.action_properties.isChecked():
            curr_sel_ids = self.get_current_selection_ids()
            if 'sd_id' in curr_sel_ids and 'si_id' in curr_sel_ids:
                img_props = self._meta_files_mgr.get_img_properties(
                    curr_sel_ids['si_id'], curr_sel_ids['sd_id'])
                self.properties_widget.setup_properties(img_props)

            self.toolBox_metadata.setCurrentIndex(0)
            self.frame_metadata.show()
            self.toolbutton_properties.setChecked(True)
            self.toolbutton_tags.setChecked(False)
            self.action_tags.setChecked(False)
            settings.save(SettingType.UI_METADATA_SHOW_PROPS, True)
            settings.save(SettingType.UI_METADATA_SHOW_TAGS, False)
        else:
            self.frame_metadata.hide()
            self.toolbutton_properties.setChecked(False)
            settings.save(SettingType.UI_METADATA_SHOW_PROPS, False)
            settings.save(SettingType.UI_METADATA_SHOW_TAGS, False)
            # Forces the list view to re-render after the metadata window is hidden
            self._thumbs_view_model.layoutChanged.emit()

    def action_tags_clicked(self):
        if self.action_tags.isChecked():
            self.toolBox_metadata.setCurrentIndex(1)
            self.frame_metadata.show()
            self.toolbutton_tags.setChecked(True)
            self.toolbutton_properties.setChecked(False)
            self.action_properties.setChecked(False)
            settings.save(SettingType.UI_METADATA_SHOW_PROPS, False)
            settings.save(SettingType.UI_METADATA_SHOW_TAGS, True)
        else:
            self.frame_metadata.hide()
            self.toolbutton_tags.setChecked(False)
            settings.save(SettingType.UI_METADATA_SHOW_PROPS, False)
            settings.save(SettingType.UI_METADATA_SHOW_TAGS, False)
            # Forces the list view to re-render after the metadata window is hidden
            self._thumbs_view_model.layoutChanged.emit()

    def show_image_properties(self):
        curr_sel_ids = self.get_current_selection_ids()
        if 'sd_id' in curr_sel_ids and 'si_id' in curr_sel_ids:
            img_props = self._meta_files_mgr.get_img_properties(
                curr_sel_ids['si_id'], curr_sel_ids['sd_id'])
            self.properties_widget.setup_properties(img_props)

        self.toolBox_metadata.setCurrentIndex(0)
        self.frame_metadata.show()
        self.toolbutton_properties.setChecked(True)
        self.toolbutton_tags.setChecked(False)
        self.action_properties.setChecked(True)
        self.action_tags.setChecked(False)

    def handle_action_settings_triggered(self):
        self.settings_window = SettingsWindow(self)
        self.settings_window.setModal(True)
        self.settings_window.show()

    def on_buttongroup_metadata_clicked(self, button):
        if button.objectName() == 'toolbutton_tags':
            self.action_tags.trigger()
        elif button.objectName() == 'toolbutton_properties':
            self.action_properties.trigger()

    def on_hslider_thumb_size_value_changed(self, value):
        if self.hslider_thumb_size.value() == 64:
            self.action_small_thumbs.setChecked(True)
        elif self.hslider_thumb_size.value() == 128:
            self.action_normal_thumbs.setChecked(True)
        else:
            self.action_small_thumbs.setChecked(False)
            self.action_normal_thumbs.setChecked(False)

        self.listView_thumbs.setIconSize(QSize(value, value))
        self.listView_thumbs.setGridSize(QSize(value + 20, value + 20))

        settings.save(SettingType.UI_THUMBS_SIZE, value)

    def _handle_check_for_updates_clicked(self):
        if not self._update_mgr:
            self._update_mgr = UpdateManager()
        self._update_mgr.get_updates()

    def _load_dir_images(self, sd_id):
        self._clear_thumbs()
        load_count = self.listView_thumbs.get_visible_thumb_count(
            self.hslider_thumb_size.value() + 20)
        print("load count %d" % load_count)
        self._loader_load_scandir.emit(sd_id, 0, load_count,
                                       ScrollDirection.Down)
        QScroller.grabGesture(self.listView_thumbs.viewport(),
                              QScroller.LeftMouseButtonGesture)

    def _load_dir_images_for_scroll_up(self, sd_id, serial, count):
        # self._clear_thumbs()
        load_count = self.listView_thumbs.get_visible_thumb_count(
            self.hslider_thumb_size.value() + 20)
        self._loader_load_scandir.emit(sd_id, serial, count,
                                       ScrollDirection.Up)
        QScroller.grabGesture(self.listView_thumbs.viewport(),
                              QScroller.LeftMouseButtonGesture)

    def _load_dir_images_for_scroll_down(self, sd_id, serial, count):
        # self._clear_thumbs()
        load_count = self.listView_thumbs.get_visible_thumb_count(
            self.hslider_thumb_size.value() + 20)
        self._loader_load_scandir.emit(sd_id, serial, count,
                                       ScrollDirection.Down)
        QScroller.grabGesture(self.listView_thumbs.viewport(),
                              QScroller.LeftMouseButtonGesture)

    @Slot(int, int)
    def on_load_dir_images_for_scroll_up(self, serial, count):
        cur_sel_ids = self.get_current_selection_ids()
        if 'sd_id' in cur_sel_ids:
            self._load_dir_images_for_scroll_up(cur_sel_ids['sd_id'], serial,
                                                count)

    @Slot(int, int)
    def on_load_dir_images_for_scroll_down(self, serial, count):
        cur_sel_ids = self.get_current_selection_ids()
        if 'sd_id' in cur_sel_ids:
            self._load_dir_images_for_scroll_down(cur_sel_ids['sd_id'], serial,
                                                  count)

    def _handle_load_scan_dir_info_success(self, dir_info):
        self.lbl_dir_name.setText(dir_info['name'])

    @Slot(object, int)
    def _handle_load_images_sucess(self, images, scrollDirection):
        img_count = len(images)
        print('Recevied %s images' % img_count)
        LOGGER.debug('Recevied %s images' % img_count)
        for img in images:
            img['thumb'] = QImage.fromData(img['thumb'])
            item = QStandardItem()

            thumb_caption_type = settings.get(
                SettingType.UI_THUMBS_CAPTION_DISPLAY_MODE,
                Thumb_Caption_Type.NoCaption.name)
            if thumb_caption_type == Thumb_Caption_Type.FileName.name:
                item.setText(img['name'])

            item.setData(img['id'], QtCore.Qt.UserRole + 1)
            item.setData(img['serial'], QtCore.Qt.UserRole + 2)
            item.setIcon(QIcon(QPixmap.fromImage(img['thumb'])))
            item.setText(str(img['serial']))

            if scrollDirection == ScrollDirection.Up:
                self._thumbs_view_model.insertRow(0, item)
            if scrollDirection == ScrollDirection.Down:
                self._thumbs_view_model.appendRow(item)

    def _clear_thumbs(self):
        self._thumbs_view_model.clear()
        self.lbl_dir_name.setText('')

    def _tv_add_scan_dir(self, dir_info, highlight=False):
        item_title = "%s(%s)" % (dir_info['name'], dir_info['img_count'])
        item = QStandardItem(item_title)
        item.setData(dir_info['id'], QtCore.Qt.UserRole + 1)
        item.setSizeHint(QSize(item.sizeHint().width(), 24))
        item.setIcon(QIcon(':/images/icon_folder'))
        if highlight:
            bold_font = QFont()
            bold_font.setBold(True)
            item.setFont(bold_font)
        folder_item = self._TV_FOLDERS_ITEM_MAP[0]
        folder_item.appendRow(item)
        self._TV_FOLDERS_ITEM_MAP[dir_info['id']] = item

    @Slot()
    def start_slideshow(self):
        selected = self.treeView_scandirs.selectedIndexes()
        sd_id = selected[0].data(QtCore.Qt.UserRole + 1)

        img_serial = 1
        thumb_selected = self.listView_thumbs.selectedIndexes()
        if len(thumb_selected) > 0:
            img_serial = thumb_selected[0].data(QtCore.Qt.UserRole + 2)

        if sd_id > 0:
            self._slideshow = SlideshowWindow(sd_id, img_serial)
            self._slideshow.setWindowFlags(QtCore.Qt.CustomizeWindowHint
                                           | QtCore.Qt.FramelessWindowHint)
            self._slideshow.showFullScreen()

    @Slot(QModelIndex)
    def on_thumb_clicked(self, mindex):
        selected = self.treeView_scandirs.selectedIndexes()
        sd_id = selected[0].data(QtCore.Qt.UserRole + 1)
        si_id = mindex.data(QtCore.Qt.UserRole + 1)
        img_props = self._meta_files_mgr.get_img_properties(si_id, sd_id)
        self.lbl_selection_summary.setText(
            self.get_thumb_selection_summary(img_props))

        if self.action_properties.isChecked():
            self.properties_widget.setup_properties(img_props)

    @Slot()
    def on_thumb_listview_empty_area_clicked(self):
        selected_thumb = self.listView_thumbs.selectedIndexes()
        if len(selected_thumb) == 0:
            selected = self.treeView_scandirs.selectedIndexes()
            sd_id = selected[0].data(QtCore.Qt.UserRole + 1)
            if sd_id:
                props = self._meta_files_mgr.get_dir_properties(sd_id)
                self.lbl_selection_summary.setText(
                    self.get_dir_selection_summary(props))

    def get_dir_selection_summary(self, props):
        img_count = props['img_count']
        modified = props['modified'].toString('dd MMMM yyyy')
        size = props['size']
        return "%s pictures        %s        %s on disk" % (img_count,
                                                            modified, size)

    def get_thumb_selection_summary(self, props):
        filename = props['filename']
        modified = props['DateTime'] if 'DateTime' in props else ''
        dimensions = props['dimensions']
        filesize = props['filesize']
        return "%s        %s        %s        %s" % (filename, modified,
                                                     dimensions, filesize)

    @Slot(QModelIndex)
    def on_scan_dir_treeView_clicked(self, index):
        sd_id = index.data(QtCore.Qt.UserRole + 1)
        # Categories tree nodes will not contain 'data'
        if sd_id:
            item = self._TV_FOLDERS_ITEM_MAP[sd_id]
            bold_font = QFont()
            bold_font.setBold(False)
            item.setFont(bold_font)
            props = self._meta_files_mgr.get_dir_properties(sd_id)
            self.lbl_selection_summary.setText(
                self.get_dir_selection_summary(props))
            self._load_dir_images(sd_id)

    @Slot(object)
    def on_new_img_found(self, img_info):
        self.statusBar().showMessage("Found new image: %s - %s" %
                                     (img_info['dir'], img_info['filename']))

    @Slot(object)
    def on_dir_added_or_updated(self, dir_info):
        if dir_info['id'] in self._TV_FOLDERS_ITEM_MAP:
            item = self._TV_FOLDERS_ITEM_MAP[dir_info['id']]
            item_title = "%s(%s)" % (dir_info['name'], dir_info['img_count'])
            item.setText(item_title)
            bold_font = QFont()
            bold_font.setBold(True)
            item.setFont(bold_font)
        else:
            self._tv_add_scan_dir(dir_info, True)

    @Slot(object)
    def on_dir_empty_deleted(self, dir_info):
        if dir_info['id'] in self._TV_FOLDERS_ITEM_MAP:
            item = self._TV_FOLDERS_ITEM_MAP[dir_info['id']]
            item_index = self._dirs_list_model.indexFromItem(item)
            parent_index = self._dirs_list_model.indexFromItem(
                self._TV_FOLDERS_ITEM_MAP[0])
            self._dirs_list_model.removeRow(item_index.row(), parent_index)
            self._TV_FOLDERS_ITEM_MAP.pop(dir_info['id'])

    @Slot()
    def on_watch_dir_empty_deleted_done(self):
        # Here we make sure a folder is always selected if one more folders
        # ever get deleted by the watcher thread. If no folders exits, just
        # display an empty thumbs list
        cur_sel_ids = self.get_current_selection_ids()
        print(cur_sel_ids)
        if 'sd_id' not in cur_sel_ids:
            self._make_default_dir_list_selection()
            cur_sel_ids = self.get_current_selection_ids()
            if 'sd_id' in cur_sel_ids:
                self._load_dir_images(cur_sel_ids['sd_id'])
            else:
                self._clear_thumbs()

    @Slot(object, object)
    def on_watch_all_done(self, elapsed, suffix):
        self.statusBar().clearMessage()
        self._is_watcher_running = False
        self.action_rescan.setEnabled(True)
        self.statusBar().showMessage("Folder scan completed in %.2f %s" %
                                     (elapsed, suffix))

    def get_current_selection_ids(self):
        selected_ids = {}
        selected = self.treeView_scandirs.selectedIndexes()
        if len(selected) <= 0:
            return selected_ids
        selected_ids['sd_id'] = selected[0].data(QtCore.Qt.UserRole + 1)
        selected_thumb = self.listView_thumbs.selectedIndexes()
        if len(selected_thumb) <= 0:
            return selected_ids
        selected_ids['si_id'] = selected_thumb[0].data(QtCore.Qt.UserRole + 1)
        return selected_ids

    def action_exit_clicked(self):
        self.close()

    def _clear_search(self):
        if 'search' in self._TV_FOLDERS_ITEM_MAP:
            search_item = self._TV_FOLDERS_ITEM_MAP['search']
            search_item.removeRows(0, search_item.rowCount())

    def _clear_folders_tree_view(self):
        folder_item = self._TV_FOLDERS_ITEM_MAP[0]
        self._TV_FOLDERS_ITEM_MAP.clear()
        self._TV_FOLDERS_ITEM_MAP[0] = folder_item
        folder_item.removeRows(0, folder_item.rowCount())

    def _remove_search_tree_view(self):
        if 'search' in self._TV_FOLDERS_ITEM_MAP:
            root_tree_item = self._dirs_list_model.invisibleRootItem()
            root_tree_item.removeRow(0)
            self._TV_FOLDERS_ITEM_MAP.pop('search')

    def is_watcher_running(self):
        return self._is_watcher_running
Example #6
0
class SlideshowWindow(QWidget, Ui_SlideshowWindow):
    def __init__(self, sd_id, img_serial, parent=None):
        super(SlideshowWindow, self).__init__(parent)
        self.setupUi(self)

        self._curr_sd_id = sd_id
        self._curr_img_serial = img_serial
        self._is_slideshow_looped = settings.get(SettingType.SLIDESHOW_LOOP,
                                                 False, 'bool')
        self._slideshow_iterval = settings.get(SettingType.SLIDESHOW_INTERVAL,
                                               1000, 'int')
        self._mouse_idle_interval = 1000
        self._is_slideshow = True
        self._gfx_item = None
        self._last_mouse_pos = QtCore.QPoint()
        self._curr_mouse_pos = QtCore.QPoint()
        self._is_mouse_inside_controls = False

        self._shortcut_exit = QShortcut(QKeySequence(QtCore.Qt.Key_Escape),
                                        self)
        self._shortcut_exit.activated.connect(self.closeWindow)

        self._timer = QTimer()
        self._timer.setSingleShot(True)
        self._timer.timeout.connect(self.slide_next_img)

        self._idle_timer = QTimer()
        self._timer.setSingleShot(True)
        self._idle_timer.timeout.connect(self.hide_slideshow_controls)

        self.gfx_slide = SlideshowGraphicsView()
        self.gfx_slide.mouse_moved.connect(self.on_mouse_moved)
        self.horizontalLayout.addWidget(self.gfx_slide)

        self._gfx_scene = QtWidgets.QGraphicsScene()
        self.gfx_slide.setScene(self._gfx_scene)
        self.gfx_slide.setAlignment(QtCore.Qt.AlignCenter)

        self._control_widget = SlideshowControlWidget(self._slideshow_iterval)
        self._control_widget.stop_slideshow.connect(self.closeWindow)
        self._control_widget.start_slideshow.connect(self.slideshow_start)
        self._control_widget.next_slide.connect(self.slide_next_img)
        self._control_widget.prev_slide.connect(self.slide_prev_img)
        self._control_widget.slideshow_interval_changed.connect(
            self.on_slideshow_interval_changed)

        self._meta_files_mgr = MetaFilesManager()
        self._meta_files_mgr.connect()

    def resizeEvent(self, event):
        if event.spontaneous():
            self._control_widget_proxy = self._gfx_scene.addWidget(
                self._control_widget)
            self._control_widget_proxy.setY(
                self.gfx_slide.size().height() -
                self._control_widget_proxy.size().height())
            self._control_widget_proxy.setZValue(1)
            self._control_widget_proxy.setFlag(
                QtWidgets.QGraphicsItem.ItemIsFocusable, True)

            self.slideshow_start()

    @Slot(object)
    def on_mouse_moved(self, mouse_pos):
        if self.gfx_slide.mapFromScene(
                self._control_widget_proxy.geometry()).boundingRect().contains(
                    mouse_pos):
            self._is_mouse_inside_controls = True
        else:
            self._is_mouse_inside_controls = False

        self.show_slideshow_controls()
        self.slideshow_stop()
        self._idle_timer.stop()
        self._idle_timer.start(self._mouse_idle_interval)

    @Slot()
    def slideshow_start(self):
        self.hide_slideshow_controls()
        dr_img = self._meta_files_mgr.get_scan_dir_image(
            self._curr_sd_id, self._curr_img_serial)
        self.load_image(dr_img['abspath'])
        self._timer.start(self._slideshow_iterval)

    def slideshow_stop(self):
        self._timer.stop()

    @Slot()
    def slide_next_img(self):
        print(self._curr_img_serial)
        self._curr_img_serial += 1
        dr_img = self._meta_files_mgr.get_scan_dir_image(
            self._curr_sd_id, self._curr_img_serial)
        if not dr_img:
            if self._is_slideshow_looped:
                self._curr_img_serial = 0
                self._timer.start(self._slideshow_iterval)
            else:
                self._curr_img_serial -= 1
                self._timer.stop()
        else:
            self.load_image(dr_img['abspath'])
            if self._is_slideshow:
                self._timer.start(self._slideshow_iterval)

    @Slot()
    def slide_prev_img(self):
        self._curr_img_serial -= 1
        dr_img = self._meta_files_mgr.get_scan_dir_image(
            self._curr_sd_id, self._curr_img_serial)
        if not dr_img:
            self._curr_img_serial += 1
        else:
            self.load_image(dr_img['abspath'])

    @Slot(object)
    def on_slideshow_interval_changed(self, interval):
        self._slideshow_iterval = interval

    def load_image(self, abspath):
        if self._gfx_item:
            self._gfx_scene.removeItem(self._gfx_item)
        self.gfx_slide.viewport().update()
        self.gfx_slide.centerOn(0, 0)

        self._gfx_item = QGraphicsPixmapItem()
        img = QImage(abspath)
        pixmap = QPixmap(img)
        pixmap = pixmap.scaled(self.gfx_slide.size(),
                               QtCore.Qt.KeepAspectRatio,
                               QtCore.Qt.SmoothTransformation)
        self._control_widget_proxy.setX((pixmap.width() / 2) - (
            self._control_widget_proxy.size().width() / 2))
        self._gfx_item.setPixmap(pixmap)

        self._gfx_scene.addItem(self._gfx_item)
        self._gfx_scene.setSceneRect(self._gfx_item.sceneBoundingRect())

    @Slot()
    def show_slideshow_controls(self):
        self.setCursor(QtCore.Qt.ArrowCursor)
        self._control_widget.setMouseTracking(True)
        self._control_widget.show()

    @Slot()
    def hide_slideshow_controls(self):
        if not self._is_mouse_inside_controls:
            self.setCursor(QtCore.Qt.BlankCursor)
            self._control_widget.hide()

    @Slot()
    def closeWindow(self):
        self._timer.stop()
        self._meta_files_mgr.disconnect()
        self.close()
Example #7
0
class Watcher(QObject):
    """
    <TODO>
    """
    watch_all_done = Signal(object, object)
    new_img_found = Signal(object)
    dir_added_or_updated = Signal(object)
    dir_empty_or_deleted = Signal(object)
    watch_empty_or_deleted_done = Signal()

    def __init__(self):
        super(Watcher, self).__init__()

        self._meta_files_mgr = MetaFilesManager()

        self._img_ext_filter = settings.get_allowed_image_formats()

        print(self._img_ext_filter)

    @Slot()
    def watch_all(self):
        """
        <TODO>
        """
        self._img_integrity_ts = start_time = time.time()

        LOGGER.info('Watch all started.')

        self._meta_files_mgr.connect()

        self.scan_folders()

        elapsed = round(time.time() - start_time, 2)
        suffix = 'seconds'
        if elapsed > 60:
            elapsed /= 60
            suffix = 'minutes'
        LOGGER.info('Watch all completed in %.2f %s.' % (elapsed, suffix))

        self.watch_all_done.emit(elapsed, suffix)

        orphaned_scan_dirs = self._meta_files_mgr.get_orphaned_scan_dirs(
            self._img_integrity_ts)
        for dir in orphaned_scan_dirs:
            self.dir_empty_or_deleted.emit({'id': dir['id']})
            self._meta_files_mgr.prune_scan_dir(dir['id'])
        self.watch_empty_or_deleted_done.emit()

        self._meta_files_mgr.disconnect()

    def scan_folders(self):
        watched_folders = self._meta_files_mgr.get_watched_dirs()

        # Scan the watched directories.
        for idx, folder in enumerate(watched_folders):
            self.scan_folder(folder['id'], folder['abspath'], folder['name'])

        # TODO: Emit list of unclean entries for notification
        # self._meta_files_mgr.get_unclean_entries

        # Finally, remove the non-existent files
        # self._meta_files_mgr.clean_db(self._img_integrity_ts)

        LOGGER.debug("Folder scan completed.")

    def scan_folder(self, parent_id, abs_path, dir_name):
        """
        <TODO>
        """
        is_new_or_modified = False
        if not os.path.isdir(abs_path):
            return
        modified_time = os.path.getmtime(abs_path)
        sd_id = 0
        sd_info = self._meta_files_mgr.get_scan_dir_id(abs_path)
        if not sd_info or sd_info['id'] <= 0:
            sd_id = self._meta_files_mgr.add_scan_dir(parent_id, abs_path,
                                                      dir_name,
                                                      self._img_integrity_ts)
            sd_info = self._meta_files_mgr.get_scan_dir_id(abs_path)
            is_new_or_modified = True
        else:
            sd_id = sd_info['id']
            self._meta_files_mgr.update_scan_dir_integrity_check(
                sd_id, self._img_integrity_ts)
            if not sd_info['mtime'] or (modified_time > sd_info['mtime']):
                LOGGER.debug("Folder(%s):(%s) has changed since last scan." %
                             (sd_id, sd_info['abspath']))
                is_new_or_modified = True

        if is_new_or_modified is True:
            dir_iter = QDirIterator(
                abs_path, self._img_ext_filter,
                QDir.AllEntries | QDir.AllDirs | QDir.NoDotAndDotDot,
                QDirIterator.FollowSymlinks)
        else:
            dir_iter = QDirIterator(abs_path,
                                    QDir.AllDirs | QDir.NoDotAndDotDot,
                                    QDirIterator.FollowSymlinks)
        has_new_images = False
        img_serial = self._meta_files_mgr.get_scan_dir_img_next_serial(sd_id)
        while dir_iter.hasNext():
            dir_iter.next()
            file_info = dir_iter.fileInfo()

            if file_info.isDir():
                LOGGER.debug("Found Directory: %s" %
                             file_info.absoluteFilePath())
                self.scan_folder(parent_id, file_info.absoluteFilePath(),
                                 file_info.fileName())
            else:

                si_info = self._meta_files_mgr.get_image_id(
                    file_info.absoluteFilePath())

                # file exists
                if si_info and si_info['id'] > 0:
                    LOGGER.debug("Found Image: %s" %
                                 file_info.absoluteFilePath())
                    latest_mtime = os.path.getmtime(
                        file_info.absoluteFilePath())
                    if latest_mtime > si_info['mtime']:
                        self._meta_files_mgr.update_image_thumb(
                            si_info['id'], file_info.absoluteFilePath(),
                            latest_mtime, self._img_integrity_ts)
                    else:
                        self._meta_files_mgr.update_image(
                            si_info['id'], self._img_integrity_ts)

                # new file to add
                elif not si_info:
                    LOGGER.debug("Found New image: %s" %
                                 file_info.absoluteFilePath())
                    self.new_img_found.emit({
                        'dir': dir_name,
                        'filename': file_info.fileName()
                    })
                    self._meta_files_mgr.add_image(
                        sd_id, file_info.absoluteFilePath(),
                        file_info.fileName(), self._img_integrity_ts,
                        img_serial)
                    has_new_images = True
                    img_serial = img_serial + 1

        self._meta_files_mgr.commit()

        if is_new_or_modified is True:
            img_del_count = self._meta_files_mgr.prune_scan_img(
                sd_id, self._img_integrity_ts)
            img_count = self._meta_files_mgr.get_scan_dir_img_count(sd_id)
            self._meta_files_mgr.update_scan_dir_img_count(sd_id, img_count)
            if has_new_images or img_del_count > 0:
                self.dir_added_or_updated.emit({
                    'id': sd_id,
                    'name': dir_name,
                    'img_count': img_count
                })
            # Only update `mtime` if the scan_dir is new or modified
            if img_count > 0:
                self._meta_files_mgr.update_scan_dir_mtime(
                    sd_id, modified_time)
            else:
                self._meta_files_mgr.remove_scan_dir(sd_id)
                self.dir_empty_or_deleted.emit({'id': sd_id})
Example #8
0
def test__get_scan_dirs():
    mgr = MetaFilesManager()
    assert len(mgr.get_scan_dirs()) > 0