【问题标题】:Running a PYQT Dialog before QT MainWindow is Activated在激活 QT MainWindow 之前运行 PYQT 对话框
【发布时间】:2021-03-11 19:56:20
【问题描述】:

我正在使用 PyQT 编写一个 Python 程序,该程序将从 SD 卡中的文件夹中收集文件列表,并让我查看照片和视频文件,因此我选择是否要将任何文件保存到更永久的存储中。根据 SD 卡的速度、卡上文件的数量以及我想要每个视频的长度,读取文件目录的过程可能会很慢。我想在设置 MainWindow 之前显示一个进度对话框。在这个对话框中会有几个标签和一个进度条,我将在收集文件进行处理时更新。

我正在使用 QT Designer 创建 MainWindow 和对话框的“UI”文件。

我尝试从 MainWindow 的 init 函数和收集文件夹中文件的函数内部实例化对话框。在任何一种情况下,当我“.exec()”对话框时,直到函数完成并且在 MainWindow 启动之前我才看到它。当它出现时,对话框会显示我为标签设置的最后一个文本,并且没有更新进度条。

我也尝试过“显示”对话框,只得到一个黑框,没有显示 3 个小部件。

我也尝试将函数从对话框中的文件夹中收集文件名,结果相同。

下面粘贴的代码是工作程序的骨架(没有任何进度对话框)-我删除了与此问题无关的其他功能。

有什么建议可以帮助我完成这项工作吗?

非常感谢!

#!/usr/bin/python3

Program_Version = "DEMO"

# Imports
import getopt
import sys
import os
import time
import subprocess
from datetime import datetime
from dateutil import tz
from ffprobe import FFProbe
import shutil
# GUI imports
from PyQt5 import QtWidgets, uic
from PyQt5.QtCore import Qt, QSize, QUrl
from PyQt5.QtGui import QPixmap
from PyQt5.QtMultimedia import QMediaContent, QMediaPlayer
from PyQt5.QtMultimediaWidgets import QVideoWidget
from PyQt5.QtWidgets import (QLabel, QVBoxLayout, QHBoxLayout, QWidget, QDialog, 
                             QCheckBox, QStatusBar, QMessageBox, QSlider, 
                             QPushButton, QShortcut, QProgressBar, qApp)
from PyQt5.uic import loadUi

# Arguments: 0=Program name,
#    0=-t/T/test/Test <use testing folder>

# Globals
# Target folder name when copying files
target_folder_name = ""
# SD Card device & folder name @ system level
SDCard_Dev = ""
SDCard_Dir = ""
# File & set Counters
sets_found = 0
sets_deleted = 0
sets_active = 0
#NotDeleted_set_list = []
files_found = 0
files_saved = 0
files_deleted = 0

# Open Progress Dialog
Ui_ProgressDialog, QtBaseClass = uic.loadUiType("ProgressDialog.ui")

class ProgressDialog(QDialog, Ui_ProgressDialog):
    def __init__(self, parent=None):
        # QT Initialization of dialog
        QDialog.__init__(self, parent=None)
        Ui_ProgressDialog.__init__(self)
        self.setupUi(self)
        print("ProgressDialog: Inside it")
        
# Open front page window
Ui_MainWindow, QtBaseClass = uic.loadUiType("SDCardViewer_V2.ui")

class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
    
    def __init__(self):
        # QT Initialization of main window
        QtWidgets.QMainWindow.__init__(self)
        Ui_MainWindow.__init__(self)
        self.setupUi(self)
        print("MainWindow: Inside it")
        # Set title 
        self.Title_label.setText("Trail Camera SD Card Photo & Video Manager "+Program_Version)
        
        # Run the progress loading dialog
        Progress_Dialog = ProgressDialog()
        #Progress_Dialog.exec()   # Opens after the function completes
        Progress_Dialog.show()   # Opens dialog with no widgets (black screen)
        Progress_Dialog.setModal(Qt.WindowModal)
        Progress_Dialog.setWindowTitle("Loading Sets and Files")
        Progress_Dialog.Progress_bar.setValue(0)
        
        # Get files from SD Card
        self.get_SDFiles()
        Progress_Dialog.destroy()
        
    def closeEvent(self, event):
        event.accept()
        self.close()

