七、PyQt5文件及文件夹操作(高级应用篇)

三、高级应用

      该部分包括文件及文件夹操作、PyQt5绘图技术、多线程编程、PyQt5程序的打包发布。学完这一部分,能够开发文件流程序、图形图像程序、多线程应用程序等,能够对PyQt5程序进行打包。

(一)文件及文件夹操作

      在变量、序列和对象中存储的数据是暂时的,程序结束后就会丢失,为了能长时间保存程序中的数据,需要将其保存到磁盘文件中。Python中内置了对文件和文件夹进行操作的模块,PyQt5同样提供了对文件和文件夹进行操作的类。
      主要用到的模块有os模块、os.path模块和shutil模块;然后使用PyQt5中的QFile类和QDir类对文件和文件夹操作。在实际开发时,推荐使用Python内置的函数和模块对文件及文件夹进行操作。

1. Python内置的文件操作

      Python中内置了文件(File)对象。在使用文件对象时,首先需通过内置的open()方法创建一个文件对象,然后通过该对象提供的方法进行一些基本文件操作。例如,可用文件对象的write()方法向文件中写入内容,以及使用close()方法关闭文件等。

1. 创建和打开文件

      在Python中,想要操作文件需要先创建或打开指定的文件并创建文件对象,这里可通过内置的open()方法实现。

open()方法的基本语法

file = open(filename[, mode[, buffering]])

open()方法的参数说明

参数 说明
file 要创建的文件对象。
filename 要创建或打开文件的文件名称,需要用单引号或双引号括起来。如果要打开的文件和当前文件在同一个目录下,那么直接写文件名即可,否则需要指定完整路径。例如,要打开当前路径下的名称为status.txt的文件,可以使用“status.txt”。
mode 可选参数,用于指定文件的打开模式,其参数值如下表。默认的打开模式为只读(即r)。
buffering 可选参数,用于指定读写文件的缓冲模式,值为0表示不缓存; 值为1表示缓存;如果大于1,表示缓冲区的大小。默认为缓存模式。

mode参数的参数值说明

说明 注意
r 以只读模式打开文件。文件的指针将放在文件的开头。 文件必须存在。
rb 以二进制格式打开文件,并且采用只读模式。文件的指针将放在文件的开头。一般用于非文本文件,如图片、声音等。
r+ 打开文件后,可以读取文件内容,也可以写入新的内容覆盖原有内容(从文件开头进行覆盖)。
rb+ 以二进制格式打开文件,并且采用读写模式。文件的指针将会放在文件的开头。一般用于非文本文件,如图片、声音等。
w 以只写模式打开文件。 文件存在,则将其覆盖,否则创建新文件。
wb 以二进制格式打开文件,并且采用只写模式。一般用于非文本文件,如图片、声音等。
w+ 打开文件后,先清空原有内容,使其变为一个空的文件,对这个空文件有读写权限。
wb+ 以二进制格式打开文件。并且采用读写模式。一般用于非文本文件,如图片、声音等。
a 以追加模式打开一个文件。如果该文件已存在,文件指针将放在文件的末尾(即新内容会被写入已有内容之后),否则,创建新文件用于写入。 \
ab 以二进制格式打开文件,并且采用追加模式。如果该文件已经存在,文件指针将放在文件的末尾(即新内容会被写入已有内容之后),否则,创建新文件用于写入。 \
a+ 以读写模式打开文件。如果该文件已经存在,文件指针将放在文件的末尾(即新内容会被写入已有内容之后),否则,创建新文件用于读写。 \
ab+ 以二进制格式打开文件,并且采用追加模式。如果该文件已经存在,文件指针将放在文件的末尾(即新内容会被写入已有内容之后),否则,创建新文件用于读写。 \

      默认情况下,使用open()方法打开一个不存在的文件,会抛出下列异常:
七、PyQt5文件及文件夹操作(高级应用篇)_第1张图片
      解决该错误的方法有两种:

  1. 在当前目录下(即与执行的文件相同的目录)创建一个名称为x.txt的文件。
  2. 在调用open()方法时,指定mode的参数值为w、w+、a、a+。这样,当要打开的文件不存在时,就可以创建新的文件了。

示例:打开文件,若文件不存在就创建

      打开一个名为“message.txt”的文件,如果不存在,则创建。

file = open("message.txt", "w")

      执行上面的代码,将在.py文件的同级目录下创建一个名称为message.txt的文件,该文件没有任何内容。
七、PyQt5文件及文件夹操作(高级应用篇)_第2张图片
      使用open()方法打开文件时,默认采用GBK编码,当被打开的文件不是GBK编码时,可能会抛出异常。解决该问题的方法有两种,一种是直接修改文件的编码,另一种是在打开文件时,直接指定使用的编码方式。推荐采用后一种方法。在调用open()方法时,通过添加encoding='utf-8’参数即可实现将编码指定为UTF-8。如果想指定其他编码,将单引号中的内容替换为想要指定的编码即可。例如,打开采用UTF-8编码保存的notice.txt文件,可以使用下面代码:

file = open("notice.txt", "r", encoding="utf-8")

2. 关闭文件

      打开文件后,需要及时关闭,以免对文件造成不必要的破坏。关闭文件可以使用文件对象的close()方法实现。

close()方法的语法格式

file.close()     # 关闭文件对象
# file为打开的文件对象

      使用close()方法时,会先刷新缓冲区中还没有写入的信息,然后再关闭文件,这样可以将没有写入文件的内容写入文件中。在关闭文件后,便不能再进行写入操作了。

3. 打开文件时使用with语句

      打开文件后,要及时将其关闭。如果忘记关闭可能会带来意想不到的问题。另外。如果在打开文件时抛出了异常,那么将导致文件不能被及时关闭。为了更好地避免此类问题发生,可以使用Python中提供的with语句,从而实现在处理文件时,无论是否抛出异常,都能保证with语句执行完毕后关闭已经打开的文件。

with语句的基本语法格式

with expression as target:
	wiht-body

with语句的参数说明

参数 说明
expression 用于指定一个表达式,这里可以是打开文件的open()方法。
target 用于指定一个变量,并且将expression的结果保存到该变量中。
with-body 用于指定with语句体,其中可以是执行with语句后相关的一些操作语句。如果不想执行任何语句,可以直接用pass语句代替。

示例:使用with语句打开文件

      在打开文件时使用with语句打开“message.txt”文件:

with open("message.txt", "w") as f:      # 使用with语句打开文件
	pass

4. 写入文件内容

      Python的文件对象提供了write()方法,可以向文件中写入内容。

write()方法的语法格式

file.write(string)
# file为打开的文件对象
# string为要写入的字符串

      注意,调用write()方法向文件中写入内容的前提是,打开文件时,指定的打开模式为w(可写)或者a(追加),否则,将抛出下列异常,表示没有写入权限:
七、PyQt5文件及文件夹操作(高级应用篇)_第3张图片

示例:向文件中写入文本内容

      使用open()方法以写方式打开一个文件(如果文件不存在,则自动创建),然后调用write()方法向该文件中写入一条信息,最后调用close()方法关闭文件。

file = open("message.txt", "w", encoding="utf-8")    # 创建或打卡文件
file.write("故不积跬步,无以至千里;不积小流,无以成江海。\n")    # 写入一条信息
file.close()      # 关闭文件对象

      运行程序,在.py文件所在的目录下创建一个名称为message.txt的文件,并且在该文件中写入了文字“故不积跬步,无以至千里;不积小流,无以成江海。“
七、PyQt5文件及文件夹操作(高级应用篇)_第4张图片
      注意,写入文件后,一定要调用close()方法关闭文件,否则写入的内容不会保存到文件中。这是因为在写入文件内容时,操作系统不会立刻把数据写入磁盘,而是先缓存起来只有调用close()方法时,操作系统才会保证没有写入的数据全部写入磁盘。

技巧

      (1)向文件中写入内容时,如果打开文件采用w(写入)模式,则先清空原文件的内容,再写入新的内容;而如果打开文件采用a(追加)模式,则不覆盖原有文件的内容,知识在文件的结尾处增加新内容。

      下面对上面示例的代码进行修改,实现在原内容的基础上再添加一条名言。代码如下:

file = open("message.txt", "a", encoding="utf-8")    # 以追加方式打开文件
file.write("骐骥一跃,不能十步;驽马十驾,功在不舍;锲而舍之,朽木不折;锲而不舍,金石可镂。\n")
file.close()    # 关闭文件对象

执行上面代码,打开message.txt文件,将显示如下效果:
七、PyQt5文件及文件夹操作(高级应用篇)_第5张图片
      (2)除了write()方法,Python的文件对象还提供了writelines()方法,可以实现把字符串列表写入文件,但是不添加换行符。

5. 读取文件

      在Python中打开文件后,除了可以像其中写入或追加内容,还可以读取文件中的内容。读取文件内容主要分3种情况。

