Example #1
0
    def _on_item_click(self, action: QtGui.QAction):
        action_name = action.objectName()
        group = action.actionGroup()
        group_name = group.objectName() if group else None
        value: Optional[Any] = None
        setting = None

        if action_name == "close":
            self.com.on_close_in_settings.emit("Clicked close in settings")
        elif action_name == "message_languages":
            self.message_box.setText(MESSAGE_LANGUAGES)
            self.message_box.exec_()
        elif group_name == "website_group" or action_name.startswith("file:/"):
            url = action_name
            self.com.on_open_url.emit(url)
        elif group_name == "settings_group":
            setting = action_name
            value = action.isChecked()
        elif group_name == "mode_group":
            setting = "mode"
            value = action_name
        elif group_name == "language_group":
            setting = "language"
            languages = [
                a.objectName() for a in group.actions() if a.isChecked()
            ]
            if not languages:
                languages = [action_name]
                action.setChecked(True)
            value = languages

        if None not in [setting, value]:
            self.settings.setValue(str(setting), value)
Example #2
0
class MainWindow(QMainWindow):
    """Main application window"""
    def __init__(self) -> None:
        QMainWindow.__init__(self)
        self.setSizePolicy(
            QSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum))
        self.setMaximumSize(QSize(1920, 1080))
        self.setStyleSheet("padding: 0px; margin: 0px;")
        self.setIconSize(QSize(32, 32))
        self.setWindowTitle("BossyBot 2000 - Image Tagger")
        self.setWindowIcon(self.load_icon(icon))

        self.menubar = QMenuBar(self)
        self.menubar.setSizePolicy(EXP_MAX)
        self.menubar.setMaximumSize(QSize(INFINITE, 30))
        self.menu_file = QMenu('File', self.menubar)
        self.menu_options = QMenu('Options', self.menubar)
        self.menu_help = QMenu('Help', self.menubar)
        self.menubar.addAction(self.menu_file.menuAction())
        self.menubar.addAction(self.menu_options.menuAction())
        self.menubar.addAction(self.menu_help.menuAction())
        self.open = QAction('Open', self)
        self.menu_file.addAction(self.open)
        self.open.triggered.connect(self.open_file)
        self.exit_button = QAction('Exit', self)
        self.exit_button.triggered.connect(lambda: sys.exit(0),
                                           Qt.QueuedConnection)
        self.menu_file.addAction(self.exit_button)
        self.setMenuBar(self.menubar)

        self.previous_button = QAction(self.load_icon(previous), '<<', self)
        self.next_button = QAction(self.load_icon(next_icon), '>>', self)
        self.rotate_left_button = QAction(self.load_icon(left), '', self)
        self.rotate_right_button = QAction(self.load_icon(right), '', self)
        self.play_button = QAction(self.load_icon(play), '', self)
        self.play_button.setCheckable(True)
        self.delete_button = QAction(self.load_icon(delete), '', self)
        self.reload_button = QAction(self.load_icon(reload), '', self)
        self.mirror_button = QAction('Mirror', self)
        self.actual_size_button = QAction('Actual Size', self)
        self.browser_button = QAction('Browser', self)
        self.browser_button.setCheckable(True)
        self.browser_button.setChecked(True)
        self.crop_button = QAction('Crop', self)
        self.crop_button.setCheckable(True)

        self.toolbuttons = {
            self.rotate_left_button: {
                'shortcut':
                ',',
                'connect':
                lambda: self.pixmap.setRotation(self.pixmap.rotation() - 90)
            },
            self.rotate_right_button: {
                'shortcut':
                '.',
                'connect':
                lambda: self.pixmap.setRotation(self.pixmap.rotation() + 90)
            },
            self.delete_button: {
                'shortcut': 'Del',
                'connect': self.delete
            },
            self.previous_button: {
                'shortcut': 'Left',
                'connect': self.previous
            },
            self.play_button: {
                'shortcut': 'Space',
                'connect': self.play
            },
            self.next_button: {
                'shortcut': 'Right',
                'connect': self.next
            },
            self.reload_button: {
                'shortcut': 'F5',
                'connect': self.reload
            }
        }

        self.toolbar = QToolBar(self)
        self.toolbar.setSizePolicy(EXP_MAX)
        self.toolbar.setMaximumSize(QSize(INFINITE, 27))
        for _ in (self.browser_button, self.crop_button, self.mirror_button,
                  self.actual_size_button):
            self.toolbar.addAction(_)
        self.addToolBar(Qt.TopToolBarArea, self.toolbar)

        for button in self.toolbuttons:
            button.setShortcut(self.toolbuttons[button]['shortcut'])
            button.triggered.connect(self.toolbuttons[button]['connect'])
            self.toolbar.addAction(button)

        self.centralwidget = QWidget(self)
        self.centralwidget.setSizePolicy(EXP_EXP)
        self.setCentralWidget(self.centralwidget)
        self.grid = QGridLayout(self.centralwidget)

        self.media = QGraphicsScene(self)
        self.media.setItemIndexMethod(QGraphicsScene.NoIndex)
        self.media.setBackgroundBrush(QBrush(Qt.black))
        self.view = MyView(self.media, self)
        self.view.setSizePolicy(EXP_EXP)
        self.media.setSceneRect(0, 0, self.view.width(), self.view.height())
        self.grid.addWidget(self.view, 0, 0, 1, 1)

        self.frame = QFrame(self.centralwidget)
        self.frame.setSizePolicy(
            QSizePolicy(QSizePolicy.Fixed, QSizePolicy.MinimumExpanding))
        self.frame.setMinimumSize(QSize(325, 500))
        self.frame.setStyleSheet(
            "QFrame { border: 4px inset #222; border-radius: 10; }")

        self.layout_widget = QWidget(self.frame)
        self.layout_widget.setGeometry(QRect(0, 400, 321, 91))
        self.layout_widget.setContentsMargins(15, 15, 15, 15)

        self.grid2 = QGridLayout(self.layout_widget)
        self.grid2.setContentsMargins(0, 0, 0, 0)

        self.save_button = QPushButton('Yes (Save)', self.layout_widget)
        self.save_button.setSizePolicy(FIX_FIX)
        self.save_button.setMaximumSize(QSize(120, 26))
        self.save_button.setVisible(False)
        self.grid2.addWidget(self.save_button, 1, 0, 1, 1)

        self.no_save_button = QPushButton('No (Reload)', self.layout_widget)
        self.no_save_button.setSizePolicy(FIX_FIX)
        self.no_save_button.setMaximumSize(QSize(120, 26))
        self.no_save_button.setVisible(False)
        self.grid2.addWidget(self.no_save_button, 1, 1, 1, 1)

        self.label = QLabel("Current image modified, save it?",
                            self.layout_widget)
        self.label.setSizePolicy(FIX_FIX)
        self.label.setMaximumSize(QSize(325, 60))
        self.label.setVisible(False)
        self.label.setAlignment(Qt.AlignCenter)
        self.grid2.addWidget(self.label, 0, 0, 1, 2)

        self.layout_widget = QWidget(self.frame)
        self.layout_widget.setGeometry(QRect(0, 0, 321, 213))

        self.ass = QRadioButton('Ass', self.layout_widget)
        self.ass_exposed = QRadioButton('Ass (exposed)', self.layout_widget)
        self.ass_reset = QRadioButton(self.frame)
        self.ass_group = QButtonGroup(self)

        self.breasts = QRadioButton('Breasts', self.layout_widget)
        self.breasts_exposed = QRadioButton('Breasts (exposed)',
                                            self.layout_widget)
        self.breasts_reset = QRadioButton(self.frame)
        self.breasts_group = QButtonGroup(self)

        self.pussy = QRadioButton('Pussy', self.layout_widget)
        self.pussy_exposed = QRadioButton('Pussy (exposed)',
                                          self.layout_widget)
        self.pussy_reset = QRadioButton(self.frame)
        self.pussy_group = QButtonGroup(self)

        self.fully_clothed = QRadioButton('Fully Clothed', self.layout_widget)
        self.fully_nude = QRadioButton('Fully Nude', self.layout_widget)
        self.nudity_reset = QRadioButton(self.frame)
        self.nudity = QButtonGroup(self)

        self.smiling = QRadioButton('Smiling', self.layout_widget)
        self.glaring = QRadioButton('Glaring', self.layout_widget)
        self.expression_reset = QRadioButton(self.frame)
        self.expression = QButtonGroup(self)

        self.grid3 = QGridLayout(self.layout_widget)
        self.grid3.setVerticalSpacing(15)
        self.grid3.setContentsMargins(0, 15, 0, 0)

        self.radios = {
            self.ass: {
                'this': 'ass',
                'that': 'ass_exposed',
                'group': self.ass_group,
                'reset': self.ass_reset,
                'grid': (0, 0, 1, 1)
            },
            self.ass_exposed: {
                'this': 'ass_exposed',
                'that': 'ass',
                'group': self.ass_group,
                'reset': self.ass_reset,
                'grid': (0, 1, 1, 1)
            },
            self.breasts: {
                'this': 'breasts',
                'that': 'breasts_exposed',
                'group': self.breasts_group,
                'reset': self.breasts_reset,
                'grid': (1, 0, 1, 1)
            },
            self.breasts_exposed: {
                'this': 'breasts_exposed',
                'that': 'breasts',
                'group': self.breasts_group,
                'reset': self.breasts_reset,
                'grid': (1, 1, 1, 1)
            },
            self.pussy: {
                'this': 'pussy',
                'that': 'pussy_exposed',
                'group': self.pussy_group,
                'reset': self.pussy_reset,
                'grid': (2, 0, 1, 1)
            },
            self.pussy_exposed: {
                'this': 'pussy_exposed',
                'that': 'pussy',
                'group': self.pussy_group,
                'reset': self.pussy_reset,
                'grid': (2, 1, 1, 1)
            },
            self.fully_clothed: {
                'this': 'fully_clothed',
                'that': 'fully_nude',
                'group': self.nudity,
                'reset': self.nudity_reset,
                'grid': (3, 0, 1, 1)
            },
            self.fully_nude: {
                'this': 'fully_nude',
                'that': 'fully_clothed',
                'group': self.nudity,
                'reset': self.nudity_reset,
                'grid': (3, 1, 1, 1)
            },
            self.smiling: {
                'this': 'smiling',
                'that': 'glaring',
                'group': self.expression,
                'reset': self.expression_reset,
                'grid': (4, 0, 1, 1)
            },
            self.glaring: {
                'this': 'glaring',
                'that': 'smiling',
                'group': self.expression,
                'reset': self.expression_reset,
                'grid': (4, 1, 1, 1)
            },
        }

        for radio in self.radios:
            radio.setSizePolicy(FIX_FIX)
            radio.setMaximumSize(QSize(150, 22))
            self.radios[radio]['reset'].setGeometry(QRect(0, 0, 0, 0))
            self.grid3.addWidget(radio, *self.radios[radio]['grid'])
            if self.radios[radio]['group'] != self.nudity:
                radio.toggled.connect(
                    lambda x=_, y=radio: self.annotate(self.radios[y]['this']))
            self.radios[radio]['group'].addButton(radio)
            self.radios[radio]['group'].addButton(self.radios[radio]['reset'])

        self.save_tags_button = QPushButton('Save Tags', self.layout_widget)
        self.save_tags_button.setSizePolicy(FIX_FIX)
        self.save_tags_button.setMaximumSize(QSize(120, 26))
        self.grid3.addWidget(self.save_tags_button, 5, 1, 1, 1)

        self.grid.addWidget(self.frame, 0, 1, 1, 1)

        self.browse_bar = QLabel(self.centralwidget)
        self.browse_bar.setSizePolicy(EXP_FIX)
        self.browse_bar.setMinimumSize(QSize(0, 100))
        self.browse_bar.setMaximumSize(QSize(INFINITE, 100))
        self.browse_bar.setStyleSheet("background: #000;")
        self.browse_bar.setAlignment(Qt.AlignCenter)
        self.h_box2 = QHBoxLayout(self.browse_bar)
        self.h_box2.setContentsMargins(4, 0, 0, 0)

        self.grid.addWidget(self.browse_bar, 1, 0, 1, 2)

        hiders = [
            self.no_save_button.clicked, self.save_button.clicked,
            self.reload_button.triggered
        ]
        for hider in hiders:
            hider.connect(self.save_button.hide)
            hider.connect(self.no_save_button.hide)
            hider.connect(self.label.hide)
        showers = [
            self.mirror_button.triggered, self.rotate_right_button.triggered,
            self.rotate_left_button.triggered
        ]
        for shower in showers:
            shower.connect(self.save_button.show)
            shower.connect(self.no_save_button.show)
            shower.connect(self.label.show)

        self.no_save_button.clicked.connect(self.reload)
        self.browser_button.toggled.connect(self.browse_bar.setVisible)

        self.play_button.toggled.connect(lambda: self.frame.setVisible(
            (True, False)[self.frame.isVisible()]))
        self.reload_button.triggered.connect(self.reload)
        self.mirror_button.triggered.connect(lambda: self.pixmap.setScale(-1))
        self.save_button.clicked.connect(self.save_image)
        self.play_button.toggled.connect(
            lambda: self.browser_button.setChecked(
                (True, False)[self.browse_bar.isVisible()]))
        self.crop_button.toggled.connect(self.view.reset)
        self.actual_size_button.triggered.connect(self.actual_size)
        self.browser_button.triggered.connect(self.browser)
        self.save_tags_button.clicked.connect(self.save_tags)
        self.view.got_rect.connect(self.set_rect)

        self.crop_rect = QRect(QPoint(0, 0), QSize(0, 0))
        self.dir_now = os.getcwd()
        self.files = []
        self.index = 0
        self.refresh_files()
        self.pixmap_is_scaled = False
        self.pixmap = QGraphicsPixmapItem()
        self.active_tag = ''
        self.reset_browser = False
        self.txt = PngInfo()

    def set_rect(self, rect: tuple[QPointF, QPointF]):
        """Converts the crop rectangle to a QRect after a crop action"""
        self.crop_rect = QRect(rect[0].toPoint(), rect[1].toPoint())

    def keyPressEvent(self, event: QKeyEvent):  # pylint: disable=invalid-name;
        """Keyboard event handler."""
        if event.key() == Qt.Key_Escape and self.play_button.isChecked():
            self.play_button.toggle()
            self.browser_button.setChecked((True, False)[self.reset_browser])
        elif (event.key() in [16777220, 16777221]
              and self.view.g_rect.rect().width() > 0):
            self.view.got_rect.emit((self.view.g_rect.rect().topLeft(),
                                     self.view.g_rect.rect().bottomRight()))
            if self.view.g_rect.pen().color() == Qt.red:
                new_pix = self.pixmap.pixmap().copy(self.crop_rect)
                if self.pixmap_is_scaled:
                    new_pix = new_pix.transformed(
                        self.view.transform().inverted()[0],
                        Qt.SmoothTransformation)
                self.update_pixmap(new_pix)
            elif self.view.g_rect.pen().color() == Qt.magenta:
                self.annotate_rect()
                self.view.annotation = False
            for _ in (self.label, self.save_button, self.no_save_button):
                _.show()
            self.view.reset()

    def play(self):
        """Starts a slideshow."""
        if self.play_button.isChecked():
            if self.browser_button.isChecked():
                self.reset_browser = True
            else:
                self.reset_browser = False
            QTimer.singleShot(3000, self.play)
            self.next()

    def _yield_radio(self):
        """Saves code connecting signals from all the radio buttons."""
        yield from self.radios.keys().__str__()

    def load_icon(self, icon_file):
        """Loads an icon from Base64 encoded strings in icons.py."""
        pix = QPixmap()
        pix.loadFromData(icon_file)
        return QIcon(pix)

    def open_file(self, file: str) -> None:
        """
        Open an image file and display it.

        :param file: The filename of the image to open
        """
        if not os.path.isfile(file):
            file = QFileDialog(self, self.dir_now,
                               self.dir_now).getOpenFileName()[0]
            self.dir_now = os.path.dirname(file)
            self.refresh_files()
        for i, index_file in enumerate(self.files):
            if file.split('/')[-1] == index_file:
                self.index = i
        self.view.setTransform(QTransform())
        self.update_pixmap(QPixmap(file))
        self.browser()
        self.load_tags()

    def refresh_files(self) -> list[str]:
        """Updates the file list when the directory is changed.
        Returns a list of image files available in the current directory."""
        files = os.listdir(self.dir_now)
        self.files = [
            file for file in sorted(files, key=lambda x: x.lower())
            if file.endswith((".png", ".jpg", ".gif", ".bmp", ".jpeg"))
        ]

    def next(self) -> None:
        """Opens the next image in the file list."""
        self.index = (self.index + 1) % len(self.files)
        self.reload()

    def previous(self) -> None:
        """Opens the previous image in the file list."""
        self.index = (self.index + (len(self.files) - 1)) % len(self.files)
        self.reload()

    def save_image(self) -> None:
        """
        Save the modified image file.  If the current pixmap has been
        scaled, we need to load a non-scaled pixmap from the original file and
        re-apply the transformations that have been performed to prevent it
        from being saved as the scaled-down image.
        """
        if self.pixmap_is_scaled:
            rotation = self.pixmap.rotation()
            mirror = self.pixmap.scale() < 0
            pix = QPixmap(self.files[self.index])
            pix = pix.transformed(QTransform().rotate(rotation))
            if mirror:
                pix = pix.transformed(QTransform().scale(-1, 1))
            pix.save(self.files[self.index], quality=-1)
        else:
            self.pixmap.pixmap().save(self.files[self.index], quality=-1)
        self.save_tags()

    def delete(self) -> None:
        """Deletes the current image from the file system."""
        with suppress(OSError):
            os.remove(f"{self.dir_now}/{self.files.pop(self.index)}")
        self.refresh_files()

    def reload(self) -> None:
        """Reloads the current pixmap; used to update the screen when the
        current file is changed."""
        self.open_file(f"{self.dir_now}/{self.files[self.index]}")

    def annotate(self, tag):
        """Starts an annotate action"""
        self.txt = PngInfo()
        self.view.annotation = True
        self.active_tag = tag
        self.view.reset()

    def wheelEvent(self, event: QWheelEvent) -> None:  # pylint: disable=invalid-name
        """With Ctrl depressed, zoom the current image, otherwise fire the
        next/previous functions."""
        modifiers = QApplication.keyboardModifiers()
        if event.angleDelta().y() == 120 and modifiers == Qt.ControlModifier:
            self.view.scale(0.75, 0.75)
        elif event.angleDelta().y() == 120:
            self.previous()
        elif event.angleDelta().y(
        ) == -120 and modifiers == Qt.ControlModifier:
            self.view.scale(1.25, 1.25)
        elif event.angleDelta().y() == -120:
            self.next()

    def actual_size(self) -> None:
        """Display the current image at its actual size, rather than scaled to
        fit the viewport."""
        self.update_pixmap(QPixmap(self.files[self.index]), False)
        self.view.setDragMode(QGraphicsView.ScrollHandDrag)

    def mousePressEvent(self, event: QMouseEvent) -> None:  # pylint: disable=invalid-name
        """Event handler for mouse button presses."""
        if event.button() == Qt.MouseButton.ForwardButton:
            self.next()
        elif event.button() == Qt.MouseButton.BackButton:
            self.previous()

    def update_pixmap(self, new: QPixmap, scaled: bool = True) -> None:
        """
        Updates the currently displayed image.

        :param new: The new `QPixmap` to be displayed.
        :param scaled: If False, don't scale the image to fit the viewport.
        """
        self.pixmap_is_scaled = scaled
        self.media.clear()
        self.pixmap = self.media.addPixmap(new)
        self.pixmap.setTransformOriginPoint(
            self.pixmap.boundingRect().width() / 2,
            self.pixmap.boundingRect().height() / 2)
        if scaled and (new.size().width() > self.view.width()
                       or new.size().height() > self.view.height()):
            self.view.fitInView(self.pixmap, Qt.KeepAspectRatio)
        self.media.setSceneRect(self.pixmap.boundingRect())

    def annotate_rect(self):
        """Creates image coordinate annotation data."""
        self.txt.add_itxt(
            f'{str(self.active_tag)}-rect',
            f'{str(self.crop_rect.x())}, {str(self.crop_rect.y())}, {str(self.crop_rect.width())}, {str(self.crop_rect.height())}'
        )

    def browser(self):
        """Slot function to initialize image thumbnails for the
        'browse mode.'"""
        while self.h_box2.itemAt(0):
            self.h_box2.takeAt(0).widget().deleteLater()
        index = (self.index + (len(self.files) - 2)) % len(self.files)
        for i, file in enumerate(self.files):
            file = self.dir_now + '/' + self.files[index]
            label = ClickableLabel(self, file)
            self.h_box2.addWidget(label)
            pix = QPixmap(file)
            if (pix.size().width() > self.browse_bar.width() / 5
                    or pix.size().height() > 100):
                pix = pix.scaled(self.browse_bar.width() / 5, 100,
                                 Qt.KeepAspectRatio)
            label.setPixmap(pix)
            index = (index + 1) % len(self.files)
            if i == 4:
                break

    def save_tags(self):
        """Save tags for currently loaded image into its iTxt data."""
        file = self.files[self.index]
        img = Image.open(file)
        img.load()
        for key, value, in img.text.items():
            self.txt.add_itxt(key, value)
        for key in self.radios:
            if key.isChecked():
                self.txt.add_itxt(self.radios[key]['this'], 'True')
                self.txt.add_itxt(self.radios[key]['that'], 'False')
        img.save(file, pnginfo=self.txt)

    def load_tags(self):
        """Load tags from iTxt data."""
        for radio in self.radios:
            if radio.isChecked():
                self.radios[radio]['reset'].setChecked(True)
        filename = self.files[self.index]
        fqp = filename
        img = Image.open(fqp)
        img.load()
        with suppress(AttributeError):
            for key, value in img.text.items():
                if value == 'True':
                    for radio in self.radios:
                        if key == self.radios[radio]['this']:
                            radio.setChecked(True)
                            self.view.annotation = False
                            self.active_tag = ''
                            self.view.reset()
            for key, value in img.text.items():
                if key.endswith('-rect'):
                    btn = [
                        radio for radio in self.radios
                        if self.radios[radio]['this'] == key.split('-')[0]
                    ]
                    print(key, value)
                    if btn[0].isChecked():
                        coords = [int(coord) for coord in value.split(', ')]
                        rect = QGraphicsRectItem(*coords)
                        rect.setPen(QPen(Qt.magenta, 1, Qt.SolidLine))
                        rect.setBrush(QBrush(Qt.magenta, Qt.Dense4Pattern))
                        self.view.scene().addItem(rect)
                        text = self.view.scene().addText(
                            key.split('-')[0],
                            QFont('monospace', 20, 400, False))
                        text.font().setPointSize(text.font().pointSize() * 2)
                        text.update()
                        text.setX(rect.rect().x() + 10)
                        text.setY(rect.rect().y() + 10)
                        print(f'set {key}')