# Functions
    # Find and organize files of JPG and AVIs into 'sets' - photos taken prior to a video
    def get_SDFiles(self):
        # A set is collection of files consisting of photos and a video
        jpg_file_count = 0
        avi_file_count = 0
        SDCard_folder_count = 0
        SDCard_folders = []
        SDCard_filesets = [[]]
        sets_found = 0
        
        SDCard_Dev = "/home"
        SDCard_Dir = "/home/dave/TrailCamera_Test/DCIM"

        file_set_new = False
        # Capture time we start to process files
        start_time = datetime.now()
        # Calculate used space on source (SD Card)
        st = os.statvfs(SDCard_Dev)
        total = (st.f_blocks * st.f_frsize)
        used = (st.f_blocks - st.f_bfree) * st.f_frsize
        try:
            pct_used = (float(used) / total) * 100
        except ZeroDivisionError:
            pct_used = 0
        # Collect folders on SD Card under DCIM folder     
        for item in os.scandir(SDCard_Dir):
            if item.is_dir():
                SDCard_folders.append(item.name)
                
        SDCard_folders.sort()
        
        # Organize into sets across all folders
        for item in SDCard_folders:
            full_folder_name = os.path.join(SDCard_Dir, item)
            SDCard_files = os.listdir(full_folder_name)
            SDCard_folder_count += 1
            print("get_SDFiles: Open Folder '{}' with {} files".format(item, len(SDCard_files)))

            SDCard_files.sort()

            progress_file_counter = 0
            for file_name in SDCard_files:
                time.sleep(.1)
                progress_file_counter += 1
                if progress_file_counter % 25 == 0:
                    print("get_SDFiles: Processed {} files {}%"
                          .format(progress_file_counter,int(100*progress_file_counter/len(SDCard_files))))
                
                full_file_name = os.path.join(full_folder_name, file_name)
                if file_name.lower().endswith(".jpg"):
                    jpg_file_count += 1
                    if file_set_new:
                        SDCard_filesets.append([full_file_name])
                        file_set_new = False
                    else:
                        SDCard_filesets[sets_found].append(full_file_name)
                elif file_name.lower().endswith(".avi"):
                    #filesize = (float(os.path.getsize(full_file_name)))/(1024*1024)
                    #AVI_length = self.getVideoTime(full_file_name)
                    avi_file_count += 1
                    if file_set_new:
                        SDCard_filesets.append([full_file_name])
                    else:
                        SDCard_filesets[sets_found].append(full_file_name)
                        sets_found += 1
                        file_set_new = True

        files_found = jpg_file_count + avi_file_count
        # Capture time we stopped processing files
        stop_time = datetime.now()
        elapsed_time = (stop_time - start_time)
        print("get_SDFiles: Elapsed time to read files was {} (h:mm:ss)".format(elapsed_time)) 

        if sets_found == 0:
            self.show_messagebox("Information","No files found.  Program closing")
            self.close()
        else:
            print("get_SDFiles: Found a total of {} files".format(files_found))
                    
        self.SDCardCounts_label.setText("{} ({:.0f}% Used) -- Folders: {:d} -- Files: {:,d} (JPGs: {:,d}, AVIs: {:,d})"
                                        .format(SDCard_Dev, pct_used, SDCard_folder_count, files_found,
                                                jpg_file_count, avi_file_count))

        max_set_pointer = len(SDCard_filesets)-1
        
    # Get length of the video
    def getVideoTime(self,clip_file):
        return(int(float(FFProbe(clip_file).streams[0].duration)))

        
# Start the main window    
if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    print("__main__: Instantiate MainWindow")
    window = MainWindow()
    print("__main__: Show MainWindow")
    window.show()
    sys.exit(app.exec_())

