当我尝试删除之前在 PySide6 QLabel 中“启动”的 .GIF 时,如何防止出现 PermissionError?

问题描述 投票:0回答:1

当我尝试删除之前在 PySide6 应用程序中“播放”的 .GIF 文件时,我将非常感谢您帮助解决这个 PermissionError 问题。 完整的可复制代码+下面的示例 .gif 文件。

在我的 PySide6 应用程序中,我创建一个

QLabel
并在其中显示一个 .GIF 文件。

这涉及到首先创建一个

QMovie
对象(它接收当前目录中 .GIF 文件的相对路径),然后使用
QLabel.setMovie(QMovie)
方法在标签内显示 .GIF 文件。 最后,
QMovie.start()
方法开始播放.GIF。

此后,我希望能够删除 .GIF 文件。 我读过两个用于删除文件的Python函数:

os.remove()
pathlib.Path.unlink()

在没有任何 PySide6 代码的情况下,这两个函数都可以按需要工作。 我可以从我的计算机中删除 .GIF 文件,而不会出现任何 PermissionError。

当我在 PySide6 应用程序中调用 QMovie.start() 来“播放”我的 .GIF 文件后,尝试调用这些函数中的任何一个时,会出现 PermissionError。 这是完整的 PermissionError 语句:


PermissionError: [WinError 32] The process cannot access the file because it is being used by another process: 'test_gif_question_mark.gif'


我已经使用
psutil

读出了我机器上的所有进程,它告诉我进程

python.exe
正在“使用”我的 .GIF 文件,并且没有其他进程正在使用它。
如果我不调用

QMovie.start()

,则不会出现PermissionError,即在创建QMovie

对象后,甚至在调用
QLabel.setMovie(QMovie)
之后,我可以自由删除.GIF文件。
很明显,我的 PySide6 应用程序打开并读取 .GIF 文件中的数据,以便显示和“播放”与其关联的动画。  对我来说不明显的是如何“关闭”.GIF 文件,以便 
python.exe

进程停止“使用”.GIF 文件,让我可以自由地删除它。

这是我尝试“关闭”.GIF 文件的一系列操作:

打电话
    QMovie.stop()
  • .

    打电话
  • QLabel.clear()
  • .

    使用
  • QMovie
  • 删除

    del

    对象。

    使用
  • QLabel
  • 删除

    del

     对象。

    关闭
  • QApplication
  • .

    使用
  • QApplication
  • 删除

    del

     .

    使用
  • QMainWindow
  • 删除

    del

     实例 - 这是我的变量中除上述所有操作之外的唯一其他实例。
    
    

    如果我随后尝试使用
  • os.remove()

Path.unlink()

 删除 .GIF 文件,上述方法都不会阻止 PermissionError 。  使用 
psutil
 ,我可以看到即使在所有这些方法之后,
python.exe
 仍然“使用”我的 .GIF 文件。
在我看来,
QMovie.stop()

没有做我想要它做的事情 - 我希望它“放开”并“关闭”.GIF文件,以便

python.exe

进程不再使用它,我可以要求 Windows 删除该文件。
如果您知道我如何强制
python.exe

停止使用.GIF文件,或者有任何其他解决方案,我将非常感谢您的帮助。

完整的可重现代码:

import sys import os from pathlib import Path from PySide6.QtWidgets import QMainWindow, QVBoxLayout, QWidget, QApplication, QPushButton, QLabel, QHBoxLayout from PySide6.QtGui import QMovie # Description: This script builds a small QApplication GUI with buttons that stimulate various steps in the process # of using, displaying, stopping using, and finally trying to delete a .gif file. class MainWindow(QMainWindow): def __init__(self): super().__init__() # Specify the path to the gif file from the current directory: self.path_to_gif = 'test_gif_question_mark.gif' # Create an instance of the QLabel class: self.my_label = QLabel() # Placeholder for the QMovie object: self.my_QMovie = None # Create a button to make the QMovie object: my_button_create_qmovie = QPushButton() my_button_create_qmovie.setText('Create QMovie') my_button_create_qmovie.clicked.connect(self.clicked_create_button) # Create a button to display the QMovie object in the QLabel: my_button_display_qmovie = QPushButton() my_button_display_qmovie.setText('Display QMovie') my_button_display_qmovie.clicked.connect(self.clicked_display_button) # Create a button to try to stop using the gif: my_button_stop_using_gif = QPushButton() my_button_stop_using_gif.setText('Try to stop using gif file') my_button_stop_using_gif.clicked.connect(self.clicked_stop_button) # Create a button to try to delete the gif: my_button_delete_gif = QPushButton() my_button_delete_gif.setText('Delete gif file') my_button_delete_gif.clicked.connect(self.clicked_delete_button) # Place the buttons in a horizontal layout: layout_h_1 = QHBoxLayout() layout_h_1.addWidget(my_button_create_qmovie) layout_h_1.addWidget(my_button_display_qmovie) layout_h_2 = QHBoxLayout() layout_h_2.addWidget(my_button_stop_using_gif) layout_h_2.addWidget(my_button_delete_gif) # Assign the horizontal layouts to empty widgets: widget_h_1 = QWidget() widget_h_1.setLayout(layout_h_1) widget_h_2 = QWidget() widget_h_2.setLayout(layout_h_2) # Place the label & buttons in a vertical layout: layout_v = QVBoxLayout() layout_v.addWidget(self.my_label) layout_v.addWidget(widget_h_1) layout_v.addWidget(widget_h_2) # Create a placeholder widget to hold the layout. widget = QWidget() widget.setLayout(layout_v) # Set the central widget of the main window: self.setCentralWidget(widget) def clicked_create_button(self): # Create a QMovie to display: self.my_QMovie = QMovie(self.path_to_gif) print('\nCreated QMovie object using path to .gif file') def clicked_display_button(self): # Display the gif inside the label: self.my_label.setMovie(self.my_QMovie) # Start the movie playing: self.my_QMovie.start() print('\nDisplayed and started QMovie') def clicked_delete_button(self): # Use os.remove to try to delete the gif file: # os.remove(self.path_to_gif) # Use pathlib.unlink to try to delete the gif file: path_gif = Path(self.path_to_gif) path_gif.unlink() def clicked_stop_button(self): # Try different things to get python to stop using the .gif file: # Try 'stopping' the QMovie object: self.my_QMovie.stop() # Try clearing the QLabel: self.my_label.clear() # Try deleting the QMovie object: del self.my_QMovie # Try deleting the QLabel: del self.my_label # Run the QApplication: app = QApplication(sys.argv) w = MainWindow() w.show() app.exec() print('app finished') # Try deleting the QApplication after it has finished: del app # Try deleting the MainWindow: del w # Try deleting the gif file even after the QApplication has closed: os.remove('test_gif_question_mark.gif')

.用于测试的GIF文件:

test_gif_question_mark.gif

这是PySide6的一个bug。当您调用
python qt permissions pyside6 qmovie
1个回答
0
投票
时,正如您在

source

 中看到的那样,绑定库假定标签拥有电影,但 C++ 实现实际上并没有使其发生。在
这里报告错误怎么样? 无论如何,这可以通过多种方式来补救,例如self.my_QMovie.deleteLater()

self.my_QMovie.setDevice(None)

,但最好的方法是让标签在C++端拥有电影,如下例所示。
...
class MainWindow(QMainWindow):
    ...
    def clicked_create_button(self):
        self.my_QMovie = QMovie(self.path_to_gif, parent=self.my_label)
    ...
    def clicked_stop_button(self):
        self.my_QMovie.setParent(None)
        self.my_QMovie = None
        self.my_label.clear()
...
print('app finished')

# You don't need to delete the app
del w
os.remove('test_gif_question_mark.gif')


© www.soinside.com 2019 - 2024. All rights reserved.