1. 读取指定字符

      文件对象提供了read()方法读取指定个数的字符。

read()方法的基本语法
file.read([size])
# file:打开的文件对象。
# size:可选参数,用于指定要读取的字符个数,如果省略,则一次性读取所有内容。

2. 读取一行

      在使用read()方法读取文件时,如果文件很大,一次读取全部内容到内存,容易造成内存不足,所以通常会采用逐行读取。文件对象提供了readline()方法用于每次读取一行数据。

readline()方法的基本语法
file.readline()
# file:打开的文件对象。

3. 读取所有行

      读取全部行的作用同调用read()方法时不指定size参数类似,只不过读取全部行时,返回的是一个字符串列表,每个元素为文件的一行内容。读取全部行,使用的是文件对象的readlines()方法。

readlines()方法的基本语法
file.readlines()
# file:打开的文件对象。

      注意,在读取文件内容时,需要指定文件的打开模式为r(只读)或者r+(读写)。

示例:以3种不同的方式读取文件内容

版本1:没有seek()方法

      分别使用read()、readline()、readlines()方法读取文件中的内容。
代码如下:

with open("message.txt", "r", encoding="utf-8") as file:
    print("==========读取前5个字符==========")
    print(file.read(5))     # 读取前5个字符
    print("==========读取第一行数据==========")
    print(file.readline())    # 读取一行数据
    print("==========读取所有数据==========")
    print(file.readlines())      # 读取全部数据

运行效果如下图:
七、PyQt5文件及文件夹操作(高级应用篇)_第6张图片

版本2:使用seek()方法
with open("message.txt", "r", encoding="utf-8") as file:
    print("==========读取前5个字符==========")
    print(file.read(5))     # 读取前5个字符
    print("==========读取第一行数据==========")
    file.seek(0, 0)
    print(file.readline())    # 读取一行数据
    print("==========读取所有数据==========")
    file.seek(0, 0)
    print(file.readlines())      # 读取全部数据

运行效果如下:
七、PyQt5文件及文件夹操作(高级应用篇)_第7张图片
      使用read()方法读取文件时,是从文件的开头读取的。如果想读取部分内容,可以先使用文件对象的seek()方法将文件的指针移动到新的位置,然后再使用read()方法读取。

seek()方法的基本语法
file.seek(offset[, whence])
# offset:用于指定移动的字符个数(offset的值是按一个汉字占两个字符、英文和数字占1个字符计算的),其具体位置与whence有关。
# whence:whence值为0表示从文件头开始计算,1表示从当前位置开始计算,2表示从文件尾开始计算,默认为0。
示例:从文件的第9个字符开始读取5个字符
...
	file.seek(9)   # 移动文件指针到新的位置
	string = file.read(5)   # 读取5个字符

6. 复制文件

      在Python中复制文件需要使用shutil模块的**copyfile()**方法。

shutil模块的copyfile()方法的基本语法

shutil.copyfile(src, dst)
# src:要复制的源文件。
# dst:复制到的目标文件。

示例:复制文件夹中的图片到项目文件夹中

      将D盘“图片”文件夹中的一张图片复制到项目的image文件夹中:

import shutil

shutil.copyfile("D:/图片/帕德玛刚玉.png", "./image/宝石之国人物1.png")

运行效果如下:(image文件夹下多了一张图片)
七、PyQt5文件及文件夹操作(高级应用篇)_第8张图片

7. 移动文件

      在Python中移动文件需要使用shutil模块的move()方法。

shutil模块的move()方法的基本语法

shutil.move(src, dst)
# src:要移动的源文件。
# dst:移动到的目标文件。

示例:将项目的image文件夹中一张图片移动到image文件夹外

import shutil

shutil.move("./image/宝石之国人物1.png", "./帕德玛刚玉.png")

运行的效果如下:(原本image文件夹中的图片被移出来了)
七、PyQt5文件及文件夹操作(高级应用篇)_第9张图片

原本的目录结构:
七、PyQt5文件及文件夹操作(高级应用篇)_第10张图片
      复制文件和移动文件的区别是,复制文件时,源文件还存在,而移动文件相当于将源文件剪切到另外一个路径,源文件不会存在。

8. 重命名文件

      在Python中重名名文件需要使用os模块的rename()方法。

os模块的rename()方法的基本语法

os.rename(src, dst)
# src:指定要进行重命名的文件。
# dst:指定重命名后的文件。

示例:将项目中的“message.txt”文件重命名为“text.txt”文件

import os


# os.rename("./message.txt", "./image/text.txt")    # 重命名也可以巧用做移动文件
# os.rename("./image/text.txt", "./message.txt")

os.rename("message.txt", "text.txt")

运行效果:
七、PyQt5文件及文件夹操作(高级应用篇)_第11张图片
      另外,也可以使用shutil模块的move()方法对文件进行重命名。

import shutil

shutil.move("text.txt", "message.txt")

七、PyQt5文件及文件夹操作(高级应用篇)_第12张图片
      在执行文件操作时,为了确保能够正常执行,可以使用os.pash模块的exists()方法判断要操作的文件是否存在。

示例:使用os.path,exists()方法判断文件是否存在

import os    # 导入os模块

if os.path.exists("./message.txt"):    # 判断文件是否存在
    print("文件存在")

运行效果如下:
七、PyQt5文件及文件夹操作(高级应用篇)_第13张图片

9. 删除文件

      在Python中删除文件需要使用os模块的remove()方法。

os模块的remove()方法的基本语法

os.remove(path)
# path:表示要删除的文件路径,可以是相对路径,也可以是绝对路径。

示例:删除项目中的"帕德玛刚玉.png"文件

import os

os.remove("./帕德玛刚玉.png")

运行后的项目结构:(删除图片)
七、PyQt5文件及文件夹操作(高级应用篇)_第14张图片
原本项目结构:
七、PyQt5文件及文件夹操作(高级应用篇)_第15张图片

10. 获取文件的基本信息

      在计算机上创建文件后,该文件本身就会包含一些信息。例如,文件的最后一次访问时间、最后一次修改时间、文件大小等基本信息。童工os模块的stat()方法可以获取文件的这些基本信息。

os模块的stat()方法的基本语法

os.stat(path)
# path:要获取文件基本信息的文件路径,可以是相对路径,也可以是绝对路径。
# stat()方法的返回值是一个对象,该对象包含下表中所示的属性。通过访问这些属性可以获取文件的基本信息。

stat()方法返回的对象的常用属性

属性 说明
st_mode 保护模式。
st_ino 索引号。
st_size 硬链接号(被连接数目)。
st_mtime 最后一次修改时间。
st_dev 设备名。
st_uid 用户ID。
st_gid 组ID。
st_atime 最后一次访问时间。
st_ctime 最后一次状态变化的时间(系统不同返回结果也不同,例如,在Windows操作系统下返回的是文件的创建时间)。

示例:获取文件基本信息并在PyQt的界面上显示

      使用PyQt5设计一个窗口,在其中添加一个PushButton控件,用来选择文件并获取文件的信息;添加一个LineEdit控件,用来显示文件的路径;添加一个TextBrowser控件,用来显示文件的信息。在该窗口中使用QFileDialog类显示文件的对话框,在该文件对话框中选择文件后,使用os.stat()获取选择文件的信息,并显示在TextBrowser控件中。

完整代码如下:

from PyQt5 import QtCore, QtWidgets
from PyQt5.QtWidgets import *


class Demo(QWidget):
    def __init__(self, parent=None):
        super(Demo, self).__init__(parent)
        self.initUi()     # 初始化窗口

    def initUi(self):
        self.setWindowTitle("获取文件信息")
        grid = QGridLayout()    # 创建网格布局
        # 创建标签
        label1 = QLabel()
        label1.setText("选择路径:")
        grid.addWidget(label1, 0, 0, QtCore.Qt.AlignLeft)
        # 创建显示选中文件的文本框
        self.text1 = QLineEdit()
        grid.addWidget(self.text1, 0, 1, 1, 3, QtCore.Qt.AlignLeft )
        # 创建选择按钮
        btn1 = QPushButton()
        btn1.setText("选择")
        btn1.clicked.connect(self.getInfo)
        grid.addWidget(btn1, 0, 4, QtCore.Qt.AlignCenter)
        # 显示文件信息的文本浏览器
        self.text2 = QTextBrowser()
        grid.addWidget(self.text2, 1, 0, 1, 5, QtCore.Qt.AlignLeft)
        self.setLayout(grid)    # 设置网格布局

    # 自定义槽函数
    def getInfo(self):
        file = QFileDialog()     # 创建文件对话框
        file.setDirectory("D:\\")     # 设置初始路径为D盘
        if file.exec_():      # 判断是否选择了文件
            filename = file.selectedFiles()[0]     # 获取选择的文件
            self.text1.setText(filename)      # 将选择的文件显示在文本框中
            import os, time    # 导入模块
            fileinfo = os.stat(filename)      # 获取文件信息
            self.text2.setText("文件完整路径:" + os.path.abspath("filename")
                               + "\n 文件大小:" + str(fileinfo.st_size) + " 字节"
                               + "\n 最后一次访问时间:" + time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(fileinfo.st_atime))
                               + "\n 最后一次修改时间:" + time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(fileinfo.st_mtime))
                               + "\n 最后一次状态变化时间:" + time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(fileinfo.st_ctime))
                               )


