图片查看器:用PyQt5实现本地图片预览工具
通过python代码,基于PyQt5实现本地图片预览查看工具。
我们对窗口进行了圆角设计,图片的翻页按钮半透明处理,当鼠标移动至按钮上的动画效果,当选择某一张图片,进行左右翻页则轮播同目录所有支持的图片格式。
import sys
import os
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QLabel,
QPushButton, QFileDialog, QHBoxLayout, QVBoxLayout,
QMessageBox)
from PyQt5.QtGui import QPixmap, QIcon, QFont, QColor, QPainter
from PyQt5.QtCore import Qt, QSize, QPoint, QPropertyAnimation, QEasingCurve
class ImageViewer(QMainWindow):
def __init__(self):
super().__init__()
self.current_file = None
self.files = []
self.current_index = -1
self.original_pixmap = None # 存储原始图片数据
self.initUI()
def initUI(self):
# 窗口设置
self.setWindowTitle('一个黑客创业者')
self.setGeometry(100, 100, 1280, 800)
self.setMinimumSize(800, 600)
# 深色主题样式
self.setStyleSheet("""
QMainWindow {
background-color: #1A1A1A;
}
QLabel#imageLabel {
background-color: #000000;
border: none;
}
#openButton {
background-color: #4A90E2;
color: white;
padding: 12px 24px;
border-radius: 8px;
font-family: 'Segoe UI';
font-size: 14px;
min-width: 120px;
}
#openButton:hover {
background-color: #357ABD;
}
#openButton:pressed {
background-color: #2A5F8F;
}
#navButton {
background-color: rgba(0, 0, 0, 0.5);
color: rgba(255, 255, 255, 0.9);
border-radius: 30px;
font-size: 24px;
}
#navButton:hover {
background-color: rgba(0, 0, 0, 0.7);
}
#navButton:disabled {
color: rgba(255, 255, 255, 0.3);
}
""")
# 主窗口部件
central_widget = QWidget()
self.setCentralWidget(central_widget)
main_layout = QVBoxLayout(central_widget)
main_layout.setContentsMargins(0, 0, 0, 0)
main_layout.setSpacing(0)
# 图片显示区域
self.image_label = QLabel()
self.image_label.setObjectName("imageLabel")
self.image_label.setAlignment(Qt.AlignCenter)
main_layout.addWidget(self.image_label)
# 底部控制栏
self.create_bottom_bar()
# 悬浮导航按钮
self.create_floating_controls()
# 初始化按钮状态
self.update_nav_buttons()
def create_bottom_bar(self):
# 底部控制栏
bottom_bar = QWidget()
bottom_bar.setFixedHeight(80)
bottom_bar.setStyleSheet("background-color: rgba(26, 26, 26, 0.9);")
layout = QHBoxLayout(bottom_bar)
layout.setContentsMargins(40, 0, 40, 0)
self.btn_open = QPushButton(" 打开图片", self)
self.btn_open.setObjectName("openButton")
self.btn_open.setIcon(QIcon.fromTheme("document-open"))
self.btn_open.setIconSize(QSize(20, 20))
layout.addStretch()
layout.addWidget(self.btn_open)
layout.addStretch()
self.centralWidget().layout().addWidget(bottom_bar)
# 信号连接
self.btn_open.clicked.connect(self.open_image)
def create_floating_controls(self):
# 左侧导航按钮
self.btn_prev = QPushButton("◀", self)
self.btn_prev.setObjectName("navButton")
self.btn_prev.setFixedSize(60, 60)
# 右侧导航按钮
self.btn_next = QPushButton("▶", self)
self.btn_next.setObjectName("navButton")
self.btn_next.setFixedSize(60, 60)
# 为每个按钮创建独立的动画对象
self.prev_animation = QPropertyAnimation(self.btn_prev, b"pos")
self.prev_animation.setEasingCurve(QEasingCurve.OutQuad)
self.next_animation = QPropertyAnimation(self.btn_next, b"pos")
self.next_animation.setEasingCurve(QEasingCurve.OutQuad)
# 信号连接
self.btn_prev.clicked.connect(self.prev_image)
self.btn_next.clicked.connect(self.next_image)
def resizeEvent(self, event):
# 更新按钮位置
nav_y = self.height() // 2 - 30
self.btn_prev.move(30, nav_y)
self.btn_next.move(self.width() - 90, nav_y)
# 更新图片显示(使用缓存的原始图片)
self.update_pixmap()
super().resizeEvent(event)
def open_image(self):
try:
file_path, _ = QFileDialog.getOpenFileName(
self, "选择图片", "",
"图片文件 (*.png *.jpg *.jpeg *.bmp *.gif)"
)
if file_path:
self.load_image(file_path)
self.update_file_list(file_path)
except Exception as e:
self.show_error("打开失败", str(e))
def load_image(self, file_path):
try:
pixmap = QPixmap(file_path)
if pixmap.isNull():
raise ValueError("不支持的图片格式")
self.original_pixmap = pixmap # 保存原始图片
self.update_pixmap()
self.current_file = file_path
except Exception as e:
self.show_error("加载失败", str(e))
self.image_label.clear()
def update_pixmap(self):
"""根据当前窗口尺寸更新显示图片"""
if self.original_pixmap:
scaled = self.original_pixmap.scaled(
self.image_label.size() - QSize(2, 2),
Qt.KeepAspectRatio,
Qt.SmoothTransformation
)
self.image_label.setPixmap(scaled)
def update_file_list(self, file_path):
try:
directory = os.path.dirname(file_path)
self.files = sorted([
os.path.normpath(os.path.join(directory, f))
for f in os.listdir(directory)
if f.lower().endswith(('.png', '.jpg', '.jpeg', '.bmp', '.gif'))
])
normed_file_path = os.path.normpath(file_path)
if normed_file_path not in self.files:
raise FileNotFoundError(f"{normed_file_path} 不在这个目录中")
self.current_index = self.files.index(normed_file_path)
self.update_nav_buttons()
except Exception as e:
self.show_error("列表更新失败", str(e))
def prev_image(self):
if self.current_index > 0:
self.current_index -= 1
self.load_image(self.files[self.current_index])
self.update_nav_buttons()
self.animate_nav_button(self.btn_prev)
def next_image(self):
if self.current_index < len(self.files) - 1:
self.current_index += 1
self.load_image(self.files[self.current_index])
self.update_nav_buttons()
self.animate_nav_button(self.btn_next)
def update_nav_buttons(self):
self.btn_prev.setEnabled(self.current_index > 0)
self.btn_next.setEnabled(self.current_index < len(self.files) - 1)
def animate_nav_button(self, button):
"""改进后的按钮动画方法"""
animation = self.prev_animation if button == self.btn_prev else self.next_animation
original_pos = button.pos()
offset = QPoint(-10, 0) if button == self.btn_prev else QPoint(10, 0)
animation.stop()
animation.setStartValue(original_pos + offset)
animation.setEndValue(original_pos)
animation.setDuration(200)
animation.start()
def show_error(self, title, message):
QMessageBox.critical(self, title,
f"<span style='color:#fff;font-size:14px;'>{message}</span>",
QMessageBox.Ok)
if __name__ == '__main__':
app = QApplication(sys.argv)
# 设置全局字体
font = QFont("Microsoft YaHei" if sys.platform == 'win32' else "Arial")
app.setFont(font)
viewer = ImageViewer()
viewer.show()
sys.exit(app.exec_())