文章目录
- 一.前言
- 二.展示
- 1.主界面
- 2.添加图片
- 3.多级目录
- 4.查看文件信息
- 5.调整UI布局
- 三.源代码
- 1.image_god_main_v.py
- 2.image_god_GUI.py
- 四.总结
一.前言
本次使用PyQt5开发一款图片浏览器,本篇主要练习QDockWidget、QTreeWidget,软件打包好放在了文末,可自取。
二.展示
1.主界面
2.添加图片
点击“添加目录”后,选择目录,软件会扫描文件夹及子文件夹下的所有图片文件,最后展示到左侧的目录树中。
3.多级目录
支持多级目录图片展示
4.查看文件信息
右击文件,点击查看文件信息
5.调整UI布局
通过鼠标拖动QDockWidget,选择悬停位置改变UI布局。
三.源代码
1.image_god_main_v.py
文件主逻辑调用
import os
import re
import sys
import traceback
from PyQt5.QtCore import Qt, QFileInfo, QSize
from PyQt5.QtGui import QPixmap, QIcon, QCursor
import qtawesome
from image_god_GUI import Ui_MainWindow as mainWindow
from webbrowser import open as open_url
from PyQt5.QtWidgets import QApplication, QMainWindow, QDockWidget, QTreeWidgetItem, QFileDialog, QAbstractItemView, \
QMenu, QFileIconProvider, QMessageBox, QAction
class ImageGod(QMainWindow, mainWindow):
pixRatio = 1
def __init__(self):
super(ImageGod, self).__init__()
self.setupUi(self)
self.ui_init()
self.slot_init()
def ui_init(self):
def icon_init():
self.action_addDir.setIcon(qtawesome.icon("mdi.folder-open", color="#3D59AB"))
self.action_broom.setIcon(qtawesome.icon("ei.broom", color="#DDA0DD"))
self.action_zoomIn.setIcon(qtawesome.icon("msc.zoom-in", color="brown"))
self.action_zoomOut.setIcon(qtawesome.icon("msc.zoom-out", color="brown"))
self.action_zoomFit.setIcon(qtawesome.icon("mdi.restore", color="green"))
self.action_exit.setIcon(qtawesome.icon("mdi6.exit-to-app", color="gray"))
self.treeWidget.setColumnCount(1)
self.treeWidget.setColumnWidth(0, 30)
self.treeWidget.setHeaderLabels(["目录文件"])
self.treeWidget.setIconSize(QSize(25, 25))
self.treeWidget.setSelectionMode(QAbstractItemView.ExtendedSelection)
self.setCentralWidget(self.scrollArea)
self.scrollArea.setWidgetResizable(True)
self.action_broom.setEnabled(False)
self.scrollArea.setAlignment(Qt.AlignCenter)
self.label.setAlignment(Qt.AlignCenter)
self.currPixmap = QPixmap()
icon_init()
self.change_action_ui(False)
self.menu_init()
def slot_init(self):
self.action_addDir.triggered.connect(self.on_openDir_triggered)
self.action_exit.triggered.connect(self.close)
self.actionabout_author.triggered.connect(lambda: open_url("https://blog.csdn.net/a1397852386"))
self.actionabout_qt.triggered.connect(lambda: QMessageBox.aboutQt(self, "关于QT"))
self.action_zoomIn.triggered.connect(self.on_actionZoomIn_triggered)
self.action_zoomOut.triggered.connect(self.on_actionZoomOut_triggered)
self.action_zoomFit.triggered.connect(self.on_actionFid_triggered)
self.action_broom.triggered.connect(self.clear_dirTree)
self.actiontree.triggered.connect(lambda: self.dockWidget.setVisible(self.actiontree.isChecked()))
# self.dockWidget.closeEvent=self.actiontree.setChecked(not self.dockWidget.isVisible())
self.treeWidget.customContextMenuRequested.connect(self.onTreeWidgetCustomContextMenuRequested)
self.treeWidget.itemClicked.connect(self.onTreeItemClicked)
def menu_init(self):
"""
treeWidget右击菜单初始化
:return:
"""
self.treeWidget_menu = QMenu(self.treeWidget)
self.action_show_image = QAction(self) # 查看图片
self.action_show_infos = QAction(self) # 查看文件信息
self.action_open_image_folder = QAction(self) # 打开所在文件夹
self.action_show_image.setText("查看图片")
self.action_show_infos.setText("查看文件信息")
self.action_open_image_folder.setText("打开所在文件夹")
self.treeWidget_menu.addActions(
[self.action_show_image, self.action_show_infos, self.action_open_image_folder, ])
def on_openDir_triggered(self):
"""
打开有图片的目录
:return:
"""
def CreateTree(dirs, root, path):
"""
创建目录树
:param dirs:
:param root:
:param path:
:return:
"""
for i in dirs:
path_new = path + '\\' + i
if os.path.isdir(path_new):
fileInfo = QFileInfo(path_new)
child = QTreeWidgetItem(root)
dirs_new = os.listdir(path_new)
data = ""
if self.check_dir(path_new): # 确定文件夹有图片文件后,再创建子节点
CreateTree(dirs_new, child, path_new)
else:
fileInfo = QFileInfo(path_new)
if not self.check_file(fileInfo.suffix()): continue
child = QTreeWidgetItem(root)
data = fileInfo.absoluteFilePath()
# 添加其他队列
# child.setText(1, str(fileInfo.size()))
# child.setToolTip(1, str(fileInfo.size()))
# child.setText(2, fileInfo.suffix())
# child.setToolTip(2, fileInfo.suffix())
child.setData(0, Qt.UserRole, data)
fileIcon = QFileIconProvider()
icon = QIcon(fileIcon.icon(fileInfo))
child.setText(0, i)
child.setToolTip(0, i)
child.setIcon(0, QIcon(icon))
child.setExpanded(False)
path = QFileDialog.getExistingDirectory(self, "选取文件夹", "./")
# self.treeWidget.setHeaderLabels(["目录文件", "大小", "类型", "创建时间", "上次修改时间"])
if not path: return
dirs = os.listdir(path)
if self.check_dir(path, root=True): # 确定文件夹有图片文件后,再创建子节点
fileInfo = QFileInfo(path)
fileIcon = QFileIconProvider()
icon = QIcon(fileIcon.icon(fileInfo))
fileName_root = QTreeWidgetItem(self.treeWidget)
fileName_root.setText(0, fileInfo.fileName())
fileName_root.setToolTip(0, fileInfo.fileName())
fileName_root.setIcon(0, QIcon(icon))
fileName_root.setData(0, Qt.UserRole, "")
CreateTree(dirs, fileName_root, path)
self.action_broom.setEnabled(True)
QApplication.processEvents()
else:
QMessageBox.warning(self, "警告", "选择的目录不存在图片文件!")
def clear_dirTree(self):
"""
清空目录树
:return:
"""
self.treeWidget.clear()
self.action_broom.setEnabled(False)
self.label.setPixmap(QPixmap(""))
self.change_action_ui(False)
def onTreeItemClicked(self, item, column):
"""
treeWidget节点被单击
:return:
"""
file = item.data(column, Qt.UserRole)
self.show_image(file)
def show_image(self, file):
if file:
self.pixRatio = 1 # 每次都重置为1
self.statusbar.showMessage(file) # 将文件名展示到状态栏
self.currPixmap.load(file)
self.on_zoomFitH_triggered()
self.change_action_ui(True)
def show_file_infos(self, file):
"""
文件信息
:param file:
:return:
"""
infos = ""
try:
file_obj = QFileInfo(file)
file_name = file_obj.fileName()
file_size = file_obj.size()
file_suffix = file_obj.suffix()
file_last = file_obj.lastModified().toString()
file_create = file_obj.created().toString()
file_ow_id = file_obj.ownerId()
infos = "文件名:{}\n文件类型:{}\n文件大小:{}字节\n创建时间:{}\n上次修改时间:{}\nownerId:{}".format(file_name, file_suffix,
file_size,
file_create, file_last,
file_ow_id)
except:
traceback.print_exc()
if infos:
QMessageBox.information(self, "文件信息", infos)
else:
QMessageBox.warning(self, "警告", "文件未找到")
def onTreeWidgetCustomContextMenuRequested(self, pos):
"""
treeWidget被右击事件
:return:
"""
try:
item = self.treeWidget.currentItem()
index=self.treeWidget.indexFromItem(item,0).row()
print("index===",index)
column = self.treeWidget.currentColumn()
item1 = self.treeWidget.itemAt(pos)
file = item.data(column, Qt.UserRole)
if item != None and item1 != None and file != "":
try:
self.action_show_image.triggered.disconnect()
self.action_show_infos.triggered.disconnect()
self.action_open_image_folder.triggered.disconnect()
except:
traceback.print_exc()
print(item.text(column))
print(file)
self.action_show_image.triggered.connect(lambda: self.show_image(file))
self.action_show_infos.triggered.connect(lambda: self.show_file_infos(file))
self.action_open_image_folder.triggered.connect(lambda: os.startfile(os.path.dirname(file)))
self.treeWidget_menu.exec_(QCursor.pos())
except:
traceback.print_exc()
def on_zoomFitH_triggered(self):
"""
自动适应高度
:return:
"""
height = self.scrollArea.height()
readHeight = self.currPixmap.height()
self.pixRatio = float(height) / readHeight
pix = self.currPixmap.scaledToHeight(height - 30)
self.label.setPixmap(pix)
def on_zoomFitW_triggered(self):
"""
自动适应宽度
:return:
"""
width = self.scrollArea.width()
readHWidth = self.currPixmap.width()
self.pixRatio = float(width) / readHWidth
pix = self.currPixmap.scaledToWidth(width - 30)
self.label.setPixmap(pix)
def on_actionZoomIn_triggered(self):
"""
放大图片
:return:
"""
self.pixRatio = self.pixRatio * 1.2
width = self.pixRatio * self.currPixmap.width()
height = self.pixRatio * self.currPixmap.height()
pix = self.currPixmap.scaled(width, height)
self.label.setPixmap(pix)
def on_actionFid_triggered(self):
"""
自适应大小
:return:
"""
self.pixRatio = 1
self.label.setPixmap(self.currPixmap)
def on_actionZoomOut_triggered(self):
"""
放大图片
:return:
"""
pixRatio_tmp = self.pixRatio
self.pixRatio = self.pixRatio * 0.8
width = self.pixRatio * self.currPixmap.width()
height = self.pixRatio * self.currPixmap.height()
if width < self.scrollArea.width() and height < self.scrollArea.height(): # 避免缩小太小导致失真
self.pixRatio = pixRatio_tmp
return
pix = self.currPixmap.scaled(width, height)
self.label.setPixmap(pix)
def closeEvent(self, event):
reply = QMessageBox.question(self, '关闭', "确定要退出吗?", QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes)
if reply == QMessageBox.Yes:
event.accept()
else:
event.ignore()
def check_dir(self, dir, root=False):
"""
查看文件夹是否有图片文件
:param dir:
:return:
"""
for file in os.listdir(dir):
file_abs = dir + '\\' + file
if os.path.isdir(file_abs):
return True
if "." in file:
if self.check_file(file.split(".")[-1]):
return True
return False
def check_file(self, suffix):
"""
看文件是否为图片文件
:param suffix:
:return:
"""
if suffix in ['jpg', 'png', 'jpeg', 'psd', 'bmp', 'webp']:
return True
else:
return False
def change_action_ui(self, flag):
"""
改变按钮UI状态
:param falg:
:return:
"""
self.action_zoomIn.setEnabled(flag)
self.action_zoomOut.setEnabled(flag)
self.action_zoomFit.setEnabled(flag)
if __name__ == '__main__':
app = QApplication(sys.argv)
ui = ImageGod()
ui.show()
sys.exit(app.exec_())
2.image_god_GUI.py
UI文件,通过uic转化而来
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'image_god_GUI.ui'
#
# Created by: PyQt5 UI code generator 5.15.4
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(1107, 622)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.horizontalLayout = QtWidgets.QHBoxLayout(self.centralwidget)
self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
self.horizontalLayout.setSpacing(0)
self.horizontalLayout.setObjectName("horizontalLayout")
self.scrollArea = QtWidgets.QScrollArea(self.centralwidget)
self.scrollArea.setWidgetResizable(True)
self.scrollArea.setObjectName("scrollArea")
self.scrollAreaWidgetContents = QtWidgets.QWidget()
self.scrollAreaWidgetContents.setGeometry(QtCore.QRect(0, 0, 845, 553))
self.scrollAreaWidgetContents.setObjectName("scrollAreaWidgetContents")
self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.scrollAreaWidgetContents)
self.verticalLayout_2.setObjectName("verticalLayout_2")
self.label = QtWidgets.QLabel(self.scrollAreaWidgetContents)
self.label.setText("")
self.label.setScaledContents(True)
self.label.setObjectName("label")
self.verticalLayout_2.addWidget(self.label)
self.scrollArea.setWidget(self.scrollAreaWidgetContents)
self.horizontalLayout.addWidget(self.scrollArea)
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 1107, 23))
self.menubar.setObjectName("menubar")
self.menu = QtWidgets.QMenu(self.menubar)
self.menu.setObjectName("menu")
self.menu_2 = QtWidgets.QMenu(self.menubar)
self.menu_2.setObjectName("menu_2")
self.menu_3 = QtWidgets.QMenu(self.menubar)
self.menu_3.setObjectName("menu_3")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.dockWidget = QtWidgets.QDockWidget(MainWindow)
self.dockWidget.setFeatures(QtWidgets.QDockWidget.DockWidgetFloatable|QtWidgets.QDockWidget.DockWidgetMovable)
self.dockWidget.setObjectName("dockWidget")
self.dockWidgetContents = QtWidgets.QWidget()
self.dockWidgetContents.setObjectName("dockWidgetContents")
self.verticalLayout = QtWidgets.QVBoxLayout(self.dockWidgetContents)
self.verticalLayout.setContentsMargins(0, 0, 0, 0)
self.verticalLayout.setObjectName("verticalLayout")
self.treeWidget = QtWidgets.QTreeWidget(self.dockWidgetContents)
self.treeWidget.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
self.treeWidget.setObjectName("treeWidget")
self.treeWidget.headerItem().setText(0, "1")
self.verticalLayout.addWidget(self.treeWidget)
self.dockWidget.setWidget(self.dockWidgetContents)
MainWindow.addDockWidget(QtCore.Qt.DockWidgetArea(1), self.dockWidget)
self.toolBar = QtWidgets.QToolBar(MainWindow)
self.toolBar.setToolButtonStyle(QtCore.Qt.ToolButtonTextUnderIcon)
self.toolBar.setObjectName("toolBar")
MainWindow.addToolBar(QtCore.Qt.TopToolBarArea, self.toolBar)
self.action_addDir = QtWidgets.QAction(MainWindow)
self.action_addDir.setObjectName("action_addDir")
self.action_broom = QtWidgets.QAction(MainWindow)
self.action_broom.setObjectName("action_broom")
self.action_zoomIn = QtWidgets.QAction(MainWindow)
self.action_zoomIn.setObjectName("action_zoomIn")
self.action_zoomOut = QtWidgets.QAction(MainWindow)
self.action_zoomOut.setObjectName("action_zoomOut")
self.action_zoomFit = QtWidgets.QAction(MainWindow)
self.action_zoomFit.setObjectName("action_zoomFit")
self.action_exit = QtWidgets.QAction(MainWindow)
self.action_exit.setObjectName("action_exit")
self.actiontree = QtWidgets.QAction(MainWindow)
self.actiontree.setCheckable(True)
self.actiontree.setChecked(True)
self.actiontree.setObjectName("actiontree")
self.actionabout_author = QtWidgets.QAction(MainWindow)
self.actionabout_author.setObjectName("actionabout_author")
self.actionabout_qt = QtWidgets.QAction(MainWindow)
self.actionabout_qt.setObjectName("actionabout_qt")
self.menu.addAction(self.action_addDir)
self.menu.addAction(self.action_broom)
self.menu.addAction(self.actiontree)
self.menu.addSeparator()
self.menu.addAction(self.action_exit)
self.menu_2.addAction(self.action_zoomIn)
self.menu_2.addAction(self.action_zoomOut)
self.menu_2.addAction(self.action_zoomFit)
self.menu_3.addAction(self.actionabout_author)
self.menu_3.addAction(self.actionabout_qt)
self.menubar.addAction(self.menu.menuAction())
self.menubar.addAction(self.menu_2.menuAction())
self.menubar.addAction(self.menu_3.menuAction())
self.toolBar.addAction(self.action_addDir)
self.toolBar.addAction(self.action_broom)
self.toolBar.addSeparator()
self.toolBar.addAction(self.action_zoomIn)
self.toolBar.addAction(self.action_zoomOut)
self.toolBar.addAction(self.action_zoomFit)
self.toolBar.addSeparator()
self.toolBar.addAction(self.action_exit)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "Image_god"))
self.menu.setTitle(_translate("MainWindow", "目录"))
self.menu_2.setTitle(_translate("MainWindow", "视图"))
self.menu_3.setTitle(_translate("MainWindow", "关于"))
self.dockWidget.setWindowTitle(_translate("MainWindow", "目录树"))
self.toolBar.setWindowTitle(_translate("MainWindow", "toolBar"))
self.action_addDir.setText(_translate("MainWindow", "添加目录"))
self.action_addDir.setToolTip(_translate("MainWindow", "添加目录"))
self.action_broom.setText(_translate("MainWindow", "清空目录树"))
self.action_broom.setToolTip(_translate("MainWindow", "清空目录树"))
self.action_zoomIn.setText(_translate("MainWindow", "放大"))
self.action_zoomIn.setToolTip(_translate("MainWindow", "放大"))
self.action_zoomOut.setText(_translate("MainWindow", "缩小"))
self.action_zoomOut.setToolTip(_translate("MainWindow", "缩小"))
self.action_zoomFit.setText(_translate("MainWindow", "原始大小"))
self.action_zoomFit.setToolTip(_translate("MainWindow", "原始大小"))
self.action_exit.setText(_translate("MainWindow", "退出"))
self.action_exit.setToolTip(_translate("MainWindow", "退出"))
self.actiontree.setText(_translate("MainWindow", "显示目录树"))
self.actiontree.setToolTip(_translate("MainWindow", "显示目录树"))
self.actionabout_author.setText(_translate("MainWindow", "关于作者"))
self.actionabout_author.setToolTip(_translate("MainWindow", "关于作者"))
self.actionabout_qt.setText(_translate("MainWindow", "关于QT"))
self.actionabout_qt.setToolTip(_translate("MainWindow", "关于QT"))
四.总结
本次练习了PyQt5相关组件使用,制作了一款图片浏览器,可以按照目录级别预览图片,支持放大、缩小、查看信息,实现功能比较基础,整体UI风格为PyQt5原生ui风格,未使用QSS做具体细节绘制。软件打包好放在了这里,解压后大小为37.5M。希望得到大家的赞,谢谢大家!