if __name__ == "__main__":
    import sys
    app = QApplication(sys.argv)    # 创建窗口程序
    demo = Demo()    # 创建窗口类对象
    demo.show()   # 显示窗口
    sys.exit(app.exec_())

运行程序,单击“选择”按钮,选择一个文件后,即可在下面的文本框中显示其信息:七、PyQt5文件及文件夹操作(高级应用篇)_第16张图片

2. Python内置的文件夹操作

      文件夹主要用于分层保存文件,通过文件夹可以分门别类地存放文件。在Python中,并没有提供直接操作文件夹的方法或者对象,而是需要使用内置的os、os.path和shutil模块实现。

1. 获取文件夹路径

      用于定位一个文件或者文件夹的字符串被称为一个路径,在程序开发时,通常涉及两种路径,一种是相对路径,另一种是绝对路径。

1. 相对路径

      在学习相对路径之前,需要先了解什么是当前工作文件。当前工作文件夹是指当前文件所在的文件夹。在Python中,可以通过os模块提供getcwd()方法获取当前工作文件夹。

示例:用os.getcwd()方法获取当前文件夹路径
import os

print(os.getcwd())   # 输出当前文件夹

      执行上面代码,将显示以下文件夹,该路径就是当前工作文件夹。
七、PyQt5文件及文件夹操作(高级应用篇)_第17张图片
      相对路径是依赖于当前工作文件夹的,如果在当前工作文件夹下,有一个名称为message.txt的文件,那么在打开这个文件时,就可以直接写上文件名,这时采用的就是相对路径,message.txt文件的实际路径就是当前工作文件夹“E:\program\Python\Code”+相对路径“message.txt”,即“E:\program\Python\Code\message.txt”。
      如果在当前工作文件夹下,有一个子文件夹demo,并且在该子文件夹下保存着文件message.txt,那么在打开这个文件时就可以写上“demo/message.txt”。例如:

with open("demo/message.txt") as file:    # 通过相对路径打开文件
	pass

      在Python中,指定文件路径时需要对路径分隔符“\”进行转义,即将路径中的“\”替换为“\”。例如,对于相对路径“demo\message.txt”需要使用“demo\message.txt”代替。另外,也可以将路径分隔符“\”替换为“/”
      在指定路径时,可以在表示路径的字符串前面加上字母r(或R),那么该字符串将原样输出,这时路径中的分隔符就不需要再转义了。例如,上面的代码也可以修改如下:

with open(r"demo\message.txt") as file:    # 通过相对路径打开文件
	pass

2. 绝对路径

      绝对路径是指在使用文件时指定文件的实际路径,它不依赖于当前工作文件夹。在Python中,可以通过os.path模块提供的abspath()方法获取一个文件夹的绝对路径。

os.path.abspath()方法的基本语法
os.path.abspath(path)
# path:为要获取绝对路径的相对路径,可以是文件,也可以是文件夹。
示例:获取“image\date.png”的绝对路径
import os

print(os.path.abspath(r"image\date.png"))   # 获取绝对路径

运行结果如下:
七、PyQt5文件及文件夹操作(高级应用篇)_第18张图片

3. 拼接路径

      如果想要将两个或者多个路径拼接到一起组成一个新的路径,可以使用os.path模块提供的join()方法实现。

os.path.join()方法的基本语法
os.path.join(path1[, path2[, ...]])
# path1、path2用于代表要拼接的文件路径,在这些路径间使用逗号进行分隔。
# 如果在要拼接的路径中没有绝对路径,那么最后拼接出来的将是一个相对路径。

      注意,使用os.path.join()方法拼接路径时,并不会检测路径是否真实存在。

示例:拼接两个路径“E:\program\Python\Code”和“demo\message.txt”
import os

print(os.path.join("E:\program\Python\Code", "demo\message.txt"))   # 拼接路径

运行结果:
七、PyQt5文件及文件夹操作(高级应用篇)_第19张图片
      在使用join()方法时,如果要拼接的路径中存在多个绝对路径,那么以从左到右最后一次出现的为准,并且该路径之前的参数都将被忽略
例如:

import os

print(os.path.join("E:\\code", "E:\\python\\mr", "Code", "C:\\", "demo"))   # 拼接路径

运行结果:
七、PyQt5文件及文件夹操作(高级应用篇)_第20张图片
      将两个路径拼接为一个路径时,不要直接使用字符串拼接,而是使用os.path.join()方法,这样可以正确处理不同操作系统的路径分隔符。

2. 判断文件夹是否存在

      在Python中,有时需要判断给定的文件夹是否存在,这时可以使用os.path模块提供的exists()方法实现。

os.path.exists()方法的基本语法

os.path.exists(path)
# path:为要判断的文件夹,可以采用绝对路径,也可以采用相对路径。
# 返回值:如果给定的路径存在,则返回True,否则返回False。

示例:pandaUN绝对路径“C:\demo”是否存在

import os

print(os.path.exists(r"C:\demo"))     # 判断文件夹是否存在

实际上C盘没有demo目录,返回结果为False:
七、PyQt5文件及文件夹操作(高级应用篇)_第21张图片

3. 创建文件夹

      在Python中,os模块提供了两个创建文件夹的方法,一个用于创建一级文件夹,另一个用于创建多级文件夹。

1. 创建一级文件夹

      创建一级文件夹是指一次只能创建顶层文件夹,而不能创建子文件夹。在Python中,可以使用os模块提供的mkdir()方法实现。通过该方法只能创建指定路径中的最后一级文件夹,如果该文件夹的上一级不存在,则抛出FileNotFoundError异常。
七、PyQt5文件及文件夹操作(高级应用篇)_第22张图片

os.mkdir()方法的基本语法
os.mkdir(path, mode=0o777)
# path:指定要创建的文件夹,可以使用绝对路径,也可以使用相对路径
# mode:指定数值模式,默认值为0o777,。该参数在非UNIX系统上无效或被忽略
示例:在项目中创建一个demo文件夹
import os

os.mkdir("./demo")   # 创建demo文件夹

创建文件夹后的项目结构:
七、PyQt5文件及文件夹操作(高级应用篇)_第23张图片
原本的项目结构:
七、PyQt5文件及文件夹操作(高级应用篇)_第24张图片
      如果创建的文件夹已经存在,将抛出FileExistsError异常,要避免该异常,可以先使用os.path.exists()方法判断要创建的文件夹是否存在。
七、PyQt5文件及文件夹操作(高级应用篇)_第25张图片

2. 创建多级文件夹

      使用mkdir()方法只能创建一级文件夹,如果想创建多级,可以使用os模块提供的makedirs()方法,该方法用于采用递归的方式创建文件夹。

os.makedirs()方法的基本语法
os.makedirs(name, mode=0o777)
# name:指定要创建的文件夹,可以使用绝对路径,也可以使用相对路径。
# mode:指定数值模式,默认为0o777。该参数在非UNIX系统上无效或被忽略。
示例:在项目中创建文件夹“./demo/test1/test2/test3”
import os

os.makedirs("./demo/test1/test2/test3")

执行上面代码,无论中间的test1、test2文件夹是否存在,都可以正常执行。因为如果路径中有文件夹不存在,makedirs()会自动创建。
七、PyQt5文件及文件夹操作(高级应用篇)_第26张图片

4. 复制文件夹

      在Python中复制文件夹需要使用shutil模块的copytree()方法

shutil.copytree()方法的基本语法

shutil.copytree(src, dst)
# src:要复制的源文件夹
# dst:复制到的目标文件夹

示例:将项目中的demo文件夹复制到项目中的D盘根目录

import shutil

shutil.copytree("./demo", "D:/demo_copy")

在这里插入图片描述
      在复制文件夹时,如果要复制的文件夹下还有子文件夹,将整体复制到目标文件夹中。

5. 移动文件夹

      在Python中移动文件夹需要使用shutil模块的move()方法。

shutil.move()方法的基本语法

shutil.move(src, dst)
# src:要移动的源文件夹。
# dst:移动到的目标文件夹。

示例:将项目中demo文件夹移动到test文件夹中

import shutil

shutil.move("./demo", "./test/demo")

移动后的目录结构:
七、PyQt5文件及文件夹操作(高级应用篇)_第27张图片

