利用 Python 制作图片轮播应用
在这篇博客中,我将向大家展示如何使用 xPython 创建一个图片轮播应用。这个应用能够从指定文件夹中加载图片,定时轮播,并提供按钮来保存当前图片到收藏夹或仅轮播收藏夹中的图片。我们还将实现退出按钮和全屏显示的功能。
C:\pythoncode\new\pictureinfoldershow.py
环境准备
首先,我们需要安装 wxPython 和 pypubsub:
pip install wxPython pypubsub
import wx
import os
import glob
import random
import xml.etree.ElementTree as ET
from pubsub import pubFAVORITE_FILE = 'favorite.xml'class PhotoFrame(wx.Frame):def __init__(self, parent, title):super(PhotoFrame, self).__init__(parent, title=title, size=(800, 600))self.Bind(wx.EVT_CLOSE, self.on_close)self.Bind(wx.EVT_KEY_DOWN, self.on_key_down)self.panel = wx.Panel(self)self.imageCtrl = wx.StaticBitmap(self.panel)# Button Panelbtn_panel = wx.Panel(self.panel)btn_exit = wx.Button(btn_panel, label="退出")btn_fav = wx.Button(btn_panel, label="保存当前文件到收藏夹")btn_show_fav = wx.Button(btn_panel, label="轮播收藏夹照片")btn_exit.Bind(wx.EVT_BUTTON, self.on_exit_button)btn_fav.Bind(wx.EVT_BUTTON, self.on_fav_button)btn_show_fav.Bind(wx.EVT_BUTTON, self.on_show_fav_button)btn_sizer = wx.BoxSizer(wx.VERTICAL)btn_sizer.Add(btn_exit, 0, wx.ALL, 5)btn_sizer.Add(btn_fav, 0, wx.ALL, 5)btn_sizer.Add(btn_show_fav, 0, wx.ALL, 5)btn_panel.SetSizer(btn_sizer)hbox = wx.BoxSizer(wx.HORIZONTAL)hbox.Add(self.imageCtrl, 1, wx.EXPAND | wx.ALL, 5)hbox.Add(btn_panel, 0, wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5)self.panel.SetSizer(hbox)self.timer = wx.Timer(self)self.Bind(wx.EVT_TIMER, self.on_timer, self.timer)pub.subscribe(self.on_folder_selected, "folder_selected")pub.subscribe(self.on_favorites_selected, "favorites_selected")self.ShowFullScreen(True)self.Centre()def on_close(self, event):self.timer.Stop()self.Destroy()def on_key_down(self, event):keycode = event.GetKeyCode()if keycode == wx.WXK_ESCAPE:self.Close()event.Skip()def on_exit_button(self, event):self.Close()def on_fav_button(self, event):self.save_to_favorites()def on_show_fav_button(self, event):self.show_favorites()def on_timer(self, event):if hasattr(self, 'photos') and self.photos:photo = random.choice(self.photos)self.show_photo(photo)def on_folder_selected(self, folder):self.photos = glob.glob(os.path.join(folder, "*.jpg")) + \glob.glob(os.path.join(folder, "*.png")) + \glob.glob(os.path.join(folder, "*.jpeg"))if self.photos:self.timer.Start(5000)def on_favorites_selected(self):self.photos = self.load_favorites()if self.photos:self.timer.Start(5000)def show_photo(self, photo):self.current_photo = photoimage = wx.Image(photo, wx.BITMAP_TYPE_ANY)screenWidth, screenHeight = self.GetSize()imgWidth, imgHeight = image.GetSize()# 等比例缩放aspectRatio = imgWidth / imgHeightif screenWidth / screenHeight > aspectRatio:newHeight = screenHeightnewWidth = screenHeight * aspectRatioelse:newWidth = screenWidthnewHeight = screenWidth / aspectRatioimage = image.Scale(int(newWidth), int(newHeight), wx.IMAGE_QUALITY_HIGH)self.imageCtrl.SetBitmap(wx.Bitmap(image))self.Layout()def save_to_favorites(self):if not hasattr(self, 'current_photo'):returnroot = ET.Element("favorites")if os.path.exists(FAVORITE_FILE):tree = ET.parse(FAVORITE_FILE)root = tree.getroot()new_entry = ET.SubElement(root, "photo")new_entry.text = self.current_phototree = ET.ElementTree(root)tree.write(FAVORITE_FILE, encoding='utf-8', xml_declaration=True)def load_favorites(self):if not os.path.exists(FAVORITE_FILE):return []tree = ET.parse(FAVORITE_FILE)root = tree.getroot()return [child.text for child in root.findall('photo')]def show_favorites(self):pub.sendMessage("favorites_selected")class FolderSelectorFrame(wx.Frame):def __init__(self, parent, title):super(FolderSelectorFrame, self).__init__(parent, title=title, size=(400, 200))panel = wx.Panel(self)vbox = wx.BoxSizer(wx.VERTICAL)self.folderPicker = wx.DirPickerCtrl(panel, message="选择照片文件夹")vbox.Add(self.folderPicker, proportion=1, flag=wx.EXPAND | wx.ALL, border=10)btn = wx.Button(panel, label="开始轮播")vbox.Add(btn, proportion=0, flag=wx.ALIGN_CENTER | wx.ALL, border=10)btn.Bind(wx.EVT_BUTTON, self.on_start_slideshow)panel.SetSizer(vbox)self.Centre()self.Show()def on_start_slideshow(self, event):folder = self.folderPicker.GetPath()pub.sendMessage("folder_selected", folder=folder)self.Close()class MyApp(wx.App):def OnInit(self):self.selectorFrame = FolderSelectorFrame(None, title="选择文件夹")self.photoFrame = PhotoFrame(None, title="照片轮播")return Trueif __name__ == "__main__":app = MyApp(False)app.MainLoop()
代码实现
我们将实现一个 PhotoFrame 类来展示图片,以及一个 FolderSelectorFrame 类来选择图片文件夹。以下是完整的代码:
import wx
import os
import glob
import random
import xml.etree.ElementTree as ET
from pubsub import pubFAVORITE_FILE = 'favorite.xml'class PhotoFrame(wx.Frame):def __init__(self, parent, title):super(PhotoFrame, self).__init__(parent, title=title, size=(800, 600))self.Bind(wx.EVT_CLOSE, self.on_close)self.Bind(wx.EVT_KEY_DOWN, self.on_key_down)self.panel = wx.Panel(self)self.imageCtrl = wx.StaticBitmap(self.panel)# Button Panelbtn_panel = wx.Panel(self.panel)btn_exit = wx.Button(btn_panel, label="退出")btn_fav = wx.Button(btn_panel, label="保存当前文件到收藏夹")btn_show_fav = wx.Button(btn_panel, label="轮播收藏夹照片")btn_exit.Bind(wx.EVT_BUTTON, self.on_exit_button)btn_fav.Bind(wx.EVT_BUTTON, self.on_fav_button)btn_show_fav.Bind(wx.EVT_BUTTON, self.on_show_fav_button)btn_sizer = wx.BoxSizer(wx.VERTICAL)btn_sizer.Add(btn_exit, 0, wx.ALL, 5)btn_sizer.Add(btn_fav, 0, wx.ALL, 5)btn_sizer.Add(btn_show_fav, 0, wx.ALL, 5)btn_panel.SetSizer(btn_sizer)hbox = wx.BoxSizer(wx.HORIZONTAL)hbox.Add(self.imageCtrl, 1, wx.EXPAND | wx.ALL, 5)hbox.Add(btn_panel, 0, wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5)self.panel.SetSizer(hbox)self.timer = wx.Timer(self)self.Bind(wx.EVT_TIMER, self.on_timer, self.timer)pub.subscribe(self.on_folder_selected, "folder_selected")pub.subscribe(self.on_favorites_selected, "favorites_selected")self.ShowFullScreen(True)self.Centre()def on_close(self, event):self.timer.Stop()self.Destroy()def on_key_down(self, event):keycode = event.GetKeyCode()if keycode == wx.WXK_ESCAPE:self.Close()event.Skip()def on_exit_button(self, event):self.Close()def on_fav_button(self, event):self.save_to_favorites()def on_show_fav_button(self, event):self.show_favorites()def on_timer(self, event):if hasattr(self, 'photos') and self.photos:photo = random.choice(self.photos)self.show_photo(photo)def on_folder_selected(self, folder):self.photos = glob.glob(os.path.join(folder, "*.jpg")) + \glob.glob(os.path.join(folder, "*.png")) + \glob.glob(os.path.join(folder, "*.jpeg"))if self.photos:self.timer.Start(5000)def on_favorites_selected(self):self.photos = self.load_favorites()if self.photos:self.timer.Start(5000)def show_photo(self, photo):self.current_photo = photoimage = wx.Image(photo, wx.BITMAP_TYPE_ANY)screenWidth, screenHeight = self.GetSize()imgWidth, imgHeight = image.GetSize()# 等比例缩放aspectRatio = imgWidth / imgHeightif screenWidth / screenHeight > aspectRatio:newHeight = screenHeightnewWidth = screenHeight * aspectRatioelse:newWidth = screenWidthnewHeight = screenWidth / aspectRatioimage = image.Scale(int(newWidth), int(newHeight), wx.IMAGE_QUALITY_HIGH)self.imageCtrl.SetBitmap(wx.Bitmap(image))self.Layout()def save_to_favorites(self):if not hasattr(self, 'current_photo'):returnroot = ET.Element("favorites")if os.path.exists(FAVORITE_FILE):tree = ET.parse(FAVORITE_FILE)root = tree.getroot()new_entry = ET.SubElement(root, "photo")new_entry.text = self.current_phototree = ET.ElementTree(root)tree.write(FAVORITE_FILE, encoding='utf-8', xml_declaration=True)def load_favorites(self):if not os.path.exists(FAVORITE_FILE):return []tree = ET.parse(FAVORITE_FILE)root = tree.getroot()return [child.text for child in root.findall('photo')]def show_favorites(self):pub.sendMessage("favorites_selected")class FolderSelectorFrame(wx.Frame):def __init__(self, parent, title):super(FolderSelectorFrame, self).__init__(parent, title=title, size=(400, 200))panel = wx.Panel(self)vbox = wx.BoxSizer(wx.VERTICAL)self.folderPicker = wx.DirPickerCtrl(panel, message="选择照片文件夹")vbox.Add(self.folderPicker, proportion=1, flag=wx.EXPAND | wx.ALL, border=10)btn = wx.Button(panel, label="开始轮播")vbox.Add(btn, proportion=0, flag=wx.ALIGN_CENTER | wx.ALL, border=10)btn.Bind(wx.EVT_BUTTON, self.on_start_slideshow)panel.SetSizer(vbox)self.Centre()self.Show()def on_start_slideshow(self, event):folder = self.folderPicker.GetPath()pub.sendMessage("folder_selected", folder=folder)self.Close()class MyApp(wx.App):def OnInit(self):self.selectorFrame = FolderSelectorFrame(None, title="选择文件夹")self.photoFrame = PhotoFrame(None, title="照片轮播")return Trueif __name__ == "__main__":app = MyApp(False)app.MainLoop()
功能实现
- 选择文件夹:用户可以选择一个包含图片的文件夹,应用将自动开始轮播。
- 定时轮播:每隔 5 秒钟,应用会自动切换到下一张图片。
- 全屏显示:应用启动时自动全屏显示图片。
- 退出功能:按下 ESC 键或点击“退出”按钮可以退出程序。
- 保存到收藏夹:点击“保存当前文件到收藏夹”按钮,将当前显示的图片路径保存到
favorite.xml文件。 - 轮播收藏夹图片:点击“轮播收藏夹照片”按钮,仅轮播
favorite.xml中保存的图片。
结果如下


