def _load(self):
     '''A la hora de realizar la carga del fichero controlo si se produce un mensaje de error y lo muestro'''
     try:
         file = QFileDialog.getOpenFileName(
             self, 'Buscar Archivo', QDir.homePath(),
             "All Files (*);;Music Mp3 Files (*.mp3)")
         if file:
             print("Archivo seleccionado: ", file)
     except:
         print("Error en la carga de datos.")
    def openDirectory(self):
        """Open a QFileDialog for selecting a local directory. Only display 
        image and GIF files.""" 
        directory = QFileDialog.getExistingDirectory(self, "Choose Directory", "", 
            QFileDialog.Option.ShowDirsOnly) # Specify the file mode to only select directories

        if directory:
            # Display the selected folder text in the QLineEdit. There are other, and 
            # possibly better, ways to handle displaying the text, but for simplicity, 
            # this is the method used in this GUI
            self.folder_line.setText(directory)

            # Get the contents of the directory and only display files with
            # specified extensions
            file_dir = QDir(directory)
            filters = ["*.gif", "*.png", "*.jpg", "*.jpeg"]
            files_info = file_dir.entryInfoList(filters, QDir.Filter.Files)

            # Clear the contents of the QTreeWidget
            if self.files_tree.model().rowCount() > 0:         
                # NOTE: Since files_tree is connected to the itemSelectionChanged signal,
                # using the clear() method below will also cause the signal to be emitted. 
                # This causes an undesired issue because of how the items are deleted. To avoid
                # the signal being called, QObject.blockSignals() will halt files_tree from 
                # emitting signals while removing items
                self.files_tree.blockSignals(True)
                # Use the convenience method clear() provided by QTreeWidget to remove 
                # all items and selections
                self.files_tree.clear() 
                self.files_tree.blockSignals(False)

                # Reset the QLabel and its image, and disable the movie buttons (in case the 
                # last item selected was a GIF)
                self.media_label.clear()
                self.media_label.setPixmap(QPixmap("icons/image_label.png"))
                self.disableMovieButtons()

            # Create items for each file and add them to the tree
            for file in files_info:
                item = QTreeWidgetItem()
                item.setText(0, file.fileName())
                self.files_tree.addTopLevelItem(item)
Ejemplo n.º 3
0
 def download_complete_slot(self, location):
     # use native separators
     location = QDir.toNativeSeparators(location)
     # show the success message
     if self.message.information(
         self,
         'Downloaded',
         f'Download complete!\nFile was successfully downloaded to :\n{location}\n\nOpen the downloaded file now ?',
         QMessageBox.StandardButtons.Open,
         QMessageBox.StandardButtons.Cancel
     ) is QMessageBox.StandardButtons.Open: subprocess.Popen(f'explorer /select,{location}')
 def loadStoredImageData(self):
     """Load images from the Images directory. The Images directory is 
     created the first time running the application."""
     if not(self.image_dir.exists()):
         QDir().mkdir(self.images_path)
     elif self.image_dir.exists():
         # Create a list of the files in the Images directory
         images = self.image_dir.entryInfoList(QDir.Filters.AllEntries | QDir.Filters.NoDotAndDotDot)
         for image in images: 
             # Imported files are QFileInfo objects
             item_name = image.baseName()
             path = image.absoluteFilePath()
             self.createListItems(path, item_name, image) 