原本的目录结构:
七、PyQt5文件及文件夹操作(高级应用篇)_第28张图片
      复制文件夹和移动文件夹的区别是,复制文件夹时,源文件还存在,而移动文件夹相当于将源文件夹剪切到另外一个路径,源文件不会存在。

6. 重命名文件夹

      在Python中重命名文件夹需要使用os模块的rename()方法。

os.rename()方法的基本语法

os.rename(src, dst)
# src:指定要进行重命名的文件。
# dst:指定重命名后的文件。

示例:将项目文件夹test文件夹重命名为test_demo

import os

os.rename("./test", "test_demo")

七、PyQt5文件及文件夹操作(高级应用篇)_第29张图片
      另外,也可以使用shutil模块的move()方法对文件夹进行重命名。

import shutil

shutil.move("./test_demo", "./test")

七、PyQt5文件及文件夹操作(高级应用篇)_第30张图片

7. 删除文件夹

      删除文件夹可以使用os模块提供的rmdir()方法实现。通过rmdir()方法删除文件夹时,只有当删除的文件夹为空时才起作用。如果要删除的文件夹不为空,使用该方法会报错OSError。
七、PyQt5文件及文件夹操作(高级应用篇)_第31张图片

os.rmdir()方法的基本语法

os.rmdir(path)
# path:为要删除的文件夹,可以使用相对路径或绝对路径。

示例:删除项目中的test文件夹中最内层的test3文件夹

import os

os.rmdir("./test/demo/test1/test2/test3")

删除之后的test文件夹结构:
七、PyQt5文件及文件夹操作(高级应用篇)_第32张图片

原本的test文件夹结构:
七、PyQt5文件及文件夹操作(高级应用篇)_第33张图片
      使用rmdir()方法只能删除空文件夹,如果想删除非空文件夹,则需要使用Python内置的标准模块shutil的rmtree()方法实现。

示例:使用shutil.rmtree()方法删除非空文件夹test

import shutil

shutil.rmtree("./test")   # 删除文件夹下的子文件夹及其包含的所有内容

七、PyQt5文件及文件夹操作(高级应用篇)_第34张图片

8. 遍历文件夹

      遍历在古汉语中的意思是全部走遍,到处周游。在Python中,遍历的意思也类似,就是对指定的文件夹下的全部文件夹(包括子文件夹)及文件走一遍。在Python中,os模块的walk()方法用于实现遍历文件夹的功能。

os.walk()方法的基本语法

os.walk(top[, topdown][, onerror][, followlinks])
# top:用于指定要遍历内容的根文件夹。
# topdown:可选参数,用于指定遍历的顺序,如果值为True,表示自上而下遍历(即先遍历根文件夹);如果值为False,表示自下而上遍历(即先遍历最后一级子文件夹)。默认为True。
# onerror:可选参数,用于指定错误处理方式,默认为忽略,如果不想忽略也可以指定一个错误处理方法。通常情况下采用默认格式。
# followlinks:可选参数,默认情况下,walk()方法不会向下转换成解析到文件夹的符号链接,将该参数值设置为True,表示用于指定在支持的系统上访问由符号链接指向的文件夹。
# 返回值:返回一个包括3个元素(dirpath,dirnames,filenames)的元组生成器对象。其中,dirpath表示当前遍历的路径,是一个字符串;dirnames表示表示当前路径下包含的子文件夹,是一个列表;filenames表示当前路径下包含的文件,也是一个列表。

示例:遍历文件夹“D:\PyQtLearning\demo\”

import os     # 导入os模块

tuples = os.walk("D:\\PyQtLearning\\demo")    # 遍历指定文件夹
for tuple1 in tuples:     # 通过for循环遍历输出结果
    print(tuple1, "\n")     # 输出每一级文件夹的元组

运行效果如下:
七、PyQt5文件及文件夹操作(高级应用篇)_第35张图片
      walk()方法只在Unix和Windows中有效。
      通过walk()方法可以遍历指定文件夹下的所有文件及子文件夹,但这种方法遍历的结果可能会很多,有时我们只需要指定文件夹根目录下的文件和文件夹,该怎么办呢?os模块提供了listdir()方法,可以获取指定文件夹根目录下的所有文件及子文件夹名。

os.listdir()方法的基本语法

os.listdir(path)
# path:表示要遍历的文件夹路径。
# 返回值:返回值是一个列表,包含了所有文件名及子文件夹名。

示例:在Qt界面上显示遍历的指定文件夹“D:\PyQtLeraning\demo”

      创建一个.py文件,导入PyQt5相应模块,设计一个窗口程序,在该程序中添加一个PushButton按钮,用来选择路径,并遍历选择的路径;添加一个TableWidget表格控件,用来显示选择的路径;添加一个TableWidget表格控件,用来显示选择路径中包含的所有文件及子文件夹。具体实现时,使用QFileDialog.getExistingDirectory()方法弹出一个选择文件夹的对话框,在该对话框中选择文件夹后,判断如果不为空,则使用os模块的listdir()方法遍历所选择的文件夹,并将其中包含的文件及子文件夹显示在TableWidget表格中。
完整代码如下:

from PyQt5 import QtWidgets, QtCore, QtGui
from PyQt5.QtWidgets import QFileDialog


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(500, 300)   # 设置窗口大小
        MainWindow.setWindowTitle("遍历文件夹")   # 设置窗口标题
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        # 添加表格
        self.tableWidget = QtWidgets.QTableWidget(self.centralwidget)
        self.tableWidget.setGeometry(QtCore.QRect(0, 40, 501, 270))
        self.tableWidget.setObjectName("tableWidget")
        self.tableWidget.setColumnCount(2)    # 设置列数
        # 设置第一列的标题
        item = QtWidgets.QTableWidgetItem()
        self.tableWidget.setHorizontalHeaderItem(0, item)
        item = self.tableWidget.horizontalHeaderItem(0)
        item.setText("文件名")
        # 设置第二列的标题
        item = QtWidgets.QTableWidgetItem()
        self.tableWidget.setHorizontalHeaderItem(1, item)
        item = self.tableWidget.horizontalHeaderItem(1)
        item.setText("详细信息")
        self.tableWidget.setColumnWidth(0, 100)   # 设置第一列宽度
        # 设置最后一列自动填充容器
        self.tableWidget.horizontalHeader().setStretchLastSection(True)
        # 创建选择路径按钮
        self.pushButton = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton.setGeometry(QtCore.QRect(10, 10, 75, 23))
        # 为按钮设置字体
        font = QtGui.QFont()
        font.setPointSize(10)
        font.setBold(True)
        font.setWeight(75)
        self.pushButton.setFont(font)
        self.pushButton.setObjectName("pushButton")
        self.pushButton.setText("选择路径")
        MainWindow.setCentralWidget(self.centralwidget)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)
        self.pushButton.clicked.connect(self.getinfo)    # 关联“选择路径”的clicked信号

    # 选择路径,并获取其中的所有文件信息,将其显示在表格中
    def getinfo(self):
        try:
            import os
            # dir_path 为选择的文件夹的绝对路径,第二形参为对话框标题
            # 第三个为对话框打开后默认的路径
            self.dir_path = QFileDialog.getExistingDirectory(None, "选择路径", os.getcwd())
            if self.dir_path != "":
                self.list = os.listdir(self.dir_path)    # 列出文件夹下所有的目录与文件
                flag = 0    # 标识插入新行的位置
                for i in range(0, len(self.list)):      # 遍历文件列表
                    # 拼接路径和文件名
                    filepath = os.path.join(self.dir_path, self.list[i])
                    self.tableWidget.insertRow(flag)    # 添加新行
                    self.tableWidget.setItem(flag, 0, QtWidgets.QTableWidgetItem(self.list[i]))    # 设置第一列的值为文件(夹)名
                    self.tableWidget.setItem(flag, 1, QtWidgets.QTableWidgetItem(filepath))   # 设置第二列的值为文件或文件夹的完整路径

                    flag += 1     # 计算下一个新行的插入位置
        except Exception as e:
            print(e)


if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    MainWindow = QtWidgets.QMainWindow()
    ui = Ui_MainWindow()
    ui.setupUi(MainWindow)
    MainWindow.show()
    sys.exit(app.exec_())

运行程序,单击“选择路径”按钮,选择一个文件夹后,将会在下面的表格中显示选中文件夹下的所有子文件夹及文件。
七、PyQt5文件及文件夹操作(高级应用篇)_第36张图片

3. PyQt5中的文件及文件夹操作

      在PyQt5中对文件和文件夹进行操作时,主要用到QFile类、QFileInfo类和QDir类
      在PyQt5窗口程序中对文件或者文件夹进行操作时,不强制必须用PyQt5中提供的QFile、QDir等类,使用Python内置的文件对象同样可以。

