文章目录
一.前言
本次使用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 importopenas open_url
from PyQt5.QtWidgets import QApplication, QMainWindow, QDockWidget, QTreeWidgetItem, QFileDialog, QAbstractItemView, \
QMenu, QFileIconProvider, QMessageBox, QAction
classImageGod(QMainWindow, mainWindow):
pixRatio =1def__init__(self):super(ImageGod, self).__init__()
self.setupUi(self)
self.ui_init()
self.slot_init()defui_init(self):deficon_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()defslot_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)defmenu_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,])defon_openDir_triggered(self):"""
打开有图片的目录
:return:
"""defCreateTree(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)ifnot 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(["目录文件", "大小", "类型", "创建时间", "上次修改时间"])ifnot 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,"警告","选择的目录不存在图片文件!")defclear_dirTree(self):"""
清空目录树
:return:
"""
self.treeWidget.clear()
self.action_broom.setEnabled(False)
self.label.setPixmap(QPixmap(""))
self.change_action_ui(False)defonTreeItemClicked(self, item, column):"""
treeWidget节点被单击
:return:
"""file= item.data(column, Qt.UserRole)
self.show_image(file)defshow_image(self,file):iffile:
self.pixRatio =1# 每次都重置为1
self.statusbar.showMessage(file)# 将文件名展示到状态栏
self.currPixmap.load(file)
self.on_zoomFitH_triggered()
self.change_action_ui(True)defshow_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,"警告","文件未找到")defonTreeWidgetCustomContextMenuRequested(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 !=Noneand item1 !=Noneandfile!="":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()defon_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)defon_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)defon_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)defon_actionFid_triggered(self):"""
自适应大小
:return:
"""
self.pixRatio =1
self.label.setPixmap(self.currPixmap)defon_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)defcloseEvent(self, event):
reply = QMessageBox.question(self,'关闭',"确定要退出吗?", QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes)if reply == QMessageBox.Yes:
event.accept()else:
event.ignore()defcheck_dir(self,dir, root=False):"""
查看文件夹是否有图片文件
:param dir:
:return:
"""forfilein os.listdir(dir):
file_abs =dir+'\\'+fileif os.path.isdir(file_abs):returnTrueif"."infile:if self.check_file(file.split(".")[-1]):returnTruereturnFalsedefcheck_file(self, suffix):"""
看文件是否为图片文件
:param suffix:
:return:
"""if suffix in['jpg','png','jpeg','psd','bmp','webp']:returnTrueelse:returnFalsedefchange_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
classUi_MainWindow(object):defsetupUi(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)defretranslateUi(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。希望得到大家的赞,谢谢大家!
版权归原作者 懷淰メ 所有, 如有侵权,请联系我们删除。