Ejemplo n.º 5
0
    def openVideoFile(self):
        # self.mediaPlayer.setMedia(QMediaContent())
        if self.sender() == self.openVideoAction:
            self.videoFile, _ = QFileDialog.getOpenFileName(
                self, "Open video", QDir.homePath())
            # if self.videoFile != '':
            #     self.setWindowTitle('{} - {}'.format(os.path.basename(self.videoFile),
            #                                          os.path.basename(self.projectFile)))

        if self.videoFile != '':
            self.setWindowTitle('{} - {}'.format(
                os.path.basename(self.videoFile),
                os.path.basename(self.projectFile)))
            self.saveProjectAction.setEnabled(True)
            self.maskGenAction.setEnabled(True)
            # self.loadGraphAction.setEnabled(True)
            # self.saveGraphAction.setEnabled(True)
            # self.drawPointAction.setEnabled(True)
            # self.drawLineAction.setEnabled(True)
            # self.drawZoneAction.setEnabled(True)

            creation_datetime, width, height = getVideoMetadata(self.videoFile)
            self.videoStartDatetime = self.videoCurrentDatetime = creation_datetime
            self.dateLabel.setText(creation_datetime.strftime('%a, %b %d, %Y'))

            self.gView.setSceneRect(0, 0, width, height)

            self.videoItem = QGraphicsVideoItem()
            self.videoItem.setAspectRatioMode(
                Qt.AspectRatioMode.KeepAspectRatio)
            self.gScene.addItem(self.videoItem)
            self.videoItem.mouseMoveEvent = self.gView.mouseMoveEvent
            self.videoItem.setSize(QSizeF(width, height))

            self.mediaPlayer.setVideoOutput(self.videoItem)
            self.mediaPlayer.setSource(QUrl.fromLocalFile(self.videoFile))

            self.gView.labelSize = width / 50

            self.playButton.setEnabled(True)
            # self.gView.setViewport(QOpenGLWidget())
            self.mediaPlayer.pause()
Ejemplo n.º 6
0
    def saveMaskFile(self):
        creation_datetime, width, height = getVideoMetadata(self.videoFile)
        item = self.gView.gPolyItem  #self.gView.unsavedZones[0]
        mask_polygon = item.polygon()
        xy = []
        for p in mask_polygon:
            xy.append((p.x(), p.y()))

        img = Image.new('RGB', (width, height), color='black')
        img1 = ImageDraw.Draw(img)
        img1.polygon(xy, fill="white", outline="white")

        fileName, _ = QFileDialog.getSaveFileName(self, "Open database file",
                                                  QDir.homePath(),
                                                  "PNG files (*.png)")
        if fileName != '':
            img.save(fileName)

        self.gView.scene().removeItem(item)
        self.gView.unsavedZones = []