总结
本文介绍了如何使用 wxPython 创建一个功能齐全的图片轮播应用。通过 wxPython 的强大功能和灵活的布局管理,我们能够轻松实现图片显示、定时切换、按钮交互和文件操作等功能。希望这篇博客能为你提供一些帮助,让你在 wxPython 的学习和使用过程中有所收获。如果有任何问题或建议,欢迎在评论区留言。
相关文章:
利用 Python 制作图片轮播应用
在这篇博客中,我将向大家展示如何使用 xPython 创建一个图片轮播应用。这个应用能够从指定文件夹中加载图片,定时轮播,并提供按钮来保存当前图片到收藏夹或仅轮播收藏夹中的图片。我们还将实现退出按钮和全屏显示的功能。 C:\pythoncode\new\…...
报表系统之Cube.js
Cube.js 是一个开源的分析框架,专为构建数据应用和分析工具而设计。它的主要目的是简化和加速构建复杂的分析和数据可视化应用。以下是对 Cube.js 的详细介绍: 核心功能和特点 1. 多数据源支持 Cube.js 支持从多个数据源中提取数据,包括 SQ…...
代码随想录算法训练营第45天
115.不同的子序列 但相对于刚讲过 392.判断子序列,本题 就有难度了 ,感受一下本题和 392.判断子序列 的区别。 代码随想录 class Solution {public int numDistinct(String s, String t) {int lenS s.length();int lenT t.length();int[][] dp new …...
solidity合约创建
合约可以通过使用new关键字来创建其他合约的实例。 这个过程会执行被创建合约的构造函数(如果存在的话),并返回一个指向新创建合约的地址的引用。 这种方式允许智能合约动态地在区块链上部署新合约,并与它们交互。 通过 new 创…...
队列---循环队列实现
循环队列详解 概述 循环队列是一种基于数组实现的队列数据结构,其中队列的队首和队尾是通过模运算连接起来形成一个逻辑上的环形结构。这样可以有效地利用数组的空间,避免出现“假溢出”的情况。 结构体定义 循环队列的结构体定义如下: …...
【视频讲解】后端增删改查接口有什么用?
B站视频地址 B站视频地址 前言 “后端增删改查接口有什么用”,其实这句话可以拆解为下面3个问题。 接口是什么意思?后端接口是什么意思?后端接口中的增删改查接口有什么用? 1、接口 概念:接口的概念在不同的领域中…...
双指针hard题
[LeetCode]4. Median of Two Sorted Arrays 中文 - YouTube 依赖merge sort和priorityqueue的废物 正式变身山景城一姐小迷妹✪ω✪ 寻找正序数组中位数 class Solution {public double findMedianSortedArrays(int[] nums1, int[] nums2) {int len1 nums1.length;int len2 …...
前端实现【 批量任务调度管理器 】demo优化
一、前提介绍 我在前文实现过一个【批量任务调度管理器】的 demo,能实现简单的任务批量并发分组,过滤等操作。但是还有很多优化空间,所以查找一些优化的库, 主要想优化两个方面, 上篇提到的: 针对 3&…...
【数据结构】包装类和泛型
🎉欢迎大家收看,请多多支持🌹 🥰关注小哇,和我一起成长🚀个人主页🚀 ⭐在更专栏Java ⭐数据结构 ⭐已更专栏有C语言、计算机网络⭐ 👑目录 包装类🌙 ⭐基本类型对应的包…...
浅学爬虫-数据存储
在数据爬取完成后,我们需要将数据存储起来,以便于后续的分析和处理。常见的数据存储方式包括存储到CSV文件和存储到数据库。下面我们详细介绍如何实现这些存储方式。 存储到CSV CSV(Comma-Separated Values)文件是一种常用的文本…...
十六、maven git-快速上手(智慧云教育平台)
🌻🌻 目录 一、概述及项目管理工具介绍1.1 项目介绍1.2 maven 介绍及其配置1.2.1 maven 介绍1.2.2 maven 下载与配置 1.3 pom 中常见标签的使用1.4 后端项目环境的搭建1.5 Git 简介1.6 Git 的基本使用1.6.1 码云的注册与仓库创建1.6.2 上传代码到码云仓库…...
chrome/edge浏览器插件开发入门与加载使用
同学们可以私信我加入学习群! 正文开始 前言一、插件与普通前端项目二、开发插件——manifest.json三、插件使用edge浏览器中使用/加载插件chrome浏览器中使用/加载插件 总结 前言 chrome插件的出现,初衷可能是为了方便用户更好地控制浏览器,…...
【完美解决】 TypeError: ‘str’ object does not support item assignment
【完美解决】 TypeError: ‘str’ object does not support item assignment 在Python编程中,遇到TypeError: str object does not support item assignment这样的错误通常意味着你试图修改字符串中的某个字符,但字符串是不可变类型,不支持这…...
Android SurfaceFlinger——渲染开始帧(四十三)
通过前面的文章我们介绍了 SurfaceFlinger 图层合成的整体流程,已经对应步骤的前五步,这里我们开始介绍帧渲染流程的第一步——开始帧。 1.更新输出设备的色彩配置文件2.更新与合成相关的状态3.计划合成帧图层4.写入合成状态5.设置颜色矩阵6.开始帧7.准备帧数据以进行显示(异…...
fastadmin搜索栏实现某字段动态下拉搜索
记录:fastadmin搜索栏实现某字段动态下拉搜索 方式一:使用selectpicker组件,可多选 { field: travel_agency, title:__(Travel_agency),addClass:"selectpicker", operate:"IN",data:"multiple", searchList:…...
.NET未来路在何方?
简述 在软件开发的漫长旅程中,将代码打包成可执行的EXE文件是一项必不可少的技能。它不仅能够保护源代码,还能为用户提供便捷的安装体验。但手动打包过程繁琐且容易出错,自动化打包成为了开发者的福音。 在软件开发的浩瀚星空中,.…...
Vue开发环境搭建
文章目录 引言I 安装NVM1.1 Windows系统安装NVM,实现Node.js多版本管理1.2 配置下载镜像1.3 NVM常用操作命令II VUE项目的基础配置2.1 制定不同的环境配置2.2 正式环境隐藏日志2.3 vscode常用插件引言 开发工具: node.js 、npm 开发编辑器:vscode 开发框架:VUE I 安装NVM…...
【数据结构初阶】详解:实现循环队列、用栈实现队列、用队列实现栈
文章目录 一、循环队列1、题目简述2、方法讲解2.1、了解tail的指向2.2、了解空间是如何利用的2.3、如何判断队列是否为空(假溢出问题)?2.4、实现代码 二、用栈实现队列1、题目简述2、方法讲解2.1、讲解2.2、实现代码 三、用队列实现栈1、题目…...
【Hot100】LeetCode—31. 下一个排列
目录 题目1- 思路2- 实现⭐31. 下一个排列——题解思路 3- ACM 实现 题目 原题连接:31. 下一个排列 1- 思路 技巧题,分为以下几个步骤 ① 寻找拐点: i 1 :出现 nums[i1] > nums[i] ,则 i 1 就是拐点 从右向左遍…...
找到学习的引擎,更让你进入心流状态的高效学习
一、心流状态的启动秘籍 1. 简单开始:找到学习的入口 从简单的任务开始,比如整理学习空间或列出学习计划,让大脑逐渐适应学习的节奏。 2. 环境塑造:打造专注的学习空间 清理桌面,减少干扰,比如将手机置…...
idea大量爆红问题解决
问题描述 在学习和工作中,idea是程序员不可缺少的一个工具,但是突然在有些时候就会出现大量爆红的问题,发现无法跳转,无论是关机重启或者是替换root都无法解决 就是如上所展示的问题,但是程序依然可以启动。 问题解决…...
django filter 统计数量 按属性去重
在Django中,如果你想要根据某个属性对查询集进行去重并统计数量,你可以使用values()方法配合annotate()方法来实现。这里有两种常见的方法来完成这个需求: 方法1:使用annotate()和Count 假设你有一个模型Item,并且你想…...
postgresql|数据库|只读用户的创建和删除(备忘)
CREATE USER read_only WITH PASSWORD 密码 -- 连接到xxx数据库 \c xxx -- 授予对xxx数据库的只读权限 GRANT CONNECT ON DATABASE xxx TO read_only; GRANT USAGE ON SCHEMA public TO read_only; GRANT SELECT ON ALL TABLES IN SCHEMA public TO read_only; GRANT EXECUTE O…...
【Go】3、Go语言进阶与依赖管理
前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课,做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程,它的核心机制是 Goroutine 协程、Channel 通道,并基于CSP(Communicating Sequential Processes࿰…...
NFT模式:数字资产确权与链游经济系统构建
NFT模式:数字资产确权与链游经济系统构建 ——从技术架构到可持续生态的范式革命 一、确权技术革新:构建可信数字资产基石 1. 区块链底层架构的进化 跨链互操作协议:基于LayerZero协议实现以太坊、Solana等公链资产互通,通过零知…...
Java入门学习详细版(一)
大家好,Java 学习是一个系统学习的过程,核心原则就是“理论 实践 坚持”,并且需循序渐进,不可过于着急,本篇文章推出的这份详细入门学习资料将带大家从零基础开始,逐步掌握 Java 的核心概念和编程技能。 …...
OPenCV CUDA模块图像处理-----对图像执行 均值漂移滤波(Mean Shift Filtering)函数meanShiftFiltering()
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 在 GPU 上对图像执行 均值漂移滤波(Mean Shift Filtering),用于图像分割或平滑处理。 该函数将输入图像中的…...
Mac下Android Studio扫描根目录卡死问题记录
环境信息 操作系统: macOS 15.5 (Apple M2芯片)Android Studio版本: Meerkat Feature Drop | 2024.3.2 Patch 1 (Build #AI-243.26053.27.2432.13536105, 2025年5月22日构建) 问题现象 在项目开发过程中,提示一个依赖外部头文件的cpp源文件需要同步,点…...
CSS | transition 和 transform的用处和区别
省流总结: transform用于变换/变形,transition是动画控制器 transform 用来对元素进行变形,常见的操作如下,它是立即生效的样式变形属性。 旋转 rotate(角度deg)、平移 translateX(像素px)、缩放 scale(倍数)、倾斜 skewX(角度…...
提升移动端网页调试效率:WebDebugX 与常见工具组合实践
在日常移动端开发中,网页调试始终是一个高频但又极具挑战的环节。尤其在面对 iOS 与 Android 的混合技术栈、各种设备差异化行为时,开发者迫切需要一套高效、可靠且跨平台的调试方案。过去,我们或多或少使用过 Chrome DevTools、Remote Debug…...