Example #3
0
class MainWindow(QMainWindow):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)

        self.settings_tree = SettingsTree()
        self.setCentralWidget(self.settings_tree)

        self.location_dialog = None

        self.create_actions()
        self.create_menus()

        self.auto_refresh_action.setChecked(True)
        self.fallbacks_action.setChecked(True)

        self.setWindowTitle("Settings Editor")
        self.resize(500, 600)

    def open_settings(self):
        if self.location_dialog is None:
            self.location_dialog = LocationDialog(self)

        if self.location_dialog.exec_():
            settings = QSettings(self.location_dialog.format(),
                                 self.location_dialog.scope(),
                                 self.location_dialog.organization(),
                                 self.location_dialog.application())
            self.set_settings_object(settings)
            self.fallbacks_action.setEnabled(True)

    def open_inifile(self):
        file_name, _ = QFileDialog.getOpenFileName(self, "Open INI File",
                '', "INI Files (*.ini *.conf)")

        if file_name:
            self.load_ini_file(file_name)

    def load_ini_file(self, file_name):
        settings = QSettings(file_name, QSettings.IniFormat)
        if settings.status() != QSettings.NoError:
            return
        self.set_settings_object(settings)
        self.fallbacks_action.setEnabled(False)

    def open_property_list(self):
        file_name, _ = QFileDialog.getOpenFileName(self,
                "Open Property List", '', "Property List Files (*.plist)")

        if file_name:
            settings = QSettings(file_name, QSettings.NativeFormat)
            self.set_settings_object(settings)
            self.fallbacks_action.setEnabled(False)

    def open_registry_path(self):
        path, ok = QInputDialog.getText(self, "Open Registry Path",
                "Enter the path in the Windows registry:",
                QLineEdit.Normal, 'HKEY_CURRENT_USER\\')

        if ok and path != '':
            settings = QSettings(path, QSettings.NativeFormat)
            self.set_settings_object(settings)
            self.fallbacks_action.setEnabled(False)

    def about(self):
        QMessageBox.about(self, "About Settings Editor",
                "The <b>Settings Editor</b> example shows how to access "
                "application settings using Qt.")

    def createActions(self):
        self.openSettingsAct = QtGui.QAction("&Open Application Settings...",
                self, shortcut="Ctrl+O", triggered=self.openSettings)

        self.openIniFileAct = QtGui.QAction("Open I&NI File...", self,
                shortcut="Ctrl+N", triggered=self.openIniFile)

        self.openPropertyListAct = QtGui.QAction("Open macOS &Property List...",
                self, shortcut="Ctrl+P", triggered=self.openPropertyList)

    def create_actions(self):
        self.open_settings_action = QAction("&Open Application Settings...",
                self, shortcut="Ctrl+O", triggered=self.open_settings)

        self.open_ini_file_action = QAction("Open I&NI File...", self,
                shortcut="Ctrl+N", triggered=self.open_inifile)

        self.open_property_list_action = QAction("Open macOS &Property List...",
                self, shortcut="Ctrl+P", triggered=self.open_property_list)
        if sys.platform != 'darwin':
            self.open_property_list_action.setEnabled(False)

        self.open_registry_path_action = QAction(
                "Open Windows &Registry Path...", self, shortcut="Ctrl+G",
                triggered=self.open_registry_path)
        if sys.platform != 'win32':
            self.open_registry_path_action.setEnabled(False)

        self.refresh_action = QAction("&Refresh", self, shortcut="Ctrl+R",
                enabled=False, triggered=self.settings_tree.refresh)

        self.exit_action = QAction("E&xit", self, shortcut="Ctrl+Q",
                triggered=self.close)

        self.auto_refresh_action = QAction("&Auto-Refresh", self,
                shortcut="Ctrl+A", checkable=True, enabled=False)
        self.auto_refresh_action.triggered[bool].connect(self.settings_tree.set_auto_refresh)
        self.auto_refresh_action.triggered[bool].connect(self.refresh_action.setDisabled)

        self.fallbacks_action = QAction("&Fallbacks", self,
                shortcut="Ctrl+F", checkable=True, enabled=False)
        self.fallbacks_action.triggered[bool].connect(self.settings_tree.set_fallbacks_enabled)

        self.about_action = QAction("&About", self, triggered=self.about)

        self.about_Qt_action = QAction("About &Qt", self,
                                       triggered=qApp.aboutQt)

    def create_menus(self):
        self.file_menu = self.menuBar().addMenu("&File")
        self.file_menu.addAction(self.open_settings_action)
        self.file_menu.addAction(self.open_ini_file_action)
        self.file_menu.addAction(self.open_property_list_action)
        self.file_menu.addAction(self.open_registry_path_action)
        self.file_menu.addSeparator()
        self.file_menu.addAction(self.refresh_action)
        self.file_menu.addSeparator()
        self.file_menu.addAction(self.exit_action)

        self.options_menu = self.menuBar().addMenu("&Options")
        self.options_menu.addAction(self.auto_refresh_action)
        self.options_menu.addAction(self.fallbacks_action)

        self.menuBar().addSeparator()

        self.help_menu = self.menuBar().addMenu("&Help")
        self.help_menu.addAction(self.about_action)
        self.help_menu.addAction(self.about_Qt_action)

    def set_settings_object(self, settings):
        settings.setFallbacksEnabled(self.fallbacks_action.isChecked())
        self.settings_tree.set_settings_object(settings)

        self.refresh_action.setEnabled(True)
        self.auto_refresh_action.setEnabled(True)

        nice_name = QDir.fromNativeSeparators(settings.fileName())
        nice_name = nice_name.split('/')[-1]

        if not settings.isWritable():
            nice_name += " (read only)"

        self.setWindowTitle("{} - Settings Editor".format(nice_name))