Ejemplo n.º 7
0
class Render(Op):
    qprog = pyqtSignal(float)

    fps: float = 20.0
    with_audio = True
    with_cursor = True
    with_text = True
    with_hl: bool = True
    dir: Path = Path(QDir.homePath()) / "Videos"
    res: Tuple[int, int] = (1920, 1080)
    title: str = "out"
    format: Format = Format.Mp4

    def __str__(self) -> str:
        return "render"

    def run(self, demo: Demo):
        """

        """
        # TODO add sect discrim fxn
        super().run(demo)
        log("[RenderOp.run] RUNNING RENDER OP on " + demo.title)
        self.started()
        self.render_video(demo)
        self.finished()

    @property
    def path(self):
        return self.dir / Path(f"{self.title}.{self.format.name}")

    @property
    def writer(self):
        return cv2.VideoWriter(
            filename=str(self.path),
            fourcc=self.format.to_codec(),
            apiPreference=cv2.CAP_FFMPEG,
            isColor=True,
            fps=self.fps,
            frameSize=self.res,
        )


    def render_video(self, demo: Demo, verbose=False):
        """
        Renders composite of all demo's images into video using python mmpeg
        """
        log("[Demo.render_video] RUNNING render_video to path " + str(self.path))
        # imgs: List[Image] = []
        imgs: List[str] = []
        for sect_i, sect in enumerate(demo):
            for step_i, step in enumerate(sect.steps):
                if step.img is not None:
                    sd: float = 1.0
                    # hov = None
                    mouse: Cursor | None = None
                    animated = False
                    log(f"\nSECTION {str(sect_i)}, STEP {str(step_i)} info: ")
                    if step.img:
                        log("IMAGE: " + str(step.img))
                    if step.hover:
                        log("HOVER: " + str(step.hover))
                        # hov = cv2.imread(str(step.hover))
                        if step.hover_time: 
                            log("HOVER TIME: " + str(step.hover_time))
                    if step.audio:
                        log("AUDIO: " + str(step.audio))
                        au = step.audio
                    if step.animated: 
                        log("ANIMATED: " + str(step.animated))
                        # animated = True
                    if (m:=step.mouse) is not None:
                        log("MOUSE:     (" + str(m[0]) + ", " + str(m[1]) + ")")
                        mouse = Cursor((int(step.mouse[0]), int(step.mouse[1])))
                    if (mh := step.mouse_hover) is not None:
                        log("MOUSE HOV: (" + str(step.mouse_hover[0]) + ", " + str(step.mouse_hover[1]) + ")")
                    if (dl := step.delay): 
                        log("DELAY: " + str(dl), verbose)
                        sd = float(dl)
                    if step.time: 
                        log("TIME: " + str(step.time))
                    if step.transition: 
                        log("TRANSITION: " + str(step.transition))
                    if step.boxes["hotspot"]:
                        log("HOTSPOT:   (" + str(step.boxes["hotspot"]["x1"]) + ", " + str(step.boxes["hotspot"]["y1"]) + ")")
                    if step.boxes["highlight"]:
                        log("HIGHLIGHT: (" + str(step.boxes["highlight"]["x1"]) + ", " + str(step.boxes["highlight"]["y1"]) + "), color: " + str(step.boxes["highlight"]["color"]))
                    if step.boxes["text"]:
                        log("TEXT:      (" + str(step.boxes["text"]["text"]) + " (font: " + str(step.boxes["text"]["font"]) + ", size: " + str(step.boxes["text"]["size"]) + ", color: " + str(step.boxes["text"]["color"]) + ")")
                    nframes: int = round(self.fps * sd)
                    simg =  cv2.imread(str(step.img))
                    # simg = cv2.cvtColor(simg, cv2.COLOR_BGR2RGB)
                    if step.audio:
                        au = str(step.audio)
                    if mouse:
                        simg = mouse.paste(simg)
                    h, w, layers = simg.shape
                    for i in range(round(nframes)):
                        imgs.append(simg)
Ejemplo n.º 8
0
    def saveProject(self):

        if self.projectFile == '':
            fileDir = QDir.homePath()
        else:
            fileDir = self.projectFile

        self.projectFile, _ = QFileDialog.getSaveFileName(
            self, "Save project file", fileDir, "Project (*.prj)")
        # fileName = "/Users/Abbas/project.xml"
        if self.projectFile == '':
            return

        file = QFile(self.projectFile)
        if (not file.open(QIODevice.OpenModeFlag.WriteOnly
                          | QIODevice.OpenModeFlag.Text)):
            return

        xmlWriter = QXmlStreamWriter(file)
        xmlWriter.setAutoFormatting(True)
        xmlWriter.writeStartDocument()

        xmlWriter.writeStartElement('project')

        xmlWriter.writeStartElement('database')
        xmlWriter.writeTextElement("fileName", self.obsTb.dbFilename)
        xmlWriter.writeEndElement()

        xmlWriter.writeStartElement('video')
        xmlWriter.writeTextElement(
            "fileName",
            self.videoFile)  #mediaPlayer.media().canonicalUrl().path())
        xmlWriter.writeTextElement("sliderValue",
                                   str(self.mediaPlayer.position()))
        xmlWriter.writeEndElement()

        xmlWriter.writeStartElement('trajectory')
        xmlWriter.writeTextElement("metadata", self.obsTb.mdbFileLedit.text())
        xmlWriter.writeTextElement(
            "site", str(self.obsTb.siteNameCombobx.currentIndex()))
        xmlWriter.writeTextElement(
            "cam_view", str(self.obsTb.camViewCombobx.currentIndex()))
        xmlWriter.writeTextElement("traj_db",
                                   str(self.obsTb.trjDbCombobx.currentIndex()))
        xmlWriter.writeEndElement()

        xmlWriter.writeStartElement('window')
        xmlWriter.writeTextElement(
            "mainWin_size", "{},{}".format(int(self.width()),
                                           int(self.height())))
        xmlWriter.writeTextElement(
            "mainWin_pos", "{},{}".format(int(self.x()), int(self.y())))
        xmlWriter.writeTextElement("obsTbx_open", str(self.obsTb.isVisible()))
        xmlWriter.writeTextElement(
            "obsTbx_size", "{},{}".format(int(self.obsTb.width()),
                                          int(self.obsTb.height())))
        xmlWriter.writeTextElement(
            "obsTbx_pos", "{},{}".format(int(self.obsTb.x()),
                                         int(self.obsTb.y())))
        xmlWriter.writeEndElement()

        xmlWriter.writeEndElement()

        self.setWindowTitle('{} - {}'.format(
            os.path.basename(self.videoFile),
            os.path.basename(self.projectFile)))
        if self.obsTb.dbFilename != None:
            self.obsTb.setWindowTitle('{} - {}'.format(
                os.path.basename(self.obsTb.dbFilename),
                os.path.basename(self.projectFile)))
