예제 #1
0
파일: gallery.py 프로젝트: haruiz/CvStudio
class Gallery(QWidget, QObject, Ui_Gallery):
    doubleClicked = pyqtSignal(GalleryCard, QWidget)
    filesDropped = pyqtSignal(list)
    cardActionClicked = pyqtSignal(str, object)

    def __init__(self, parent=None):
        super(Gallery, self).__init__(parent)
        self.setupUi(self)
        self.setup_toolbar()
        self.setup_paginator()
        self._items: [] = []
        self._pages = []
        self._page_size = 50
        self._curr_page = 0
        self._thread_pool = QThreadPool()
        self.setAcceptDrops(True)
        self.center_widget = None
        self.center_layout = None
        self._content_type = "Images"
        self._tag = None
        self._actions = []
        self._loading_dialog = QLoadingDialog(parent=self)

    def setup_toolbar(self):
        uncheck_all_icon = GUIUtilities.get_icon("uncheck_all.png")
        self.btn_uncheck_all = ImageButton(icon=uncheck_all_icon,
                                           size=QSize(20, 20))
        check_all_icon = GUIUtilities.get_icon("check_all.png")
        self.btn_check_all = ImageButton(icon=check_all_icon,
                                         size=QSize(20, 20))
        self.btn_check_all.setFixedWidth(40)
        self.btn_uncheck_all.setFixedWidth(40)
        self.btn_check_all.clicked.connect(self.btn_check_all_on_click_slot)
        self.btn_uncheck_all.clicked.connect(
            self.btn_uncheck_all_on_click_slot)

    @property
    def actions(self):
        return self._actions

    @actions.setter
    def actions(self, value):
        self._actions = value

    @property
    def content_type(self):
        return self._content_type

    @content_type.setter
    def content_type(self, value):
        self._content_type = value

    def enable_paginator(self, val):
        self.btn_check_all.setEnabled(val)
        self.btn_uncheck_all.setEnabled(val)
        self.btn_next_page.setEnabled(val)
        self.btn_prev_page.setEnabled(val)
        self.btn_last_page.setEnabled(val)
        self.btn_first_page.setEnabled(val)

    def setup_paginator(self):
        self.grid_actions_layout.addWidget(self.btn_check_all)
        self.grid_actions_layout.addWidget(self.btn_uncheck_all)
        self.btn_next_page.clicked.connect(self.btn_next_page_on_click)
        self.btn_prev_page.clicked.connect(self.btn_prev_page_on_click)
        self.btn_last_page.clicked.connect(self.btn_last_page_on_click)
        self.btn_first_page.clicked.connect(self.btn_first_page_on_click)
        self.btn_first_page.setIcon(GUIUtilities.get_icon("first.png"))
        self.btn_prev_page.setIcon(GUIUtilities.get_icon("left.png"))
        self.btn_next_page.setIcon(GUIUtilities.get_icon("right.png"))
        self.btn_last_page.setIcon(GUIUtilities.get_icon("last.png"))
        self.btn_first_page.setStyleSheet('QPushButton{border: 0px solid;}')
        self.btn_prev_page.setStyleSheet('QPushButton{border: 0px solid;}')
        self.btn_last_page.setStyleSheet('QPushButton{border: 0px solid;}')
        self.btn_next_page.setStyleSheet('QPushButton{border: 0px solid;}')
        self.grid_actions_layout.setAlignment(QtCore.Qt.AlignCenter)

    @property
    def tag(self):
        return self._tag

    @tag.setter
    def tag(self, value):
        self._tag = value

    @property
    def items(self):
        return self._items

    @items.setter
    def items(self, value):
        self._items = value

    @property
    def page_size(self):
        return self._page_size

    @page_size.setter
    def page_size(self, value):
        self._page_size = value
        self.update_pager()

    @property
    def current_page(self):
        return self._curr_page + 1

    @current_page.setter
    def current_page(self, val):
        self._curr_page = val
        self._curr_page = self._curr_page % self.total_pages
        self.lbl_current_page.setText(str(self.current_page))
        self.bind()

    @property
    def total_pages(self):
        return len(self._pages)

    def update_pager(self):
        self._pages = list(MiscUtilities.chunk(self._items, self._page_size))
        self.lbl_total_pages.setText("{}".format(len(self._pages)))
        self.lbl_current_page.setText(str(self.current_page))

    def btn_next_page_on_click(self):
        if len(self._pages) == 0:
            return
        self._curr_page += 1
        self.current_page = self._curr_page

    def btn_last_page_on_click(self):
        if len(self._pages) == 0:
            return
        self.current_page = len(self._pages) - 1

    def btn_first_page_on_click(self):
        if len(self._pages) == 0:
            return
        self.current_page = 0

    def btn_prev_page_on_click(self):
        if len(self._pages) == 0:
            return
        self._curr_page -= 1
        self.current_page = self._curr_page

    def dragEnterEvent(self, event: QtGui.QDragEnterEvent) -> None:
        data = event.mimeData()
        if data.hasUrls():
            if any(url.isLocalFile() for url in data.urls()):
                event.accept()
                return
        else:
            event.ignore()

    def dragMoveEvent(self, event: QtGui.QDragMoveEvent) -> None:
        if event.mimeData().hasUrls:
            event.setDropAction(QtCore.Qt.CopyAction)
            event.accept()
            return
        else:
            event.ignore()

    def dropEvent(self, event: QtGui.QDropEvent) -> None:
        valid_files = []
        files = [u.toLocalFile() for u in event.mimeData().urls()]
        for f in files:
            if os.path.isfile(f):
                mime_type, encoding = mimetypes.guess_type(
                    f)  # magic.from_file(f,mime=True)
                if mime_type.find(
                        "video") != -1 and self.content_type == "Videos":
                    valid_files.append(f)
                elif mime_type.find(
                        "image") != -1 and self.content_type == "Images":
                    valid_files.append(f)
        valid_files = sorted(valid_files, key=lambda v: os.path.basename(v))
        self.filesDropped.emit(valid_files)

    def load_images(self):
        def do_work():
            page = self._curr_page
            items = self._pages[page]

            def create_thumbnail(item):
                file_path = item.file_path
                if os.path.isfile(file_path):
                    image = cv2.imread(file_path)
                    h, w, _ = np.shape(image)
                    if w > h:
                        thumbnail_array = imutils.resize(image, width=150)
                    else:
                        thumbnail_array = imutils.resize(image, height=150)
                    thumbnail_array = cv2.cvtColor(thumbnail_array,
                                                   cv2.COLOR_BGR2RGB)
                    thumbnail = GUIUtilities.array_to_qimage(thumbnail_array)
                    thumbnail = QPixmap.fromImage(thumbnail)
                    del thumbnail_array
                    del image
                    return item, h, w, thumbnail, os.path.getsize(
                        file_path), False
                thumbnail = GUIUtilities.get_image("placeholder.png")
                thumbnail = thumbnail.scaledToHeight(100)
                h, w = thumbnail.height(), thumbnail.width()
                return item, h, w, thumbnail, 0, True

            delayed_tasks = [
                dask.delayed(create_thumbnail)(item) for item in items
            ]
            images = dask.compute(*delayed_tasks)
            return images

        def done_work(images):
            for img in images:
                if img:
                    item, h, w, thumbnail, file_size, is_broken = img
                    image_card = ImageCard()
                    image_card.is_broken = is_broken
                    image_card.tag = item
                    image_card.source = thumbnail
                    image_card.file_path = item.file_path
                    image_size_str = size(
                        file_size,
                        system=alternative) if file_size > 0 else "0 MB"
                    image_card.label.setText(
                        "\n ({0}px / {1}px) \n {2}".format(
                            w, h, image_size_str))
                    image_card.setFixedHeight(240)
                    image_card.doubleClicked.connect(
                        self.gallery_card_double_click)
                    image_card.add_buttons(self.actions)
                    if self.actions:
                        image_card.actionClicked.connect(
                            lambda name, item: self.cardActionClicked.emit(
                                name, item))
                    self.center_layout.add_item(image_card)

        def finished_work():
            self._loading_dialog.close()
            self.enable_paginator(True)

        worker = Worker(do_work)
        worker.signals.result.connect(done_work)
        worker.signals.finished.connect(finished_work)
        self._thread_pool.start(worker)
        self.enable_paginator(False)
        self._loading_dialog.show()

    def bind(self):
        self.update_pager()
        if len(self._pages) > 0:
            self.center_widget = QWidget()
            self.center_layout = GalleryLayout()
            self.center_widget.setLayout(self.center_layout)
            self.center_layout.setAlignment(QtCore.Qt.AlignTop)
            self.scrollArea.setWidget(self.center_widget)
            self.center_layout.initialize(n_items=self.page_size)
            if self.content_type == "Images":
                self.load_images()
            else:
                raise NotImplementedError
        else:
            self.center_widget = QWidget()
            self.center_layout = QVBoxLayout()
            self.center_widget.setLayout(self.center_layout)
            self.center_layout.setAlignment(QtCore.Qt.AlignCenter)
            self.center_layout.addWidget(QLabel("Drag and Drop your files"))
            self.scrollArea.setWidget(self.center_widget)

    def gallery_card_double_click(self, card: GalleryCard):
        self.doubleClicked.emit(card, self)

    def btn_check_all_on_click_slot(self):
        if self.items is None:
            return
        layout = self.scrollArea.widget().layout()
        for i in reversed(range(layout.count())):
            child = layout.itemAt(i)
            widget = child.widget()
            if isinstance(child.widget(), GalleryCard):
                widget.is_selected = True

    def btn_uncheck_all_on_click_slot(self):
        if self.items is None:
            return
        layout = self.scrollArea.widget().layout()
        for i in reversed(range(layout.count())):
            child = layout.itemAt(i)
            widget = child.widget()
            if isinstance(child.widget(), GalleryCard):
                widget.is_selected = False