目录
在这篇博客中,我们将一步步探索如何使用Python和PyQt5库来创建一个简单的图片浏览器。这个项目非常适合初学者学习GUI编程和文件处理。
1. 概述
我们的目标是制作一个能够浏览电脑上图片文件的应用程序。用户可以在文件系统树中选择一个文件夹,应用程序随后展示该文件夹内的所有图片。此外,我们的图片浏览器将提供基本的导航功能,如查看上一张或下一张图片,并显示当前查看图片的信息。
2. 功能点
- 文件系统访问:使用QTreeView和QFileSystemModel浏览本地文件系统。
- 图片展示:通过QLabel展示选中的图片。
- 图片缩放:保持图片的长宽比例正确缩放。
- 导航功能:实现“上一张”和“下一张”按钮,用于图片间的导航。
- 显示图片信息:展示当前图片的序号、总图片数和文件名。
- 右键菜单:右键点击图片可复制图片的完整路径。
3.完整代码
import sys
import os
from PyQt5.QtWidgets import QApplication, QMainWindow, QTreeView, QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, QFileSystemModel, QSplitter, QMenu
from PyQt5.QtGui import QPixmap
from PyQt5.QtCore import QModelIndex, Qt, QSize
class ImagePreviewer(QMainWindow):
def __init__(self):
super().__init__()
self.currentImageIndex = 0
self.imageFiles = []
self.initUI()
def initUI(self):
self.setWindowTitle('图片预览工具')
self.setGeometry(100, 100, 800, 600)
# 主布局
mainLayout = QHBoxLayout()
# 文件系统树
self.treeView = QTreeView()
self.model = QFileSystemModel()
self.model.setRootPath('')
self.treeView.setModel(self.model)
self.treeView.clicked.connect(self.on_treeView_clicked)
# 图片显示区域
self.imageLabel = QLabel("请选择一个文件夹")
self.imageLabel.setScaledContents(True)
self.imageLabel.setContextMenuPolicy(Qt.CustomContextMenu)
self.imageLabel.customContextMenuRequested.connect(self.openMenu)
# 图片信息标签
self.imageInfoLabel = QLabel("图片信息")
self.imageInfoLabel.setFixedHeight(30)
# 上一张、下一张按钮
self.prevButton = QPushButton("上一张")
self.nextButton = QPushButton("下一张")
self.prevButton.clicked.connect(self.showPrevImage)
self.nextButton.clicked.connect(self.showNextImage)
# 布局设置
buttonLayout = QHBoxLayout()
buttonLayout.addWidget(self.prevButton)
buttonLayout.addWidget(self.nextButton)
rightLayout = QVBoxLayout()
rightLayout.addWidget(self.imageLabel)
rightLayout.addWidget(self.imageInfoLabel)
rightLayout.addLayout(buttonLayout)
splitter = QSplitter(Qt.Horizontal)
splitter.addWidget(self.treeView)
rightWidget = QWidget()
rightWidget.setLayout(rightLayout)
splitter.addWidget(rightWidget)
splitter.setSizes([200, 600])
mainLayout.addWidget(splitter)
# 中心窗口
centralWidget = QWidget()
centralWidget.setLayout(mainLayout)
self.setCentralWidget(centralWidget)
def on_treeView_clicked(self, index: QModelIndex):
path = self.model.filePath(index)
if os.path.isdir(path):
self.imageFiles = [os.path.join(path, f) for f in os.listdir(path) if os.path.isfile(os.path.join(path, f)) and f.lower().endswith(('.png', '.jpg', '.jpeg', '.bmp', '.gif'))]
self.currentImageIndex = 0
self.showImageByIndex(self.currentImageIndex)
def showImageByIndex(self, index):
if 0 <= index < len(self.imageFiles):
self.currentImageIndex = index
pixmap = QPixmap(self.imageFiles[index])
scaledPixmap = self.scalePixmapToLabel(pixmap, self.imageLabel)
self.imageLabel.setScaledContents(False)
self.imageLabel.setPixmap(scaledPixmap)
self.updateImageInfo()
def scalePixmapToLabel(self, pixmap, label):
labelSize = label.size()
pixmapSize = pixmap.size()
if labelSize.width() / labelSize.height() > pixmapSize.width() / pixmap.height(): # 高度缩放
ratio = labelSize.height() / pixmapSize.height()
else:
ratio = labelSize.width() / pixmapSize.width()
new_size = pixmapSize * ratio
scaledSize = pixmapSize.scaled(new_size, Qt.KeepAspectRatio)
return pixmap.scaled(scaledSize, Qt.KeepAspectRatio)
def updateImageInfo(self):
filename = os.path.basename(self.imageFiles[self.currentImageIndex])
infoText = f"{self.currentImageIndex + 1}/{len(self.imageFiles)}: {filename}"
self.imageInfoLabel.setText(infoText)
def showPrevImage(self):
if self.currentImageIndex > 0:
self.showImageByIndex(self.currentImageIndex - 1)
def showNextImage(self):
if self.currentImageIndex < len(self.imageFiles) - 1:
self.showImageByIndex(self.currentImageIndex + 1)
def openMenu(self, position):
menu = QMenu()
copyAction = menu.addAction("复制图片地址")
action = menu.exec_(self.imageLabel.mapToGlobal(position))
if action == copyAction:
QApplication.clipboard().setText(self.imageFiles[self.currentImageIndex])
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = ImagePreviewer()
ex.show()
sys.exit(app.exec_())