1. 使用QFile类操作文件

      QFile类主要用来对文件进行打开、读写、复制、重命名、删除等操作。

QFile类的常用方法及说明

方法 说明
open()
  • 打开文件,文件的打开方式可以通过QIODevice的枚举值进行设置,取值如下:
  • QIODevice.NotOpen:不打开;
  • QIODevice.ReadOnly:以只读方式打开;
  • QIODevice.WriteOnly:以只写方式打开;
  • QIODevice.ReadWrite:以读写方式打开;
  • QIODevice.Append:以追加方式打开。
isOpen() 判断文件是否打开。
close() 关闭文件。
copy() 复制文件。
exists() 判断文件是否存在。
read() 从文件中读取指定个数的字符。
readAll() 从文件中读取所有数据。
readLine() 从文件中读取一行数据。
remove() 删除文件。
rename() 重命名文件。
seek() 查找文件。
setFileName() 设置文件名。
write() 向文件中写入数据
获取文件信息需要使用QFileInfo类。

QFileInfo类的常用方法及说明

方法 说明
size() 获取文件大小。
created() 获取文件创建时间。
lastModified() 获取文件最后一次修改时间。
lastRead() 获取文件最后一次访问时间。
isDir() 判断是否是文件夹。
isFile() 判断是否为文件。
isHidden() 判断是否隐藏。
isReadable() 判断是否可读。
isWritable() 判断是否可写。
isExecutable() 判断是否可执行。

示例:按文件存储知乎奇葩问题

      爬取知乎上的一篇奇葩问题收集文章,将不同问题保存在不同文件中,放在同一文件夹questions中,文本名称由问题的前5个字+"…"组成。
      在Qt Designer设计器中创建一个窗口,在其中添加两个LineEdit控件,分别用来显示选择的文件和输入文件创建路径;添加两个PushButton控件,分别用来选择存储问题的文件和执行创建文件的功能;添加一个TableWidget控件,用来显示创建的文件列表。窗口设计完成后保存为.ui文件,并使用PyUIC工具将其转换为.py文件。
      在.py文件中,定义两个槽函数,分别用来实现选择存储问题的文本文件,以及根据文件内容创建多个文件,并将内容分别写入不同文件中的功能。

1. 爬取文章和保存问题的代码如下:
# -*- encoding=utf-8 -*-
import requests
import re
import os


def get_text(url):
    headers = {
        "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36",
        "cookie": 'q_c1=932f9a227b7048bd98d1b6e87539a6dc|1658322630000|1658322630000; _zap=e8dae566-79e6-4384-8211-0576cc5bb078; d_c0="AHCfxXcCRxWPTvpdNJCD-fJm1izHlDzFZ5M=|1658322629"; _9755xjdesxxd_=32; YD00517437729195%3AWM_TID=xYBYS0ziPdREVVUERFaBHBbfytp0AaGd; __snaker__id=3bs9XpTn5qAnjuZv; _xsrf=7f05cb83-9289-49ac-9371-ebf0b500b77d; Hm_lvt_98beee57fd2ef70ccdd5ca52b9740c49=1664287408,1664456793,1665028997,1665380150; gdxidpyhxdE=KVTsfJjcP5BJAoRy%2B%5CVakCk%2BUOZvgONNHVfPZmqDShweJJwEV18q70vZZoqDnwyOgEAED%2FQ7t1Um%5CBg0PG%2F4nkw6T1nnOhdnGmaKyLctJvNeG0kcuj5%5CVvPnluo90GtDiPa6GsoWT1YQep5A8Ub9TsUVf2GoqE0HkP5bPWf9ztQQJ6zv%3A1665381071287; YD00517437729195%3AWM_NI=XYUvqE1R%2B2HtpXMIIYzV4HrD0BFNqGYUp2LetwbCkwkWJ%2BtM%2FlF05Ey5KzQoS3G4AnRDNFxT5JE2JWc6mhS0B5jys%2Fc97CeK8LFIIayRrgd593XeE5Au%2FN6lUrHvgyJwNEk%3D; YD00517437729195%3AWM_NIKE=9ca17ae2e6ffcda170e2e6ee82d3488695a1dad23487ef8ea7c14a939f9ab0c149f59b8f83cb50a7ec9996fc2af0fea7c3b92a9c879b97f44ff38ee5d3cb80b48883aacc53ba8afca9f150f49e8aaff44d9b8bc0d3cf6fb8a9aea4ea7da98986ccf747edb48ad1b84fbbeb859bd46591ed89d5c466929c82a7fb60f7968bd3c95da1eaa389d23c82eba69af225a1e7f9a4e841fc948aaacc6bbb8ebe88e73ffbed9886c83f83abe1afb56897ae8897e6738aa982a6ea37e2a3; captcha_ticket_v2=2|1:0|10:1665380439|17:captcha_ticket_v2|704:eyJ2YWxpZGF0ZSI6IkNOMzFfRlhZLnZ4YjdKN1VtYkNRaHQxMThJaWNPZDVtTUNpeUd1VzVteGdLSl9jU1psSnlzUTBJWHM0MC5qVXJKOEN3UlRMNDdaVC1CeF96SVZESU9YYTZTdi5HNVpKRVN0VjRpcDZ3Z1EueXJ3ZnVTcEVvTUt3amZNUXk5Ul9mZWRDeWxPMk5jTlRqX1JrNWRJWFdCeHhBNDdmQ09EcVY2UVhzWGNvNW9rTGtya2RqcUtub1hySExQZlFDMThWQlNxam9weTVlaTdJRzFXSVMxd0NYVXZ6bmhIRm1vUTF6QVlTenNEemtwN0xZUDhXY3hQZXpJLkV2OVIycW9uNDR6MnlmQjlibnVTTUdUOENrRnBQOGlBcFV5VUt0YTYwYzFOUnI3NFFzbUpfd0tfZkVNOUVQcWZZUGVwQngxellFZ0pkNGlFdDRsMExKeXBOVjJpZ28xbmhxdkg1cHJ2LmdSZ19nSnU5RUdRa1NHTlVIdS51Z2FJalo0RjgtNGFQam9GRFhRcmhKZkhNY0YxbjJtMGFpSnMtQVdJdWRFU29YZWd6ZngxZkdIY0lJZ25QcTdSUGNIWTZ3MVlUNXZFLUFMRHgxdlJEOE16dXYxUS5mN25JLUNVR0xpSGtDemw4bU1HQkp1ZmJPbm9jV2NLaVc4VWNURDdaWmNNSVZnaVpjMyJ9|fb85b43fbcf0cc59411891c15c509e596c550716d89d2eace5d652c4f380340d; z_c0=2|1:0|10:1665380459|4:z_c0|92:Mi4xemU5Tk1RQUFBQUFBY0pfRmR3SkhGU1lBQUFCZ0FsVk5hXzR3WkFEVE8zbHFfZm90MEY4Tzhxc3JwNDVodFdnRkJR|ae1d4b68bd4016632b8c0e8cb46869224034429c152ab8991203180bdbd0c896; captcha_session_v2=2|1:0|10:1665380460|18:captcha_session_v2|88:WU40K3dVeWFYODlKbWZYU1YxbHZSRDQ4TXdCOGN3RkZta2U2TWY2cnlyS3o4enVtck0xZ0pxTjRDdnh1ZWg4cQ==|f6cdb6954c7d4b0140d021284144dd901fed3b69e21607ffacbc4adca2cf5ed6; q_c1=e0a29a67871a4ec588eba3387d49550a|1665380459000|1665380459000; NOT_UNREGISTER_WAITING=1; SESSIONID=aDyGRUvToIzfRLDWQvnopVQmn8g2WAi4pvMu8DXmjKC; JOID=VloQB01WKwh8p-BUcVaPlpve4hdlMWhwOfezJSQ7ZHUb964WIU-cSB-j4VJysJLrK1urWAZ9-s6E2RwGit8niac=; osd=VVoQBUtVKwh-oeNUcVSJlZve4BFmMWhyP_SzJSY9Z3Ub9agVIU-eThyj4VB0s5LrKV2oWAZ__M2E2R4Aid8ni6E=; KLBRSID=d6f775bb0765885473b0cba3a5fa9c12|1665380662|1665380148; Hm_lpvt_98beee57fd2ef70ccdd5ca52b9740c49=1665380667; tst=r'
    }    # 请求头
    resp = requests.get(url=url, headers=headers)    # 发起请求
    text = resp.text   # 获得数据
    resp.close()   # 关闭请求
    return text    # 返回数据