Example #4
0
class Window(QMainWindow):
    def __init__(self):
        QMainWindow.__init__(self)

        self.images = []
        self.index = -1
        self.ratio = 1  # ratio for QLabel image
        self.mouse_position = None
        self.settings = None

        # Extensions
        self.extensions = []
        for format in QImageReader.supportedImageFormats():
            self.extensions.append(format.data().decode('utf-8'))

        # Filters
        self.filters = []
        for extension in self.extensions:
            self.filters.append('*.{0}'.format(str(extension)))

        # UI
        self.set_up_ui()

        # settings
        self.load_settings()

    def on_message_received(self, msg):
        """ on message received from single application

        Args:
            msg (string): file path
        """
        self.create_images(msg)
        self.display_image()

    def set_up_ui(self):
        # Status Bar
        self.status_bar = self.statusBar()
        self.label_name = QLabel()
        self.label_numero = QLabel()
        self.status_bar.addPermanentWidget(self.label_name, 1)
        self.status_bar.addPermanentWidget(self.label_numero, 0)

        # Main Window
        self.setWindowTitle('BaloViewer')
        self.setWindowIcon(QIcon('baloviewer.ico'))

        # Label image
        self.image = QLabel()
        self.image.setScaledContents(True)

        # Scroll area
        self.scroll_area = QScrollArea()
        self.scroll_area.setWidget(self.image)
        self.scroll_area.showMaximized()
        self.scroll_area.setFocusPolicy(Qt.FocusPolicy.NoFocus)
        self.scroll_area.setAlignment(Qt.AlignmentFlag.AlignCenter)
        self.scroll_area.viewport().installEventFilter(self)

        # image list
        self.image_gallery = ImageGallery()
        self.image_gallery.itemClicked.connect(self.image_gallery_clicked)
        self.image_gallery.viewport().installEventFilter(self)
        self.dock_widget = QDockWidget('Image Gallery', self)
        self.dock_widget.setWidget(self.image_gallery)
        self.dock_widget.setFloating(False)
        self.addDockWidget(Qt.LeftDockWidgetArea, self.dock_widget)

        # central widget
        self.setCentralWidget(self.scroll_area)

        # Action bar
        self.create_actions()
        self.create_menubar()
        self.create_toolbar()

        # option parser
        parser = OptionParser()
        parser.add_option("-f", "--file", dest="filename", help="open a file")
        (options, args) = parser.parse_args()
        parser_file = options.filename
        if parser_file is not None and os.path.isfile(parser_file):
            self.create_images(parser_file)
            self.display_image()

    def create_actions(self):
        # Action Open
        self.action_open = QAction(QIcon.fromTheme('document-open'), 'Open',
                                   self)
        self.action_open.setShortcut('Ctrl+O')
        self.action_open.setStatusTip('Open file')
        self.action_open.triggered.connect(self.open)

        # Action Save
        self.action_save = QAction(QIcon.fromTheme('document-save'), 'Save',
                                   self)
        self.action_save.setShortcut('Ctrl+S')
        self.action_save.setStatusTip('Save file')
        self.action_save.triggered.connect(self.save)

        # Action Copy
        self.action_copy = QAction(QIcon.fromTheme('edit-copy'), 'Copy', self)
        self.action_copy.setStatusTip('Copy')
        self.action_copy.triggered.connect(self.copy)

        # Action move
        self.action_move = QAction(QIcon.fromTheme('edit-cut'), 'Move', self)
        self.action_move.setStatusTip('Move')
        self.action_move.triggered.connect(self.move)

        # Action Delete
        self.action_delete = QAction(QIcon.fromTheme('edit-delete'), 'Delete',
                                     self)
        self.action_delete.setStatusTip('Delete')
        self.action_delete.triggered.connect(self.delete)

        # Action Quit
        self.action_quit = QAction(QIcon.fromTheme('application-exit'), 'Quit',
                                   self)
        self.action_quit.setShortcut('Ctrl+Q')
        self.action_quit.setStatusTip('Quit')
        self.action_quit.triggered.connect(self.close)

        # Action Rotate left
        self.action_rotate_left = QAction(
            QIcon.fromTheme('object-rotate-left'), 'Rotate left', self)
        self.action_rotate_left.setStatusTip('Rotate left')
        self.action_rotate_left.triggered.connect(self.rotate_left)

        # Action Rotate right
        self.action_rotate_right = QAction(
            QIcon.fromTheme('object-rotate-right'), 'Rotate right', self)
        self.action_rotate_right.setStatusTip('Rotate right')
        self.action_rotate_right.triggered.connect(self.rotate_right)

        # Action Mirror
        self.action_flip_horizontal = QAction(
            QIcon.fromTheme('object-flip-horizontal'), 'Flip horizontally',
            self)
        self.action_flip_horizontal.setStatusTip('Flip horizontally')
        self.action_flip_horizontal.triggered.connect(self.flip_horizontal)

        # Action Flip vertical
        self.action_flip_vertical = QAction(
            QIcon.fromTheme('object-flip-vertical'), 'Flip vertically', self)
        self.action_flip_vertical.setStatusTip('Flip vertically')
        self.action_flip_vertical.triggered.connect(self.flip_vertical)

        # Action Previous image
        self.action_previous_image = QAction(QIcon.fromTheme('go-previous'),
                                             'Previous image', self)
        self.action_previous_image.setStatusTip('Previous image')
        self.action_previous_image.triggered.connect(self.previous_image)

        # Action Full screen
        self.action_fullscreen = QAction(QIcon.fromTheme('view-fullscreen'),
                                         'Full screen', self)
        self.action_fullscreen.setStatusTip('Full screen')
        self.action_fullscreen.triggered.connect(self.fullscreen)

        # Action Normal size
        self.action_normal_size = QAction(QIcon.fromTheme('zoom-original'),
                                          'Normal size', self)
        self.action_normal_size.setStatusTip('Normal Size')
        self.action_normal_size.triggered.connect(self.normal_size)

        # Action Fit Screen
        self.action_fit_screen = QAction(QIcon.fromTheme('zoom-fit-best'),
                                         'Fit to screen', self)
        self.action_fit_screen.setStatusTip('Fit to screen')
        self.action_fit_screen.setCheckable(True)
        self.action_fit_screen.triggered.connect(self.fit_screen)

        # Action Zoom in
        self.action_zoom_in = QAction(QIcon.fromTheme('zoom-in'), 'Zoom in',
                                      self)
        self.action_zoom_in.setStatusTip('Zoom in')
        self.action_zoom_in.triggered.connect(self.zoom_in)

        # Action Zoom out
        self.action_zoom_out = QAction(QIcon.fromTheme('zoom-out'), 'Zoom out',
                                       self)
        self.action_zoom_out.setStatusTip('Zoom out')
        self.action_zoom_out.triggered.connect(self.zoom_out)

        # Action Fit height
        self.action_fit_vertical = QAction('Fit vertically', self)
        self.action_fit_vertical.setStatusTip('Fit vertically')
        self.action_fit_vertical.setCheckable(True)
        self.action_fit_vertical.triggered.connect(self.fit_height)

        # Action Fit width
        self.action_fit_horizontal = QAction('Fit horizontally', self)
        self.action_fit_horizontal.setStatusTip('Fit horizontally')
        self.action_fit_horizontal.setCheckable(True)
        self.action_fit_horizontal.triggered.connect(self.fit_width)

        # Action Fit width
        self.action_fit_horizontal = QAction('Fit horizontally', self)
        self.action_fit_horizontal.setStatusTip('Fit horizontally')
        self.action_fit_horizontal.setCheckable(True)
        self.action_fit_horizontal.triggered.connect(self.fit_width)

        # Action Image list
        self.action_image_gallery = QAction('Image gallery', self)
        self.action_image_gallery.setStatusTip('Image gallery')
        self.action_image_gallery.setCheckable(True)
        self.action_image_gallery.triggered.connect(
            self.image_gallery_triggered)

        # Action Next_image
        self.action_next_image = QAction(QIcon.fromTheme('go-next'),
                                         'Next image', self)
        self.action_next_image.setStatusTip('Next image')
        self.action_next_image.triggered.connect(self.next_image)

        # Action First image
        self.action_first_image = QAction(QIcon.fromTheme('go-first'),
                                          'First image', self)
        self.action_first_image.setStatusTip('First image')
        self.action_first_image.triggered.connect(self.first_image)

        # Action Last image
        self.action_last_image = QAction(QIcon.fromTheme('go-last'),
                                         'Last image', self)
        self.action_last_image.setStatusTip('Last image')
        self.action_last_image.triggered.connect(self.last_image)

        # Action About
        self.action_about = QAction(QIcon.fromTheme('help-about'), 'About',
                                    self)
        self.action_about.setStatusTip('About')
        self.action_about.triggered.connect(self.about)

    def create_menubar(self):
        self.menubar = self.menuBar()

        # File
        self.menu_file = self.menubar.addMenu('File')
        self.menu_file.addAction(self.action_open)
        self.menu_file.addAction(self.action_save)
        self.menu_file.addSeparator()
        self.menu_file.addAction(self.action_copy)
        self.menu_file.addAction(self.action_move)
        self.menu_file.addAction(self.action_delete)
        self.menu_file.addSeparator()
        self.menu_file.addAction(self.action_quit)

        # Edit
        self.menu_edit = self.menubar.addMenu('Edit')
        self.menu_edit.addAction(self.action_rotate_left)
        self.menu_edit.addAction(self.action_rotate_right)
        self.menu_edit.addSeparator()
        self.menu_edit.addAction(self.action_flip_horizontal)
        self.menu_edit.addAction(self.action_flip_vertical)

        # View
        self.menu_view = self.menubar.addMenu('View')
        self.menu_view.addAction(self.action_fullscreen)
        self.menu_view.addAction(self.action_normal_size)
        self.menu_view.addAction(self.action_fit_screen)
        self.menu_view.addSeparator()
        self.menu_view.addAction(self.action_zoom_in)
        self.menu_view.addAction(self.action_zoom_out)
        self.menu_view.addSeparator()
        self.menu_view.addAction(self.action_fit_vertical)
        self.menu_view.addAction(self.action_fit_horizontal)
        self.menu_view.addSeparator()
        self.menu_view.addAction(self.action_image_gallery)

        # Go
        self.menu_go = self.menubar.addMenu('Go')
        self.menu_go.addAction(self.action_previous_image)
        self.menu_go.addAction(self.action_next_image)
        self.menu_go.addSeparator()
        self.menu_go.addAction(self.action_first_image)
        self.menu_go.addAction(self.action_last_image)

        # About
        self.menu_about = self.menubar.addMenu('About')
        self.menu_about.addAction(self.action_about)

    def create_toolbar(self):
        self.toolbar = self.addToolBar('Tool bar')

        self.toolbar.addAction(self.action_open)
        self.toolbar.addAction(self.action_save)
        self.toolbar.addSeparator()
        self.toolbar.addAction(self.action_fullscreen)
        self.toolbar.addAction(self.action_normal_size)
        self.toolbar.addAction(self.action_fit_screen)
        self.toolbar.addSeparator()
        self.toolbar.addAction(self.action_zoom_in)
        self.toolbar.addAction(self.action_zoom_out)
        self.toolbar.addSeparator()
        self.toolbar.addAction(self.action_rotate_left)
        self.toolbar.addAction(self.action_rotate_right)
        self.toolbar.addSeparator()
        self.toolbar.addAction(self.action_first_image)
        self.toolbar.addAction(self.action_previous_image)
        self.toolbar.addAction(self.action_next_image)
        self.toolbar.addAction(self.action_last_image)
        self.toolbar.addSeparator()
        self.toolbar.addAction(self.action_copy)
        self.toolbar.addAction(self.action_move)

    def load_settings(self):
        self.settings = QSettings()
        check_state = self.settings.value('view/image_gallery',
                                          True,
                                          type=bool)
        self.action_image_gallery.setChecked(check_state)
        self.image_gallery_triggered()

    def contextMenuEvent(self, QContextMenuEvent):
        menu = QMenu()
        menu.addAction(self.action_fullscreen)
        menu.addSeparator()
        menu.addAction(self.action_image_gallery)
        menu.addSeparator()
        menu.addAction(self.action_previous_image)
        menu.addAction(self.action_next_image)
        menu.addSeparator()
        menu.addAction(self.action_normal_size)
        menu.addAction(self.action_fit_screen)
        menu.addAction(self.action_fit_vertical)
        menu.addAction(self.action_fit_horizontal)
        menu.addSeparator()
        menu.addAction(self.action_zoom_in)
        menu.addAction(self.action_zoom_out)
        menu.addSeparator()
        menu.addAction(self.action_copy)
        menu.addAction(self.action_move)
        menu.addSeparator()
        menu.addAction(self.action_delete)
        menu.exec_(QContextMenuEvent.globalPos())

    def eventFilter(self, obj, event):
        """ filter events for wheel events

        Args:
            obj (QWidget): scroll_area
            event (QEvent): event
        """

        # try:
        if event.type() == QEvent.Wheel:
            if event.angleDelta().y() < 0:
                self.next_image()
            else:
                self.previous_image()

            return True
        elif event.type() == QEvent.MouseButtonPress and event.button(
        ) == Qt.RightButton:
            index = self.image_gallery.select_row_pos()
            if index > -1:
                self.index = index
                self.display_image()
                return True
        # pass the event on to the parent class
        return super(QMainWindow, self).eventFilter(obj, event)

    def keyPressEvent(self, event):
        key = event.key()
        if key == Qt.Key_Delete:
            self.delete()
        elif key == Qt.Key_Left:
            self.previous_image()
        elif key == Qt.Key_Right:
            self.next_image()
        elif key == Qt.Key_PageUp:
            self.first_image()
        elif key == Qt.Key_PageDown:
            self.last_image()
        elif key == Qt.Key_Escape and self.isFullScreen():
            self.fullscreen()
        else:
            QWidget.keyPressEvent(self, event)

    def mouseDoubleClickEvent(self, QMouseEvent):
        self.fullscreen()

    def mousePressEvent(self, QMouseEvent):
        self.mouse_position = QMouseEvent.pos()

    def mouseMoveEvent(self, QMouseEvent):
        diff = QPoint(QMouseEvent.pos() - self.mouse_position)
        self.mouse_position = QMouseEvent.pos()
        self.scroll_area.verticalScrollBar().setValue(
            self.scroll_area.verticalScrollBar().value() - diff.y())
        self.scroll_area.horizontalScrollBar().setValue(
            self.scroll_area.horizontalScrollBar().value() - diff.x())

    def resizeEvent(self, event):
        if not self.index == -1:
            self.display_image()

    def create_images(self, filename):
        """Create image list

        Args:
            filename (string): file from which to retrieve the list of images in the folder
        """

        self.images.clear()
        # get images only with an allowed extension
        for ext in self.extensions:
            self.images += glob.glob(
                os.path.join(
                    glob.escape(os.path.dirname(filename)),
                    '*.' + ''.join('[%s%s]' % (e.lower(), e.upper())
                                   for e in ext)))

        self.images.sort()
        if filename in self.images:
            self.index = self.images.index(filename)
        else:
            self.index = -1

        # iamge list
        self.image_gallery.add_images(self.images)

    def remove_index(self):
        """ remove file from list images and display next or previous image
        """

        del self.images[self.index]
        self.image_gallery.remove_row(self.index)

        if len(self.images) == 0:
            self.images.clear()
            self.index = -1
            self.image.clear()
            self.image.resize(self.image.minimumSizeHint())
        elif self.index < len(self.images) - 1:
            self.display_image()
        else:
            self.index = len(self.images) - 1
            self.display_image()

    def display_image(self):
        if not self.index == -1:
            self.image.clear()
            self.image.resize(self.image.minimumSizeHint())

            file = self.images[self.index]
            if os.path.isfile(file):
                self.label_name.setText(file)
                self.label_numero.setText(
                    str(self.index + 1) + ' / ' + str(len(self.images)))

                # image list
                self.image_gallery.select_row(self.index)

                image_reader = QImageReader(file)
                if image_reader.imageCount() > 1:
                    # Animated image
                    movie = QMovie(file)
                    movie.setCacheMode(QMovie.CacheAll)
                    movie.jumpToFrame(0)
                    movie_size = movie.currentPixmap().size()
                    self.image.setMovie(movie)
                    self.image.resize(movie_size)
                    movie.start()
                else:
                    self.image.setPixmap(QPixmap(file))
                    self.image.resize(self.image.pixmap().size())

                # fit image
                if self.action_fit_screen.isChecked():
                    self.fit_screen()
                elif self.action_fit_horizontal.isChecked():
                    self.fit_width()
                elif self.action_fit_vertical.isChecked():
                    self.fit_height()

                else:
                    self.ratio = 1.0

                self.action_zoom_in.setEnabled(True)
                self.action_zoom_out.setEnabled(True)

                # scrollbar position
                self.scroll_area.verticalScrollBar().setSliderPosition(0)
                self.scroll_area.horizontalScrollBar().setSliderPosition(0)

    def resize_image(self):
        if self.action_fit_screen.isChecked():
            self.fit_screen()
        elif self.action_fit_horizontal.isChecked():
            self.fit_width()
        elif self.action_fit_vertical.isChecked():
            self.fit_height()
        elif self.image.pixmap():
            self.image.resize(self.ratio * self.image.pixmap().size())
        elif movie := self.image.movie():
            movie.jumpToFrame(0)
            movie_size = movie.currentPixmap().size()
            self.image.resize(self.ratio * movie_size)