Ejemplo n.º 9
0
    def openProject(self):
        self.projectFile, _ = QFileDialog.getOpenFileName(
            self, "Open project file", QDir.homePath(), "Project (*.prj)")

        if self.projectFile == '':
            return

        self.saveProjectAction.setEnabled(True)
        self.maskGenAction.setEnabled(True)
        # self.loadGraphAction.setEnabled(True)
        # self.saveGraphAction.setEnabled(True)
        # self.drawPointAction.setEnabled(True)
        # self.drawLineAction.setEnabled(True)
        # self.drawZoneAction.setEnabled(True)

        tree = ET.parse(self.projectFile)
        root = tree.getroot()
        gItems = []
        for elem in root:
            subEelTexts = {}
            for subelem in elem:
                subEelTexts[subelem.tag] = subelem.text
            gItems.append([elem.tag, subEelTexts])

        for key in gItems:
            if key[0] == 'database':
                item = key[1]
                if item['fileName'] is not None:
                    self.obsTb.dbFilename = item['fileName']
                    self.obsTb.opendbFile()

            elif key[0] == 'video':
                item = key[1]
                if item['fileName'] is not None:
                    self.videoFile = item['fileName']
                    self.openVideoFile()
                    self.mediaPlayer.setPosition(int(item['sliderValue']))
                    if item['fileName'] is not None:
                        self.loadGraphics()

            elif key[0] == 'trajectory':
                item = key[1]
                if item['metadata'] != None:
                    self.obsTb.mdbFileLedit.setText(item['metadata'])
                    self.obsTb.openMdbFile()
                    self.obsTb.siteNameCombobx.setCurrentIndex(
                        int(item['site']))
                    self.obsTb.camViewCombobx.setCurrentIndex(
                        int(item['cam_view']))
                    self.obsTb.trjDbCombobx.setCurrentIndex(
                        int(item['traj_db']))

            elif key[0] == 'window':
                item = key[1]
                x, y = item['mainWin_pos'].split(',')
                w, h = item['mainWin_size'].split(',')
                self.setGeometry(int(x), int(y), int(w), int(h))
                if item['obsTbx_open'] == 'True':
                    self.obsTb.show()
                    x, y = item['obsTbx_pos'].split(',')
                    w, h = item['obsTbx_size'].split(',')
                    self.obsTb.setGeometry(int(x), int(y), int(w), int(h))