def main():
    # url = "https://www.zhihu.com/api/v3/moments?limit=10&desktop=true"
    url = "https://www.zhihu.com/question/20364508"
    text = get_text(url)

    questions = re.findall('class="internal">(.*?)', text)

    # 删除列表元素的方式一:通过中间列表得到
    # que = []
    # for item in questions:
    #     if item not in que and "??" not in item:
    #         que.append(item)
    # for item in que:
    #     print(item)
    # del questions    # 删除整个列表

    # 删除列表的方式二:通过从后往前取下标
    for i in range(len(questions) - 1, -1, -1):
        if "??" in questions[i]:
            questions.remove(questions[i])

    # # 保存文本:将不同的问题保存在不同文件中,放在questions文件夹中
    # if not os.path.exists("./questions"):
    #     os.mkdir("./questions")
    # for item in questions:
    #     # 将获取出来的内容保存在文本文件中
    #     with open(f"./questions/{item[:5]}....txt", "w", encoding="utf-8") as f:
    #         f.write(item)
    with open("questions.txt", "w", encoding="utf-8") as f:
        for item in questions:
            f.write(item + "\n")


if __name__ == "__main__":
    main()

运行结果:
七、PyQt5文件及文件夹操作(高级应用篇)_第37张图片
以下为参考:
七、PyQt5文件及文件夹操作(高级应用篇)_第38张图片

问题2:解决删除指定列表元素
      在上面代码去掉部分违规数据的时候,使用for-in循环无法删除列表中的指定元素。最终通过网上查找,找到了两种方法,第一种方法是通过中间列表过滤元素,第二种方法是通过for-in-range循环倒序遍历列表进行删除(防止下标越界)。       参考文章:[https://blog.csdn.net/weixin_41666747/article/details/125213619](https://blog.csdn.net/weixin_41666747/article/details/125213619)
问题3:简单正则表达式的使用方法
      参考文章:[https://blog.csdn.net/Jakey_src/article/details/122592295](https://blog.csdn.net/Jakey_src/article/details/122592295)
2. 编写PyQt代码
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import QFileDialog
from PyQt5.QtCore import QFile, QFileInfo, QIODevice,QTextStream
import os
import datetime


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(355, 293)
        MainWindow.setWindowTitle("将现有问题存放到不同的文件中")
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        #  分组框
        self.groupBox = QtWidgets.QGroupBox(self.centralwidget)
        self.groupBox.setGeometry(QtCore.QRect(10, 10, 331, 91))
        self.groupBox.setStyleSheet("color: reg(0, 0, 255);")
        self.groupBox.setObjectName("groupBox")
        # 选择文件标签
        self.label = QtWidgets.QLabel(self.groupBox)
        # self.label.setGeometry(QtCore.QRect(20, 20, 61, 16))
        self.label.setGeometry(QtCore.QRect(12, 20, 61, 16))
        self.label.setStyleSheet("color: rgb(0, 0, 0);")     # 设置字体为黑色
        self.label.setObjectName("label")
        # 显示选择的文件路径
        self.lineEdit = QtWidgets.QLineEdit(self.groupBox)
        # self.lineEdit.setGeometry(QtCore.QRect(80, 20, 171, 20))
        self.lineEdit.setGeometry(QtCore.QRect(75, 20, 175, 20))
        self.lineEdit.setObjectName("lineEdit")
        # “选择”按钮
        self.pushButton = QtWidgets.QPushButton(self.groupBox)
        self.pushButton.setGeometry(QtCore.QRect(260, 20, 61, 23))
        self.pushButton.setStyleSheet("color: rgb(0, 0, 0);")
        self.pushButton.setObjectName("pushButton")
        # 输入创建路径标签
        self.label_2 = QtWidgets.QLabel(self.groupBox)
        # self.label_2.setGeometry(QtCore.QRect(19, 52, 81, 16))
        self.label_2.setGeometry(QtCore.QRect(12, 52, 89, 16))
        self.label_2.setStyleSheet("color: rgb(0, 0, 0);")
        self.label_2.setObjectName("label_2")
        # 输入创建路径的文本框
        self.lineEdit_2 = QtWidgets.QLineEdit(self.groupBox)
        # self.lineEdit_2.setGeometry(QtCore.QRect(109, 52, 141, 20))
        self.lineEdit_2.setGeometry(QtCore.QRect(105, 52, 145, 20))
        self.lineEdit_2.setObjectName("lineEdit_2")
        # 创建“按钮”
        self.pushButton_2 = QtWidgets.QPushButton(self.groupBox)
        self.pushButton_2.setGeometry(QtCore.QRect(259, 52, 61, 23))
        self.pushButton_2.setObjectName("pushButton_2")
        # 显示创建的文件列表及大小
        self.tableWidget = QtWidgets.QTableWidget(self.centralwidget)
        self.tableWidget.setGeometry(QtCore.QRect(10, 105, 331, 181))
        self.tableWidget.setObjectName("tableWidget")
        self.tableWidget.setColumnCount(2)
        # 设置第一列的标题
        item = QtWidgets.QTableWidgetItem()
        self.tableWidget.setHorizontalHeaderItem(0, item)
        item = self.tableWidget.horizontalHeaderItem(0)
        item.setText("文件名")
        # 设置第二列标题
        item = QtWidgets.QTableWidgetItem()
        self.tableWidget.setHorizontalHeaderItem(1, item)
        item = self.tableWidget.horizontalHeaderItem(1)
        item.setText("文件大小")
        self.tableWidget.setColumnWidth(0, 100)    # 设置第一列宽度
        # 设置最后一列自动填充容器
        self.tableWidget.horizontalHeader().setStretchLastSection(True)
        # 设置分组框、标签及按钮的文本
        self.groupBox.setTitle("基础设置")
        self.label.setText("选择文件:")
        self.label_2.setText("输入创建路径:")
        self.pushButton.setText("选择")
        self.pushButton_2.setText("创建")
        MainWindow.setCentralWidget(self.centralwidget)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)
        # 为“选择”按钮的clicked信号绑定槽函数
        self.pushButton.clicked.connect(self.getfile)
        # 为“创建”按钮的clicked信号绑定槽函数
        self.pushButton_2.clicked.connect(self.getpath)

    # 选择文件并显示在文本框中
    def getfile(self):
        dir = QFileDialog()      # 创建文件对话框
        # dir.setDirectory("D:\\")   # 设置初始路径为D盘
        dir.setDirectory(".\\")   # 设置初始路径为D盘
        # 设置只显示文本文件
        dir.setNameFilter("文本文件(*.txt)")
        if dir.exec_():    # 判断是否选择了文件
            self.lineEdit.setText(dir.selectedFiles()[0])     # 将选择的文件显示在文本框中

    # 选择路径,根据日期创建文件,并写入选择的文件中的文本
    def getpath(self):
        try:
            path = self.lineEdit_2.text()    # 记录创建路径
            if self.lineEdit_2.text() != "":    # 判断路径不为空
                list = []     # 定义列表,用来按行记录选择的文件中的文本
                file = QFile(self.lineEdit.text())    # 创建QFile文件对象
                if file.open(QIODevice.ReadOnly):     # 以只读方式打开文件
                    read = QTextStream(file)     # 创建文本流
                    read.setCodec("utf-8")    # 设置写入编码
                    while not read.atEnd():     # 如果未读取完
                        list.append(read.readLine())    # 按行记录遍历到的文本
                # 判断要创建的文件的路径是否存在,没有则创建文件夹
                if not os.path.exists(path):
                    os.makedirs(path)    # 创建文件夹
                for i in range(len(list)):     # 遍历已经记录的文本数据列表
                    # 获取当前时间,用来作为文件名
                    mytime = str(datetime.datetime.utcnow().strftime("%Y%m%d%H%M%S"))
                    # 在指定路径下创建txt文本文件
                    files = path + mytime + str(i) + ".txt"
                    file = QFile(files)      # 创建QFile文件对象
                    # 以读写和文本模式打开文件
                    file.open(QIODevice.ReadWrite | QIODevice.Text)
                    file.write(bytes(list[i], encoding="utf8"))     # 向文件中写入数据
                    file.close()    # 关闭文件
                filelist = os.listdir(path)     # 遍历文件夹
                flag = 0    # 定义标识,用来指定在表格中的哪行插入数据
                for f in filelist:     # 遍历文件列表
                    file = QFileInfo(f)         # 创建对象,用来获取文件信息
                    if file.fileName().endswith(".txt"):    # 判断是否为.txt文件
                        self.tableWidget.insertRow(flag)     # 添加新行
                        # 设置第一列的值为文件名
                        self.tableWidget.setItem(flag, 0, QtWidgets.QTableWidgetItem(file.fileName()))
                        # 设置第二列的值为文件大小
                        self.tableWidget.setItem(flag, 1, QtWidgets.QTableWidgetItem(str(file.size()) + "B"))
                        flag += 1     # 标识加1
        except Exception as e:
            print(e)


if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    MainWindow = QtWidgets.QMainWindow()
    ui = Ui_MainWindow()
    ui.setupUi(MainWindow)
    MainWindow.show()
    sys.exit(app.exec_())

      运行程序,单击“选择”按钮,选择存储知乎问题的文本文件,在下面的文本框中输入创建文件的路径,单击“创建”按钮,即可根据选择的we文本文件中的行数创建相应个数的文本文件,并且每一行的内容写入对应的文本文件中。将会在下面的表格中显示选中文件夹下的所有子文件夹及文件。
七、PyQt5文件及文件夹操作(高级应用篇)_第39张图片
七、PyQt5文件及文件夹操作(高级应用篇)_第40张图片
保存格式二:(以前5个字符+“…”为文件名)

from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import QFileDialog
from PyQt5.QtCore import QFile, QFileInfo, QIODevice,QTextStream
import os
import datetime


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(355, 293)
        MainWindow.setWindowTitle("将现有问题存放到不同的文件中")
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        #  分组框
        self.groupBox = QtWidgets.QGroupBox(self.centralwidget)
        self.groupBox.setGeometry(QtCore.QRect(10, 10, 331, 91))
        self.groupBox.setStyleSheet("color: reg(0, 0, 255);")
        self.groupBox.setObjectName("groupBox")
        # 选择文件标签
        self.label = QtWidgets.QLabel(self.groupBox)
        # self.label.setGeometry(QtCore.QRect(20, 20, 61, 16))
        self.label.setGeometry(QtCore.QRect(12, 20, 61, 16))
        self.label.setStyleSheet("color: rgb(0, 0, 0);")     # 设置字体为黑色
        self.label.setObjectName("label")
        # 显示选择的文件路径
        self.lineEdit = QtWidgets.QLineEdit(self.groupBox)
        # self.lineEdit.setGeometry(QtCore.QRect(80, 20, 171, 20))
        self.lineEdit.setGeometry(QtCore.QRect(75, 20, 175, 20))
        self.lineEdit.setObjectName("lineEdit")
        # “选择”按钮
        self.pushButton = QtWidgets.QPushButton(self.groupBox)
        self.pushButton.setGeometry(QtCore.QRect(260, 20, 61, 23))
        self.pushButton.setStyleSheet("color: rgb(0, 0, 0);")
        self.pushButton.setObjectName("pushButton")
        # 输入创建路径标签
        self.label_2 = QtWidgets.QLabel(self.groupBox)
        # self.label_2.setGeometry(QtCore.QRect(19, 52, 81, 16))
        self.label_2.setGeometry(QtCore.QRect(12, 52, 89, 16))
        self.label_2.setStyleSheet("color: rgb(0, 0, 0);")
        self.label_2.setObjectName("label_2")
        # 输入创建路径的文本框
        self.lineEdit_2 = QtWidgets.QLineEdit(self.groupBox)
        # self.lineEdit_2.setGeometry(QtCore.QRect(109, 52, 141, 20))
        self.lineEdit_2.setGeometry(QtCore.QRect(105, 52, 145, 20))
        self.lineEdit_2.setObjectName("lineEdit_2")
        # 创建“按钮”
        self.pushButton_2 = QtWidgets.QPushButton(self.groupBox)
        self.pushButton_2.setGeometry(QtCore.QRect(259, 52, 61, 23))
        self.pushButton_2.setObjectName("pushButton_2")
        # 显示创建的文件列表及大小
        self.tableWidget = QtWidgets.QTableWidget(self.centralwidget)
        self.tableWidget.setGeometry(QtCore.QRect(10, 105, 331, 181))
        self.tableWidget.setObjectName("tableWidget")
        self.tableWidget.setColumnCount(2)
        # 设置第一列的标题
        item = QtWidgets.QTableWidgetItem()
        self.tableWidget.setHorizontalHeaderItem(0, item)
        item = self.tableWidget.horizontalHeaderItem(0)
        item.setText("文件名")
        # 设置第二列标题
        item = QtWidgets.QTableWidgetItem()
        self.tableWidget.setHorizontalHeaderItem(1, item)
        item = self.tableWidget.horizontalHeaderItem(1)
        item.setText("文件大小")
        self.tableWidget.setColumnWidth(0, 100)    # 设置第一列宽度
        # 设置最后一列自动填充容器
        self.tableWidget.horizontalHeader().setStretchLastSection(True)
        # 设置分组框、标签及按钮的文本
        self.groupBox.setTitle("基础设置")
        self.label.setText("选择文件:")
        self.label_2.setText("输入创建路径:")
        self.pushButton.setText("选择")
        self.pushButton_2.setText("创建")
        MainWindow.setCentralWidget(self.centralwidget)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)
        # 为“选择”按钮的clicked信号绑定槽函数
        self.pushButton.clicked.connect(self.getfile)
        # 为“创建”按钮的clicked信号绑定槽函数
        self.pushButton_2.clicked.connect(self.getpath)

    # 选择文件并显示在文本框中
    def getfile(self):
        dir = QFileDialog()      # 创建文件对话框
        # dir.setDirectory("D:\\")   # 设置初始路径为D盘
        dir.setDirectory(".\\")   # 设置初始路径为D盘
        # 设置只显示文本文件
        dir.setNameFilter("文本文件(*.txt)")
        if dir.exec_():    # 判断是否选择了文件
            self.lineEdit.setText(dir.selectedFiles()[0])     # 将选择的文件显示在文本框中

    # 选择路径,根据日期创建文件,并写入选择的文件中的文本
    def getpath(self):
        try:
            path = self.lineEdit_2.text()    # 记录创建路径
            if self.lineEdit_2.text() != "":    # 判断路径不为空
                list = []     # 定义列表,用来按行记录选择的文件中的文本
                file = QFile(self.lineEdit.text())    # 创建QFile文件对象
                if file.open(QIODevice.ReadOnly):     # 以只读方式打开文件
                    read = QTextStream(file)     # 创建文本流
                    read.setCodec("utf-8")    # 设置写入编码
                    while not read.atEnd():     # 如果未读取完
                        list.append(read.readLine())    # 按行记录遍历到的文本
                # 判断要创建的文件的路径是否存在,没有则创建文件夹
                if not os.path.exists(path):
                    os.makedirs(path)    # 创建文件夹
                for i in range(len(list)):     # 遍历已经记录的文本数据列表
                    # 获取当前时间,用来作为文件名
                    # mytime = str(datetime.datetime.utcnow().strftime("%Y%m%d%H%M%S"))
                    mytime = list[i][:5] + "..."
                    # 在指定路径下创建txt文本文件
                    # files = path + mytime + str(i) + ".txt"
                    files = path + str(i + 1) + ". " + mytime + ".txt"
                    file = QFile(files)      # 创建QFile文件对象
                    # 以读写和文本模式打开文件
                    file.open(QIODevice.ReadWrite | QIODevice.Text)
                    file.write(bytes(list[i], encoding="utf8"))     # 向文件中写入数据
                    file.close()    # 关闭文件
                filelist = os.listdir(path)     # 遍历文件夹
                flag = 0    # 定义标识,用来指定在表格中的哪行插入数据
                for f in filelist:     # 遍历文件列表
                    file = QFileInfo(f)         # 创建对象,用来获取文件信息
                    if file.fileName().endswith(".txt"):    # 判断是否为.txt文件
                        self.tableWidget.insertRow(flag)     # 添加新行
                        # 设置第一列的值为文件名
                        self.tableWidget.setItem(flag, 0, QtWidgets.QTableWidgetItem(file.fileName()))
                        # 设置第二列的值为文件大小
                        self.tableWidget.setItem(flag, 1, QtWidgets.QTableWidgetItem(str(file.size()) + "B"))
                        flag += 1     # 标识加1
        except Exception as e:
            print(e)