【问题讨论】:

    标签: python pyqt dialog mainwindow


    【解决方案1】:

    最好将逻辑与扫描和 GUI 分开,以便我们更好地处理流程。这个想法是在第二个线程中运行扫描并通过信号将信息发送到 GUI。因此我们可以创建一个显示进度的 QDialog,当用户按下接受按钮时,将显示主窗口。以下示例尝试显示一般过程(不要实现所有计算,如 SDCard_filesets、sets_found,因为我不了解它们的逻辑)

    from datetime import datetime
    import os.path
    import sys
    import threading
    import time
    
    from PyQt5.QtCore import pyqtSignal, pyqtSlot, QObject, Qt
    from PyQt5.QtWidgets import (
        QApplication,
        QDialog,
        QDialogButtonBox,
        QLabel,
        QMainWindow,
        QProgressBar,
        QVBoxLayout,
    )
    
    
    class SDCardScanner(QObject):
        started = pyqtSignal()
        filesProcessed = pyqtSignal(int, int)
        directoriesProcessed = pyqtSignal(int, int)
        finished = pyqtSignal(str, float, int, int, int, int, int)
    
        def start(self, dev, directory):
            threading.Thread(
                target=self._execute, args=(dev, directory), daemon=True
            ).start()
    
        def _execute(self, dev, directory):
            self.started.emit()
            jpg_file_count = 0
            avi_file_count = 0
            files_found = 0
    
            directories = []
    
            start_time = datetime.now()
    
            st = os.statvfs(dev)
            total = st.f_blocks * st.f_frsize
            used = (st.f_blocks - st.f_bfree) * st.f_frsize
            try:
                pct_used = (float(used) / total) * 100
            except ZeroDivisionError:
                pct_used = 0
    
            for item in os.scandir(directory):
                if item.is_dir():
                    directories.append(item.name)
    
            # SDCard_folders.sort()
    
            total_directories = len(directories)
    
            # Organize into sets across all folders
            for i, item in enumerate(directories, start=1):
                full_folder_name = os.path.join(directory, item)
                files = os.listdir(full_folder_name)
                files.sort()
                total_files = len(files)
                for j, file_name in enumerate(files, start=1):
                    time.sleep(0.1)
                    self.filesProcessed.emit(j, total_files)
                    full_file_name = os.path.join(full_folder_name, file_name)
                    if file_name.endswith(".jpg"):
                        jpg_file_count += 1
                    elif file_name.endswith(".avi"):
                        avi_file_count += 1
                self.directoriesProcessed.emit(i, total_directories)
            stop_time = datetime.now()
            elapsed_time = stop_time - start_time
            files_found = jpg_file_count + avi_file_count
            self.finished.emit(
                dev, pct_used, elapsed_time, i, jpg_file_count, avi_file_count, files_found
            )
    
    
    class ProgressBarDialog(QDialog):
        def __init__(self, parent=None):
            super().__init__(parent)
            self.setWindowTitle("Loading Sets and Files")
            self.directories_progressbar = QProgressBar(format="%v of %m")
            self.files_progressbar = QProgressBar(format="%v of %m")
    
            button_box = QDialogButtonBox()
            button_box.setOrientation(Qt.Horizontal)
            button_box.setStandardButtons(QDialogButtonBox.Cancel | QDialogButtonBox.Ok)
    
            button_box.accepted.connect(self.accept)
            button_box.rejected.connect(self.reject)
    
            lay = QVBoxLayout(self)
            lay.addWidget(QLabel("Directories processed:"))
            lay.addWidget(self.directories_progressbar)
            lay.addWidget(QLabel("Files processed:"))
            lay.addWidget(self.files_progressbar)
            lay.addWidget(button_box)
    
            self.setMaximumHeight(self.sizeHint().height())
            self.resize(400, self.height())
    
        @pyqtSlot(int, int)
        def update_directories_processed(self, progress, maximum):
            self.directories_progressbar.setMaximum(maximum)
            self.directories_progressbar.setValue(progress)
    
        @pyqtSlot(int, int)
        def update_files_processed(self, progress, maximum):
            self.files_progressbar.setMaximum(maximum)
            self.files_progressbar.setValue(progress)
    
    
    class MainWindow(QMainWindow):
        def __init__(self, parent=None):
            super().__init__(parent)
    
            self.SDCardCounts_label = QLabel()
            self.setCentralWidget(self.SDCardCounts_label)
            self.resize(640, 480)
    
        @pyqtSlot(str, float, int, int, int, int, int)
        def show_scanner_data(
            self,
            dev,
            pct_used,
            elapsed_time,
            folder_count,
            jpg_file_count,
            avi_file_count,
            files_found,
        ):
            self.SDCardCounts_label.setText(
                "{} ({:.0f}% Used) -- Folders: {:d} -- Files: {:,d} (JPGs: {:,d}, AVIs: {:,d})".format(
                    dev,
                    pct_used,
                    folder_count,
                    files_found,
                    jpg_file_count,
                    avi_file_count,
                )
            )
    
    
    def main():
        app = QApplication(sys.argv)
    
        w = MainWindow()
        progressdialog = ProgressBarDialog()
    
        scanner = SDCardScanner()
    
        scanner.directoriesProcessed.connect(progressdialog.update_directories_processed)
        scanner.filesProcessed.connect(progressdialog.update_files_processed)
        scanner.start("/home", "/home/dave/TrailCamera_Test/DCIM")
    
        scanner.finished.connect(w.show_scanner_data)
        if progressdialog.exec_() == QDialog.Accepted:
            w.show()
        else:
            sys.exit(0)
    
        ret = app.exec_()
        sys.exit(ret)
    
    
    if __name__ == "__main__":
        main()
    

    【讨论】:

    • 谢谢!我会将您的建议纳入计划!仅供参考,SD 卡(和文件)来自跟踪相机,它们设置为拍摄两张照片和一个 15 秒的视频。我将它们视为“集合”(2 张照片和 1 个视频)并以这种方式保存。我将主要代码刻在我发布的内容中,以找到一种方法来执行您的建议!
    猜你喜欢
    • 1970-01-01
    • 2022-08-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多