七、PyQt5文件及文件夹操作(高级应用篇)
使用os模块、os.path模块和shutil模块操作文件和文件夹,使用PyQt5中的QFile类和QDir类对文件和文件夹操作。
三、高级应用
该部分包括文件及文件夹操作、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()方法打开一个不存在的文件,会抛出下列异常:
解决该错误的方法有两种:
- 在当前目录下(即与执行的文件相同的目录)创建一个名称为x.txt的文件。
- 在调用open()方法时,指定mode的参数值为w、w+、a、a+。这样,当要打开的文件不存在时,就可以创建新的文件了。
示例:打开文件,若文件不存在就创建
打开一个名为“message.txt”的文件,如果不存在,则创建。
file = open("message.txt", "w")
执行上面的代码,将在.py文件的同级目录下创建一个名称为message.txt的文件,该文件没有任何内容。
使用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(追加),否则,将抛出下列异常,表示没有写入权限:
示例:向文件中写入文本内容
使用open()方法以写方式打开一个文件(如果文件不存在,则自动创建),然后调用write()方法向该文件中写入一条信息,最后调用close()方法关闭文件。
file = open("message.txt", "w", encoding="utf-8") # 创建或打卡文件
file.write("故不积跬步,无以至千里;不积小流,无以成江海。\n") # 写入一条信息
file.close() # 关闭文件对象
运行程序,在.py文件所在的目录下创建一个名称为message.txt的文件,并且在该文件中写入了文字“故不积跬步,无以至千里;不积小流,无以成江海。“
注意,写入文件后,一定要调用close()方法关闭文件,否则写入的内容不会保存到文件中。这是因为在写入文件内容时,操作系统不会立刻把数据写入磁盘,而是先缓存起来只有调用close()方法时,操作系统才会保证没有写入的数据全部写入磁盘。
技巧
(1)向文件中写入内容时,如果打开文件采用w(写入)模式,则先清空原文件的内容,再写入新的内容;而如果打开文件采用a(追加)模式,则不覆盖原有文件的内容,知识在文件的结尾处增加新内容。
下面对上面示例的代码进行修改,实现在原内容的基础上再添加一条名言。代码如下:
file = open("message.txt", "a", encoding="utf-8") # 以追加方式打开文件
file.write("骐骥一跃,不能十步;驽马十驾,功在不舍;锲而舍之,朽木不折;锲而不舍,金石可镂。\n")
file.close() # 关闭文件对象
执行上面代码,打开message.txt文件,将显示如下效果:
(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()) # 读取全部数据
运行效果如下图:
版本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()) # 读取全部数据
运行效果如下:
使用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文件夹下多了一张图片)
7. 移动文件
在Python中移动文件需要使用shutil模块的move()方法。
shutil模块的move()方法的基本语法
shutil.move(src, dst)
# src:要移动的源文件。
# dst:移动到的目标文件。
示例:将项目的image文件夹中一张图片移动到image文件夹外
import shutil
shutil.move("./image/宝石之国人物1.png", "./帕德玛刚玉.png")
运行的效果如下:(原本image文件夹中的图片被移出来了)
原本的目录结构:
复制文件和移动文件的区别是,复制文件时,源文件还存在,而移动文件相当于将源文件剪切到另外一个路径,源文件不会存在。
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")
运行效果:
另外,也可以使用shutil模块的move()方法对文件进行重命名。
import shutil
shutil.move("text.txt", "message.txt")
在执行文件操作时,为了确保能够正常执行,可以使用os.pash模块的exists()方法判断要操作的文件是否存在。
示例:使用os.path,exists()方法判断文件是否存在
import os # 导入os模块
if os.path.exists("./message.txt"): # 判断文件是否存在
print("文件存在")
运行效果如下:
9. 删除文件
在Python中删除文件需要使用os模块的remove()方法。
os模块的remove()方法的基本语法
os.remove(path)
# path:表示要删除的文件路径,可以是相对路径,也可以是绝对路径。
示例:删除项目中的"帕德玛刚玉.png"文件
import os
os.remove("./帕德玛刚玉.png")
运行后的项目结构:(删除图片)
原本项目结构:
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_())
运行程序,单击“选择”按钮,选择一个文件后,即可在下面的文本框中显示其信息:
2. Python内置的文件夹操作
文件夹主要用于分层保存文件,通过文件夹可以分门别类地存放文件。在Python中,并没有提供直接操作文件夹的方法或者对象,而是需要使用内置的os、os.path和shutil模块实现。
1. 获取文件夹路径
用于定位一个文件或者文件夹的字符串被称为一个路径,在程序开发时,通常涉及两种路径,一种是相对路径,另一种是绝对路径。
1. 相对路径
在学习相对路径之前,需要先了解什么是当前工作文件。当前工作文件夹是指当前文件所在的文件夹。在Python中,可以通过os模块提供getcwd()方法获取当前工作文件夹。
示例:用os.getcwd()方法获取当前文件夹路径
import os
print(os.getcwd()) # 输出当前文件夹
执行上面代码,将显示以下文件夹,该路径就是当前工作文件夹。
相对路径是依赖于当前工作文件夹的,如果在当前工作文件夹下,有一个名称为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")) # 获取绝对路径
运行结果如下:
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")) # 拼接路径
运行结果:
在使用join()方法时,如果要拼接的路径中存在多个绝对路径,那么以从左到右最后一次出现的为准,并且该路径之前的参数都将被忽略。
例如:
import os
print(os.path.join("E:\\code", "E:\\python\\mr", "Code", "C:\\", "demo")) # 拼接路径
运行结果:
将两个路径拼接为一个路径时,不要直接使用字符串拼接,而是使用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:
3. 创建文件夹
在Python中,os模块提供了两个创建文件夹的方法,一个用于创建一级文件夹,另一个用于创建多级文件夹。
1. 创建一级文件夹
创建一级文件夹是指一次只能创建顶层文件夹,而不能创建子文件夹。在Python中,可以使用os模块提供的mkdir()方法实现。通过该方法只能创建指定路径中的最后一级文件夹,如果该文件夹的上一级不存在,则抛出FileNotFoundError异常。
os.mkdir()方法的基本语法
os.mkdir(path, mode=0o777)
# path:指定要创建的文件夹,可以使用绝对路径,也可以使用相对路径
# mode:指定数值模式,默认值为0o777,。该参数在非UNIX系统上无效或被忽略
示例:在项目中创建一个demo文件夹
import os
os.mkdir("./demo") # 创建demo文件夹
创建文件夹后的项目结构:
原本的项目结构:
如果创建的文件夹已经存在,将抛出FileExistsError异常,要避免该异常,可以先使用os.path.exists()方法判断要创建的文件夹是否存在。
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()会自动创建。
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")
移动后的目录结构:
原本的目录结构:
复制文件夹和移动文件夹的区别是,复制文件夹时,源文件还存在,而移动文件夹相当于将源文件夹剪切到另外一个路径,源文件不会存在。
6. 重命名文件夹
在Python中重命名文件夹需要使用os模块的rename()方法。
os.rename()方法的基本语法
os.rename(src, dst)
# src:指定要进行重命名的文件。
# dst:指定重命名后的文件。
示例:将项目文件夹test文件夹重命名为test_demo
import os
os.rename("./test", "test_demo")
另外,也可以使用shutil模块的move()方法对文件夹进行重命名。
import shutil
shutil.move("./test_demo", "./test")
7. 删除文件夹
删除文件夹可以使用os模块提供的rmdir()方法实现。通过rmdir()方法删除文件夹时,只有当删除的文件夹为空时才起作用。如果要删除的文件夹不为空,使用该方法会报错OSError。
os.rmdir()方法的基本语法
os.rmdir(path)
# path:为要删除的文件夹,可以使用相对路径或绝对路径。
示例:删除项目中的test文件夹中最内层的test3文件夹
import os
os.rmdir("./test/demo/test1/test2/test3")
删除之后的test文件夹结构:
原本的test文件夹结构:
使用rmdir()方法只能删除空文件夹,如果想删除非空文件夹,则需要使用Python内置的标准模块shutil的rmtree()方法实现。
示例:使用shutil.rmtree()方法删除非空文件夹test
import shutil
shutil.rmtree("./test") # 删除文件夹下的子文件夹及其包含的所有内容
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") # 输出每一级文件夹的元组
运行效果如下:
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_())
运行程序,单击“选择路径”按钮,选择一个文件夹后,将会在下面的表格中显示选中文件夹下的所有子文件夹及文件。
3. PyQt5中的文件及文件夹操作
在PyQt5中对文件和文件夹进行操作时,主要用到QFile类、QFileInfo类和QDir类。
在PyQt5窗口程序中对文件或者文件夹进行操作时,不强制必须用PyQt5中提供的QFile、QDir等类,使用Python内置的文件对象同样可以。
1. 使用QFile类操作文件
QFile类主要用来对文件进行打开、读写、复制、重命名、删除等操作。
QFile类的常用方法及说明
方法 | 说明 |
---|---|
open() |
|
isOpen() | 判断文件是否打开。 |
close() | 关闭文件。 |
copy() | 复制文件。 |
exists() | 判断文件是否存在。 |
read() | 从文件中读取指定个数的字符。 |
readAll() | 从文件中读取所有数据。 |
readLine() | 从文件中读取一行数据。 |
remove() | 删除文件。 |
rename() | 重命名文件。 |
seek() | 查找文件。 |
setFileName() | 设置文件名。 |
write() | 向文件中写入数据 |
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">(.*?)</a>', 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()
运行结果:
以下为参考:
问题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文本文件中的行数创建相应个数的文本文件,并且每一行的内容写入对应的文本文件中。将会在下面的表格中显示选中文件夹下的所有子文件夹及文件。
保存格式二:(以前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_())
运行效果:
2. 使用QDir类操作文件夹
QDir类提供对文件夹结构及其内容的访问,使用它可以对文件夹进行创建、重命名、删除、遍历等操作。
QDir类的常用方法及说明
方法 | 说明 |
---|---|
mkdir() | 创建文件夹。 |
exists() | 判断文件夹是否存在。 |
rename() | 重命名文件夹。 |
rmdir() | 删除文件夹。 |
entryList() | 遍历文件夹,获取文件夹中所有子文件夹和文件的名称列表。 |
entryInfoList() | 遍历文件夹,获取文件中所有子文件夹和文件的QFileInfo对象的列表。 |
count() | 获取文件夹和文件的数量。 |
setSorting() |
|
path() | 获取QDir()对象所关联的文件夹路径。 |
absolutePath() | 获取文件夹的绝对路径。 |
isAbsolute() | 判断是否为绝对路径。 |
isRelative() | 判断是否为相对路径。 |
isReadable() | 判断文件夹是否可读,并且是否能够通过名称打开。 |
isRoot() | 判断是否为根目录。 |
cd() | 改变QDir的路径为dirName。 |
setFilter() |
|
示例:使用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_())
运行程序,输入一个路径,单击“确定”按钮,自动遍历改路径下的所有子文件夹,并显示在下方的表格中,如果输入的路径不存在,则自动创建;而当用户选择表格中的某个文件夹后,可以通过单击“重命名”和“删除”按钮,对其进行相应操作。
没有文件夹就自动创建:
重命名文件夹:
删除文件夹:
更多推荐
所有评论(0)