利用 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. 环境塑造:打造专注的学习空间 清理桌面,减少干扰,比如将手机置…...

Docker 离线安装指南
参考文章 1、确认操作系统类型及内核版本 Docker依赖于Linux内核的一些特性,不同版本的Docker对内核版本有不同要求。例如,Docker 17.06及之后的版本通常需要Linux内核3.10及以上版本,Docker17.09及更高版本对应Linux内核4.9.x及更高版本。…...
在软件开发中正确使用MySQL日期时间类型的深度解析
在日常软件开发场景中,时间信息的存储是底层且核心的需求。从金融交易的精确记账时间、用户操作的行为日志,到供应链系统的物流节点时间戳,时间数据的准确性直接决定业务逻辑的可靠性。MySQL作为主流关系型数据库,其日期时间类型的…...

树莓派超全系列教程文档--(61)树莓派摄像头高级使用方法
树莓派摄像头高级使用方法 配置通过调谐文件来调整相机行为 使用多个摄像头安装 libcam 和 rpicam-apps依赖关系开发包 文章来源: http://raspberry.dns8844.cn/documentation 原文网址 配置 大多数用例自动工作,无需更改相机配置。但是,一…...
Oracle查询表空间大小
1 查询数据库中所有的表空间以及表空间所占空间的大小 SELECTtablespace_name,sum( bytes ) / 1024 / 1024 FROMdba_data_files GROUP BYtablespace_name; 2 Oracle查询表空间大小及每个表所占空间的大小 SELECTtablespace_name,file_id,file_name,round( bytes / ( 1024 …...

UDP(Echoserver)
网络命令 Ping 命令 检测网络是否连通 使用方法: ping -c 次数 网址ping -c 3 www.baidu.comnetstat 命令 netstat 是一个用来查看网络状态的重要工具. 语法:netstat [选项] 功能:查看网络状态 常用选项: n 拒绝显示别名&#…...

智能在线客服平台:数字化时代企业连接用户的 AI 中枢
随着互联网技术的飞速发展,消费者期望能够随时随地与企业进行交流。在线客服平台作为连接企业与客户的重要桥梁,不仅优化了客户体验,还提升了企业的服务效率和市场竞争力。本文将探讨在线客服平台的重要性、技术进展、实际应用,并…...
鱼香ros docker配置镜像报错:https://registry-1.docker.io/v2/
使用鱼香ros一件安装docker时的https://registry-1.docker.io/v2/问题 一键安装指令 wget http://fishros.com/install -O fishros && . fishros出现问题:docker pull 失败 网络不同,需要使用镜像源 按照如下步骤操作 sudo vi /etc/docker/dae…...

Map相关知识
数据结构 二叉树 二叉树,顾名思义,每个节点最多有两个“叉”,也就是两个子节点,分别是左子 节点和右子节点。不过,二叉树并不要求每个节点都有两个子节点,有的节点只 有左子节点,有的节点只有…...

有限自动机到正规文法转换器v1.0
1 项目简介 这是一个功能强大的有限自动机(Finite Automaton, FA)到正规文法(Regular Grammar)转换器,它配备了一个直观且完整的图形用户界面,使用户能够轻松地进行操作和观察。该程序基于编译原理中的经典…...

学校时钟系统,标准考场时钟系统,AI亮相2025高考,赛思时钟系统为教育公平筑起“精准防线”
2025年#高考 将在近日拉开帷幕,#AI 监考一度冲上热搜。当AI深度融入高考,#时间同步 不再是辅助功能,而是决定AI监考系统成败的“生命线”。 AI亮相2025高考,40种异常行为0.5秒精准识别 2025年高考即将拉开帷幕,江西、…...