当前位置: 首页 > news >正文

图片查看器:用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_())

相关文章:

  • 使用AOP + Prometheus + node-exporter + grafana 实现Java系统的接口监控
  • 【自动化】Automa网页自动化之路
  • ⭐算法OJ⭐链表排序【归并排序】(C++/JavaScript 实现)
  • ChatGPT辅助学术写作有哪些挑战?怎么解决?
  • 11 应用层的域名知识点
  • Application.OnTime如何引用带参数的过程
  • 【Hive】Hive安装
  • 网络安全基础与应用习题 网络安全基础答案
  • 解决电脑问题(7)——软件问题
  • Android中使用Glide加载图片闪烁问题
  • Transformer原理硬核解析:Self-Attention与位置编码
  • 算法优选系列(1.双指针_下)
  • Python Flask 构建REST API 简介
  • Linux 进程信号
  • 文件包含漏洞第一关
  • llvm数据流分析
  • 【数据结构】2算法及分析
  • Android 粘包与丢包处理工具类:支持多种粘包策略的 Helper 实现
  • 灰度发布和方法灰度实践探索
  • 【一起学Rust | Tauri2.0框架】基于 Rust 与 Tauri 2.0 框架实现软件开机自启
  • 解锁川北底色密码,“文化三地”志愿宣讲员招募计划启动报名
  • 视频丨054B型护卫舰钦州舰南海实战化训练
  • 消费持续升温,这个“五一”假期有何新亮点?
  • 5月1日,多位省级党委书记调研旅游市场、假期安全等情况
  • 苹果第二财季营收增长5%,受关税政策影响预计下一财季新增9亿美元成本
  • 全文丨中华人民共和国传染病防治法