if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    MainWindow = QtWidgets.QMainWindow()
    ui = Ui_MainWindow()
    ui.setupUi(MainWindow)
    MainWindow.show()
    sys.exit(app.exec_())


运行效果:
七、PyQt5文件及文件夹操作(高级应用篇)_第41张图片
七、PyQt5文件及文件夹操作(高级应用篇)_第42张图片

2. 使用QDir类操作文件夹

      QDir类提供对文件夹结构及其内容的访问,使用它可以对文件夹进行创建、重命名、删除、遍历等操作。

QDir类的常用方法及说明

方法 说明
mkdir() 创建文件夹。
exists() 判断文件夹是否存在。
rename() 重命名文件夹。
rmdir() 删除文件夹。
entryList() 遍历文件夹,获取文件夹中所有子文件夹和文件的名称列表。
entryInfoList() 遍历文件夹,获取文件中所有子文件夹和文件的QFileInfo对象的列表。
count() 获取文件夹和文件的数量。
setSorting()
  • 设置entryList()和entryInfoList()使用的排序顺序,取值如下:
  • QDir.Name:按名称排序;
  • QDir.Time:按修改时间排序;
  • QDir.Size:按文件大小排序;
  • QDir.Type:按文件类型排序;
  • QDir.Unsorted:不排序;
  • QDir.DirsFirst:先显示文件夹,然后显示文件;
  • QDir.DirsLast:先显示文件,然后显示文件夹;
  • QDir.Reversed:反向排序;
  • QDir.IgnoreCase:不区分大小写排序;
  • QDir.DefaultSort:默认排序。