class MainWindow(QMainWindow):

    # Create a QSettings object rather than storing values. Pass in a company 
    # name and an application name
    settings = QSettings("Custom GUIs", "Image Manager GUI")
    #print(settings.fileName()) # NOTE: Uncomment to print the path to settings

    images_path = "Images" # File path to the Images directory
    image_dir = QDir(images_path)
    info_dialog = None # Create variable for modeless dialog

    def __init__(self):
        """MainWindow Constructor for Image Manager"""
        super().__init__() # Constructor for QMainWindow
        self.initializeUI()

    def initializeUI(self):
        """Set up the GUI's main window and load initial settings and data."""
        self.setWindowTitle("Image Manager")
        self.setObjectName("ImageManager")

        # Set up the main window, menu, dock widgets, and initialize the GUI's settings
        self.setUpMainWindow()
        self.displayImagePreviewDock()
        self.createActions()
        self.createMenus()
        self.loadStoredImageData()
        self.getInitialSettings()
        self.show() # Display the main window

    def setUpMainWindow(self):
        """Set up the application's main window containing the QListWidget."""
        self.image_view_lw = ImageViewerListWidget(self)
        # Use signals/slots to interact with the list widget 
        self.image_view_lw.itemSelectionChanged.connect(self.updateDockInfo)
        self.image_view_lw.itemDoubleClicked.connect(self.displayImageInfoDialog)
        # Use the list widget's internal model to enable/disable menu items
        self.image_view_lw.model().rowsInserted.connect(self.manageMenuItems)
        self.image_view_lw.model().rowsRemoved.connect(self.manageMenuItems)

        self.setCentralWidget(self.image_view_lw)

    def createActions(self):
        """Create the application's menu actions."""
        # Create actions for File menu
        self.import_act = QAction("Import Images...", self, triggered=self.importImages)
        self.import_act.setShortcut("Ctrl+I") 

        self.preferences_act = QAction("Preferences...", self, triggered=self.showPreferencesDialog)

        self.quit_act = QAction("Quit Task Manager", self, triggered=self.close)
        self.quit_act.setShortcut(QKeySequence.StandardKey.Quit) # Ctrl+Q

        # Create actions for Edit menu
        self.select_all_act = QAction("Select All", self, triggered=self.image_view_lw.selectAll)
        self.select_all_act.setShortcut(QKeySequence.StandardKey.SelectAll) # Ctrl+A

        self.delete_act = QAction("Delete Images", self, triggered=self.deleteImages)
        self.delete_act.setShortcut(QKeySequence.StandardKey.Delete) # Del
        self.delete_act.setEnabled(False)

        # Create actions for View menu
        # Handle the visibility of the dock widget that displays images
        self.show_dock_act = self.image_preview_dock.toggleViewAction()
        self.show_dock_act.setText("Show Image View")  

        self.sort_ascend_act = QAction("Sort Ascending", self,
            triggered=lambda: self.sortListItems(Qt.SortOrder.AscendingOrder))
        self.sort_ascend_act.setEnabled(False)

        self.sort_descend_act = QAction("Sort Descending", self,
            triggered=lambda: self.sortListItems(Qt.SortOrder.DescendingOrder))
        self.sort_descend_act.setEnabled(False)

        self.fullscreen_act = QAction("Show Fullscreen", self, 
            triggered=self.displayFullScreen, checkable=True)

        # Create actions for Help menu
        self.about_act = QAction("About Image Manager", 
            self, triggered=self.showAboutDialog)  

    def createMenus(self):
        """Create the application's menu."""
        if QSysInfo.productType() == "macos" or "osx":
            self.menuBar().setNativeMenuBar(False)

        self.file_menu = self.menuBar().addMenu("&File")
        self.file_menu.addAction(self.import_act)
        self.file_menu.addSeparator()
        self.file_menu.addAction(self.preferences_act)
        self.file_menu.addSeparator()
        self.file_menu.addAction(self.quit_act)

        self.edit_menu = self.menuBar().addMenu("&Edit")
        self.edit_menu.addAction(self.select_all_act)
        self.edit_menu.addSeparator()
        self.edit_menu.addAction(self.delete_act)  

        self.view_menu = self.menuBar().addMenu("&View")
        self.view_menu.addAction(self.show_dock_act)  
        self.view_menu.addSeparator()
        self.view_menu.addAction(self.sort_ascend_act)
        self.view_menu.addAction(self.sort_descend_act)
        self.view_menu.addSeparator()
        self.view_menu.addAction(self.fullscreen_act)       

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

    def manageMenuItems(self, parent, first, last):
        """Slot to enable/disable menu items if rows have been 
        added/deleted to QListWidget. The rowsInserted() and 
        rowsRemoved() that trigger this slot return the 'parent',
        'first', and 'last' values, but they are not used in 
        this method."""
        if self.image_view_lw.count() == 0:
            self.delete_act.setEnabled(False)
            self.sort_ascend_act.setEnabled(False)
            self.sort_descend_act.setEnabled(False)
        elif self.image_view_lw.count() > 0:
            self.delete_act.setEnabled(True)
            self.sort_ascend_act.setEnabled(True)
            self.sort_descend_act.setEnabled(True)   

    def displayImagePreviewDock(self):
        """Dock widget that displays a selected image in a scrollable 
        area and uses its file name as the dock's title."""
        self.image_preview_dock = QDockWidget()
        self.image_preview_dock.setObjectName("PreviewDock")
        self.image_preview_dock.setWindowTitle("Show Image View")
        self.image_preview_dock.setAllowedAreas(Qt.DockWidgetAreas.RightDockWidgetArea)

        self.display_image_label = QLabel()
        self.display_image_label.setAlignment(Qt.Alignment.AlignCenter)

        self.view_scroll_area = QScrollArea()
        self.view_scroll_area.setMinimumWidth(300)
        self.view_scroll_area.setWidgetResizable(True)

        self.image_preview_dock.setWidget(self.view_scroll_area)
        # Set initial location of dock widget in the main window
        self.addDockWidget(Qt.DockWidgetAreas.RightDockWidgetArea, self.image_preview_dock)   

    def updateDockInfo(self):
        """Slot to update the image that the dock widget displays."""
        # Only display an image if one item is selected
        if (len(self.image_view_lw.selectedItems()) == 0 or 
            len(self.image_view_lw.selectedItems()) > 1):
            self.image_preview_dock.setWindowTitle("Show Image View")
            self.display_image_label.clear()
        else:
            curr_item = self.image_view_lw.currentItem()
            self.image_preview_dock.setWindowTitle(curr_item.text())
            self.show_dock_act.setText("Show Image View") 

            # Get the current height of the dock widget
            dock_height = self.image_preview_dock.height()
            # Get the size of the original image/item
            icon_size = curr_item.icon().availableSizes()[0]
            icon_width = icon_size.width()

            # Return a pixmap from the item's icon and display in the scroll area
            pixmap = curr_item.icon().pixmap(QSize(icon_width, dock_height)) 
            self.display_image_label.setPixmap(pixmap)
            self.view_scroll_area.setWidget(self.display_image_label) 
        
    def importImages(self):
        """Import the images a user selects, remove duplicates, and add
        items to the QListWidget."""
        duplicate_images = [] # Store the names of duplicate images
        image_paths, _ = QFileDialog.getOpenFileNames(self, 
            "Select Image Files", "", "Images (*.png *.xpm *.jpg *.jpeg)")

        if image_paths:
            if self.image_dir.exists():
                for image_path in image_paths:
                    # Pass image path to QFileInfo object
                    image_info = QFileInfo(image_path) 
                    file_name = image_info.fileName()
                    item_name = image_info.baseName()

                    # Copy the files into the Images directory, check for files 
                    # with the same name
                    new_name = self.image_dir.absolutePath() + f"/{file_name}"
                    file_exists = QFile.copy(image_path, new_name)
                    if file_exists == False:
                        duplicate_images.append(image_path)
                    else:
                        self.createListItems(image_path, item_name, image_info, new_name)
                        if self.is_delete_checked == True: # Handle deleting images
                            QFile.moveToTrash(image_path) 
            else:
                QMessageBox.warning(self, "Images Location Not Found",
                    """<p>The Images Location cannot be found. Restart the application to
                    recreate the directory.</p>""")

        # Display a custom dialog to inform the user of duplicate images
        if len(duplicate_images) != 0:
            duplicates_dialog = QMessageBox(self)
            duplicates_dialog.setIcon(QMessageBox.Icon.Information)
            duplicates_dialog.setWindowTitle("Duplicate Images")
            duplicates_dialog.setText("""<p>Some images were not imported because 
                they already exist.</p>""")

            details = '\n'.join([item for item in duplicate_images])
            duplicates_dialog.setDetailedText(details)
            duplicates_dialog.exec()

            duplicate_images.clear() # Clear the list 
        # Check if window is still in focus. If not, give it focus
        if self.isActiveWindow() == False:
            self.activateWindow()

    def createListItems(self, image_path, item_name, image_info, new_name=None):
        """Simple method for creating QListWidgetItem objects. 
        'image_path': the path to the file.
        'item_name': the base name used for QListWidgetItem objects.
        'image_info': the QFileInfo object.
        'new_name': used when importing new photos, making sure the program
        points to the new image location."""
        list_item = QListWidgetItem(QIcon(image_path), item_name)
        self.image_view_lw.setIconSize(QSize(80, 80))
        self.image_view_lw.addItem(list_item)
        if new_name != None:
            image_info.setFile(new_name)
        self.image_view_lw.images_info_list.append(image_info) 

    def sortListItems(self, order): 
        """First, sort the items in the QListWidget using sortItems(). Then handle 
        sorting the QFileInfo objects in the images_info_list using Python's sort() to 
        match how the QListWidget sorts items."""
        self.image_view_lw.sortItems(order)
        if order == Qt.SortOrder.AscendingOrder:
            self.image_view_lw.images_info_list.sort(key=lambda item: (item.baseName().upper(), item.baseName()[0].islower()))
        elif order == Qt.SortOrder.DescendingOrder:
            self.image_view_lw.images_info_list.sort(reverse=True, key=lambda item: (item.baseName().upper(), item.baseName()[0].islower()))

    def deleteImages(self):
        """Delete images from the QListWidget and from where images
        are stored on disk."""
        number_of_photos = len(self.image_view_lw.selectedItems())
        answer = QMessageBox.warning(self, "Delete Image(s)", 
            f"Are you sure you want to delete {number_of_photos} image(s)?", 
            QMessageBox.StandardButtons.No | QMessageBox.StandardButtons.Yes, 
            QMessageBox.StandardButtons.No)

        if answer == QMessageBox.StandardButtons.Yes:
            for item in self.image_view_lw.selectedItems():
                index = self.image_view_lw.indexFromItem(item).row()
                # Get the image's information before deletion
                image_info = self.image_view_lw.images_info_list[index] 

                # Remove items from the Images directory, from the list widget, 
                # and the images_info_list that stores QFileInfo objects
                QFile.moveToTrash(image_info.absoluteFilePath()) 
                self.image_view_lw.takeItem(index)
                del self.image_view_lw.images_info_list[index] 
                del item        

    def loadStoredImageData(self):
        """Load images from the Images directory. The Images directory is 
        created the first time running the application."""
        if not(self.image_dir.exists()):
            QDir().mkdir(self.images_path)
        elif self.image_dir.exists():
            # Create a list of the files in the Images directory
            images = self.image_dir.entryInfoList(QDir.Filters.AllEntries | QDir.Filters.NoDotAndDotDot)
            for image in images: 
                # Imported files are QFileInfo objects
                item_name = image.baseName()
                path = image.absoluteFilePath()
                self.createListItems(path, item_name, image) 

    def displayImageInfoDialog(self, item): 
        """Display image metadata in a modeless dialog box. 'index' is the index of 
        the item that is clicked on."""
        index = self.image_view_lw.indexFromItem(item).row()
        if self.info_dialog == None: 
            self.info_dialog = ImageInfoDialog(self, self.image_view_lw.images_info_list[index])
        elif self.info_dialog != None:
            self.info_dialog.close()
            self.info_dialog = ImageInfoDialog(self, self.image_view_lw.images_info_list[index])
        self.info_dialog.show()         

    def showPreferencesDialog(self):
        """Display the application's preferences dialog. Save the value of the 
        delete_images_checkbox in the settings."""
        prefs_dialog = PreferencesDialog(self, self.image_dir, self.is_delete_checked)
        response = prefs_dialog.exec()

        if response == 1: # QDialog.DialogCode.Accepted == 1
            self.settings.setValue("delete_images", prefs_dialog.delete_images_checkbox.isChecked())
            self.is_delete_checked = self.settings.value("delete_images", type=bool)

    def displayFullScreen(self, state):
        """Check the state of checkable fullscreen_act. If True, show the 
        main window as fullscreen."""
        if state: self.showFullScreen()
        else: self.showNormal()

    def showAboutDialog(self):
        """Display the application's about dialog."""
        QMessageBox.about(self, "Image Manager",
            """<h3 style='text-align:center'>Image Manager</h3>
            <p style='font-weight: normal'>The <b><i>Image Manager GUI</i></b> 
            demonstrates how to build an application for managing photos. This 
            program also examines some of the common features found in many GUIs.</p>
            <p style='font-weight: normal'>This application is part of
            <b><i>Building Custom UIs with PyQt</i></b>.</p>
            <p style='font-weight: normal'>Designed by: <b>Joshua Willman</b></p>
            <p style='font-weight: normal'>Icons created by: <b>Joshua Willman</b></p>""")

    def getInitialSettings(self):
        """Get initial settings of the application using QSettings upon startup."""
        position = self.settings.value("position", QPoint(200, 0))
        size = self.settings.value("size", QSize(800, 500))
        self.is_delete_checked = self.settings.value("delete_images", type=bool) 
        # restoreState() is used here to restore the image_preview_dock widget
        self.restoreState(self.settings.value("window_state", bytes(QByteArray())))

        self.resize(size)
        self.move(position)
        return self.is_delete_checked
    
    def saveSettings(self):
        """Save the settings of the application."""
        self.settings.setValue("position", self.pos())
        self.settings.setValue("size", self.size())
        self.settings.setValue("window_state", self.saveState())

    def closeEvent(self, event):
        """Save the application's settings in the closeEvent()."""
        self.saveSettings()
        event.setAccepted(True)
Ejemplo n.º 11
0
from smseventlog.gui import (delegates, dialogs, gui, refreshtables, tables,
                             update)

log = getlog(__name__)

# Deprecated in PyQt6, not sure if needed
# try:
#     # try setting app ID for windows only
#     from PyQt6.QtWinExtras import QtWin  # type: ignore # noqa
#     app_id = 'com.sms.smseventlog'  # .{VERSION}' > dont include version, windows thinks its a different app
#     QtWin.setCurrentProcessExplicitAppUserModelID(app_id)
# except ImportError:
#     pass

# add icons path to Qt to be loaded in darkstyle.qss
QDir.addSearchPath('qdark_icons', str(cf.p_res / 'images/qdark_icons'))


def decorate_modules():
    # decorate all classes' methods in these modules with @e error handler
    modules = [delegates, dialogs, gui, refreshtables, tables, update]
    for module in modules:
        er.decorate_all_classes(module=module)


@er.errlog('Error in main process.')
def launch():
    log.info(f'\n\n\n{dt.now():%Y-%m-%d %H:%M} | init | {VERSION}')

    app = gbl.get_qt_app()