【问题标题】:PyQt5 not move file and show text in QTextEdit parallelPyQt5 不移动文件并在 QTextEdit 中并行显示文本
【发布时间】:2021-04-07 13:17:41
【问题描述】:

我写了一个小 GUI 来将文件从一个目录移动到另一个目录,我的想法是使用 QTextEdit 来显示进度,当一个文件移动时,然后在 QTextEdit 中写入这个文件名,依此类推,直到所有文件都被移动。我尝试如下:

from PyQt5 import QtGui
from PyQt5.QtWidgets import QStyleFactory, QApplication, QWidget, QListWidget, QLabel, QVBoxLayout, QHBoxLayout, QPushButton
from PyQt5.QtWidgets import (QWidget, QLabel, QLineEdit, QFileDialog,
                             QTextEdit, QGridLayout, QApplication, QMessageBox, QProgressBar)

from PyQt5.QtCore import Qt, QThread, pyqtSignal
import sys, os, time

     
            
class Window(QWidget):
    def __init__(self):
        super().__init__()
        self.title = "test"
        self.top = 200
        self.left = 500
        self.width = 400
        self.height = 300
        self.InitWindow()
        
    def InitWindow(self):
        self.setWindowTitle(self.title)
        self.setGeometry(self.left, self.top, self.width, self.height)
       
        # main layout
        self.vbox = QVBoxLayout()
        
        # for src path
        self.hbox1 = QHBoxLayout()
        self.label1 = QLabel('Source      ')
        self.src = QLineEdit()
        self.path1 = self.src.text()
        self.btnSrc = QPushButton('...')
        self.btnSrc.clicked.connect(lambda: self.getSrc())
        self.hbox1.addWidget(self.label1)
        self.hbox1.addWidget(self.src)
        self.hbox1.addWidget(self.btnSrc)
            
        # for des path
        self.hbox2 = QHBoxLayout()
        self.label2 = QLabel('Destination')
        self.des = QLineEdit()
        self.path2 = self.des.text()
        self.btnDes = QPushButton('...')
        self.btnDes.clicked.connect(lambda: self.getDes())
        self.hbox2.addWidget(self.label2)
        self.hbox2.addWidget(self.des)
        self.hbox2.addWidget(self.btnDes)
        
        self.statusBox = QTextEdit()
        self.statusBox.setReadOnly(True)
        
        self.btnStart = QPushButton('Start')
        self.btnStart.clicked.connect(self.startMov)
        
        self.vbox.addLayout(self.hbox1)
        self.vbox.addLayout(self.hbox2)
        self.vbox.addWidget(self.statusBox)
        self.vbox.addWidget(self.btnStart)
        
        self.setLayout(self.vbox)
        
        self.show()
        
    def getSrc(self):
        try:
            self.path1 = QFileDialog.getExistingDirectory(self)
            self.src.setText(self.path1)
            #all file in this path
            self.filenames = os.listdir(self.path1)
            self.statusBox.setPlainText(str(len(self.filenames)) + ' files found in this folder:')
            self.statusBox.append('')
            for i, sample in enumerate(self.filenames):
                self.statusBox.append(str(i+1) + '. ' + sample)
        except:
            pass
        
    def getDes(self):
        try:
            self.path2 = QFileDialog.getExistingDirectory(self)
            self.des.setText(self.path2)
        except:
            pass
    
    def startMov(self):    
        buttonReply = QMessageBox.question(self, 'wait...', "Do you want to move all this files?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
        if buttonReply == QMessageBox.Yes:
            for file in self.filenames:
                blueText = "<span style=\" font-size:8pt; font-weight:600; color:#0047b3;\" >moving: </span>"
                greenText = "<span style=\" font-size:8pt; font-weight:600; color:#009933;\" >done! </span>"
                self.statusBox.append(blueText)                          
                os.replace(self.path1 + '\\' + file, self.path2 + '\\' + file)
                self.statusBox.append(file)
                self.statusBox.append(greenText)
        else:
            print('No clicked.')
    
App = QApplication(sys.argv)
window = Window()
sys.exit(App.exec())

但它只在所有文件移动后在 QTextEdit 中显示文本,我根本看不到进度。

为什么会这样,我该如何解决?

编辑: 我想要的顺序如下:

# 1. before one file is moved --> in QLineEdit should be: moving 
self.statusBox.append(blueText) 

# 2. file should be moved from src to des 
os.replace(self.path1 + '\\' + file, self.path2 + '\\' + file) 

# 3. name of file stand in QLineEdit 
self.statusBox.append(file) 

# 4. then 'done' stand in QLineEdit 
self.statusBox.append(greenText)

但现在的顺序是:1 2 4 3 在所有文本都存在之后,文件被移动

【问题讨论】:

标签: python pyqt5 qtextedit


【解决方案1】:

我尝试通过这段代码进行测试。

def startMov(self):    
        buttonReply = QMessageBox.question(self, 'wait...', "Do you want to move all this files?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
        if buttonReply == QMessageBox.Yes:
            dir_ = QDir()
            import time
            for file in self.filenames:
                # QApplication.processEvents()
                blueText = "<span style=\" font-size:8pt; font-weight:600; color:#0047b3;\" >moving: </span>"
                greenText = "<span style=\" font-size:8pt; font-weight:600; color:#009933;\" >done! </span>"
                self.statusBox.append(blueText)                   
                os.replace(self.path1 + '\\' + file, self.path2 + '\\' + file)
                print(104, dir_.exists(self.path2 + '\\' + file))                      
                self.statusBox.append(file)
                self.statusBox.append(greenText)
                self.statusBox.repaint()

104 总是返回 True。它表示移动的文件在 3 和 4 之前存在于目标文件。 我假设操作系统在某种程度上像缓冲区对象一样存储文件数据。 完成后,操作系统会立即显示目标文件夹上的文件名。 当我写评论时,我不知道如何连接到操作系统执行。

很抱歉,我无法按照您的要求解决这个问题。希望其他英雄来。

解决方案

一种可能的解决方案是使用QApplication.processEvents()

至于你正在导入QThread,我想你可能会尝试实现线程。

Qt 在一个线程上运行,Qt GUI 应该在这个线程上运行。

你不应该在另一个线程中使用某种 Qt GUIS。

processEvents,只能在线程中使用并发进程。

#is omitting...

# class Thread(QThread):
#     def __init__(self, parent=None):
#         super(Thread, self).__init__(parent)
        
#     def run(self):
#         self.parent().forloop()
            

#is omitting...  
        
    def startMov(self):    
        buttonReply = QMessageBox.question(self, 'wait...', "Do you want to move all this files?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
        if buttonReply == QMessageBox.Yes:
            QApplication.processEvents()
            for file in self.filenames:
                QApplication.processEvents()
                blueText = "<span style=\" font-size:8pt; font-weight:600; color:#0047b3;\" >moving: </span>"
                greenText = "<span style=\" font-size:8pt; font-weight:600; color:#009933;\" >done! </span>"
                self.statusBox.append(blueText)                          
                os.replace(self.path1 + '\\' + file, self.path2 + '\\' + file)        
                self.statusBox.append(file)
                self.statusBox.append(greenText)
            # I don't recommend it.Error happends.
            # thread = Thread(self)
            # thread.execute.connect(self.execute)
            # thread.start()
            
        else:
            print('No clicked.')
#is omitting...

第二个解决方案 你从QWidget调用方法的repaint()

#is omitting...
def startMov(self):    
        buttonReply = QMessageBox.question(self, 'wait...', "Do you want to move all this files?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
        if buttonReply == QMessageBox.Yes:
            # QApplication.processEvents()
            for file in self.filenames:
                # QApplication.processEvents()
                blueText = "<span style=\" font-size:8pt; font-weight:600; color:#0047b3;\" >moving: </span>"
                greenText = "<span style=\" font-size:8pt; font-weight:600; color:#009933;\" >done! </span>"
                self.statusBox.append(blueText)                          
                os.replace(self.path1 + '\\' + file, self.path2 + '\\' + file)        
                self.statusBox.append(file)
                self.statusBox.append(greenText)
                self.statusBox.repaint()
            # I don't recommend it.
            # thread = Thread(self)
            # thread.start()
            
        else:
            print('No clicked.')
#is omitting...

为什么 QTextEdit 的 append 方法有效?

事实上,这在QTextEdit 的表面下是有效的。 例如,您可以通过这些代码进行检查。

            print(self.statusBox.document().characterCount())
            print(self.statusBox.document().blockCount())

您可以确保 blockcount 和 characterCount 在增加。

但似乎Qt并没有一一重绘。 Qt在最后一次重绘gui。可能是为了保存。这取决于开发者。

如果你想使用QThread,我建议你提前阅读Modify Qt GUI from background worker thread。 不过我觉得没必要。

【讨论】:

  • 嗨,Haru,谢谢你的回答,我已经尝试了你的两种解决方案。两者都逐行更新状态框,但毕竟文件是移动的,而不是我写的代码顺序
  • 感谢您的回复。无论如何,我已经写了QProcessEvents()它不存在。 processEvents()是正确的。我目前对您的问题的理解如下: 1-您要查看文件正在移动的过程。 2-但现在,只显示过程的结果。 3-如何修复这个?根据您的评论,文件顺序有问题吗?在我的测试中,文件是有序的,没有问题。或者,是您编码的顺序问题吗?AFAI阅读了您的第一个问题,我认为您没有写下顺序。你能把要点写得更清楚吗?
  • @actnmk 我明白了。您希望文件在 2 和 4 期间移动到另一个文件中。为了确保这种现象,我们需要在查看两个文件(在两个 QLineEdits 指定的位置)的情况下执行此代码。对吗?当然,文件不会在时间。我已经明白你想要什么了,感谢你的回复!我努力解决这个问题。
  • 理论上,os.replace是在你的OS执行下的。python授权给它执行。之后,OS尽可能快地执行命令。也就是说,这是另一个线程,Qt有自己的线程,所以python(Qt)授权给OS执行后,Qt又回到自己的线程执行,也就是说, 4.如果不担心速度,一定要检查是否存在一个一个地移动文件。简而言之,Qt 的工作就是将 os.replace 的工作传递给你的 OS 系统。
  • 总之你的代码肯定是按顺序执行的,但是文件的视图并没有体现出来。因为执行的区域不在Qt中。总之这个问题是一种Qt 和你的操作系统之间的线程编程。我认为你不能只在 Qt 领域解决这个问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-14
  • 1970-01-01
相关资源
最近更新 更多