path() 获取QDir()对象所关联的文件夹路径。
absolutePath() 获取文件夹的绝对路径。
isAbsolute() 判断是否为绝对路径。
isRelative() 判断是否为相对路径。
isReadable() 判断文件夹是否可读,并且是否能够通过名称打开。
isRoot() 判断是否为根目录。
cd() 改变QDir的路径为dirName。
setFilter()
  • 设置过滤器,以决定entryList()entryInfoList()返回哪些文件,取值如下:
  • QDir.Dirs:按照过滤方式列出所有文件夹;
  • QDir.AllDirs:不考虑过滤方式,列出所有文件夹;
  • QDir.Files:只列出所有文件;
  • QDir.Drives:只列出磁盘(UNIX系统无效);
  • QDir.NoSymLinks:不列出符号连接;
  • QDir.NoDotAndDotDot:不列出“.”和“..”;
  • QDir.AllEntries:列出所有文件夹、w文件和磁盘;
  • QDir.Readble:列出所有具有可读属性的文件夹和文件;
  • QDir.Writable:列出所有具有可写属性的文件夹和文件;
  • QDir.Executable:列出所有具有可执行属性的文件夹和文件;
  • QDir.Modified:列出被修改过的文件夹和文件(UNIX系统无效);
  • QDir.Hidden:列出隐藏的文件夹和文件;
  • Qdir.System:列出系统文件夹和文件;
  • QDir.CaseSensitive:文件系统如果区分文件名大小写,则按大小写方式进行过滤。

示例:使用QDir遍历、重命名和删除文件夹

      在Qt Designer设计器中创建一个窗口,在其中添加两个LineEdit控件,分别用来输入路径和要重命名的文件夹名;添加3个PushButton控件,分别用来执行文件夹的遍历、重命名和删除操作;添加一个TableWidget控件,用来显示指定路径下的所有子文件夹。窗口设计完成后保存为.ui文件,并使用PyUIC工具将其转换为.py文件。
      在.py文件中,定义4个槽函数:getItem()、getpath()、rename()和delete(),其中,getItem()用来获取表格中选中的重命名或者删除的文件夹;getpath()用来获取指定路径下的子文件夹,并显示在表格中,如果输入额路径不存在,则自动创建;rename()用来对指定文件夹进行重命名;delete()用来删除指定文件夹。
完整代码如下:

from PyQt5 import QtCore, QtWidgets
from PyQt5.QtCore import QDir
import os


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(390, 252)
        MainWindow.setWindowTitle("QDir应用")   # 设置标题
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        # 输入路径标签
        self.label = QtWidgets.QLabel(self.centralwidget)
        self.label.setGeometry(QtCore.QRect(24, 10, 61, 16))
        self.label.setObjectName("label")
        self.label.setText("输入路径:")
        # 输入路径的文本框
        self.lineEdit = QtWidgets.QLineEdit(self.centralwidget)
        self.lineEdit.setGeometry(QtCore.QRect(84, 10, 211, 20))
        self.lineEdit.setObjectName("lineEdit")
        # 确定按钮,判断输入路径是否存在,如果不存在,则创建
        # 如果存在,获取其中的所有文件夹
        self.pushButton = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton.setGeometry(QtCore.QRect(304, 10, 61, 23))
        self.pushButton.setObjectName("pushButton")
        self.pushButton.setText("确定")
        # 表格,显示指定路径下的所有文件夹
        self.tableWidget = QtWidgets.QTableWidget(self.centralwidget)
        self.tableWidget.setGeometry(QtCore.QRect(14, 40, 351, 171))
        self.tableWidget.setObjectName("tableWidget")
        self.tableWidget.setColumnCount(1)
        item = QtWidgets.QTableWidgetItem()
        self.tableWidget.setHorizontalHeaderItem(0, item)
        item = self.tableWidget.horizontalHeaderItem(0)
        item.setText("路径")
        self.tableWidget.verticalHeader().setVisible(False)   # 隐藏垂直标题栏
        # 设置自动填充器
        self.tableWidget.horizontalHeader().setSectionResizeMode(QtWidgets.QHeaderView.Stretch)
        # 设置新文件夹名称标签
        self.lable_2 = QtWidgets.QLabel(self.centralwidget)
        self.lable_2.setGeometry(QtCore.QRect(4, 220, 125, 21))
        self.lable_2.setObjectName("label_2")
        self.lable_2.setText("设置新文件夹名称:")
        # 输入新文件夹名称
        self.lineEdit_2 = QtWidgets.QLineEdit(self.centralwidget)
        self.lineEdit_2.setGeometry(QtCore.QRect(130, 220, 81, 20))
        self.lineEdit_2.setObjectName("lineEdit_2")
        # 重命名按钮
        self.pushButton_2 = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton_2.setGeometry(QtCore.QRect(220, 220, 61, 23))
        self.pushButton_2.setObjectName("pushButton_2")
        self.pushButton_2.setText("重命名")
        # 删除按钮
        self.pushButton_3 = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton_3.setGeometry(QtCore.QRect(300, 220, 61, 23))
        self.pushButton_3.setObjectName("pushButton_3")
        self.pushButton_3.setText("删除")
        MainWindow.setCentralWidget(self.centralwidget)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)
        # 给控件绑定自定义槽函数:
        self.pushButton.clicked.connect(self.getpath)
        self.pushButton_2.clicked.connect(self.rename)
        self.pushButton_3.clicked.connect(self.delete)
        self.tableWidget.itemClicked.connect(self.getItem)

    # 定义 自动以槽函数
    def getItem(self, item):    # 获取选中的表格内容
        self.select = item.text()

    # 获取指定路径下的所有文件夹
    def getpath(self):
        self.tableWidget.setRowCount(0)    # 清空表格中的所有行
        path = self.lineEdit.text()     # 记录输入的路径
        if path != "":     # 判断路径不为空
            dir = QDir()    # 创建QDir()对象
            if not dir.exists(path):     # 判断路径是否存在
                dir.mkdir(path)     # 如果路径不存在,就创建文件夹
            dir = QDir(path)   # 创建QDir对象
            flag = 0    # 定义标识,用来指定在表格中的哪行插入数据
            # 遍历指定路径下的所有子文件夹
            for d in dir.entryList(QDir.Dirs | QDir.NoDotAndDotDot):
                self.tableWidget.insertRow(flag)    # 添加新行
                # 设置第一列的值为文件夹全路径(包括文件夹名)
                self.tableWidget.setItem(flag, 0, QtWidgets.QTableWidgetItem(os.path.join(path, d)))
                flag += 1    # 标识加1

    # 重命名文件夹
    def rename(self):
        newname = self.lineEdit_2.text()   # 记录新文件夹名
        if newname != "":    # 判断新文件夹名是否为空
            if self.select != "":    # 判断是偶选择了要重命名的文件夹
                dir = QDir()   # 创建QDir对象
                # 对选中的文件夹进行重命名
                dir.rename(self.select, os.path.join(self.lineEdit.text(), newname))
                QtWidgets.QMessageBox.information(MainWindow, "提示", "重命名文件夹成功!")
                self.getpath()    # 更新表格

    # 删除文件夹
    def delete(self):
        if self.select != "":    # 判断是否选择了要删除的文件夹
            dir = QDir()   # 创建QDir对象
            dir.rmdir(self.select)    # 删除选中的文件夹
            QtWidgets.QMessageBox.information(MainWindow, "提示", "成功删除文件夹!")
            self.getpath()   # 更新表格


if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)

    global MainWindow
    MainWindow = QtWidgets.QMainWindow()

    ui = Ui_MainWindow()
    ui.setupUi(MainWindow)
    MainWindow.show()
    sys.exit(app.exec_())

      运行程序,输入一个路径,单击“确定”按钮,自动遍历改路径下的所有子文件夹,并显示在下方的表格中,如果输入的路径不存在,则自动创建;而当用户选择表格中的某个文件夹后,可以通过单击“重命名”和“删除”按钮,对其进行相应操作。
没有文件夹就自动创建:七、PyQt5文件及文件夹操作(高级应用篇)_第43张图片
重命名文件夹:
七、PyQt5文件及文件夹操作(高级应用篇)_第44张图片
七、PyQt5文件及文件夹操作(高级应用篇)_第45张图片
删除文件夹:
七、PyQt5文件及文件夹操作(高级应用篇)_第46张图片
七、PyQt5文件及文件夹操作(高级应用篇)_第47张图片

你可能感兴趣的:(PyQt,python,开发语言)