使用wxpython实现的一个简单图片浏览器实例


Posted in Python onJuly 10, 2014

上次我爬了n多图片,但是浏览的时候有一个问题。

图片浏览器的浏览一般都是按名称排的,而我对图片的命名是按照数字递增的。比如3总是会排在10后面,也就无法快速地浏览图片了。

所以,出于方便自己查阅图片,也出于学习,决定做一个自己的图片浏览器。

目标:浏览目录,通过滚轮不断显示同一个文件夹下的图片,并自定义排序。

步骤0:要实现图形界面,我使用wxPython。

至于如何安装和简单地使用wxpython,可以到网上检索,一大堆资料。
以下步骤默认你已经知道如何生成一个自己的frame。

步骤1:浏览目录。

这个功能就是类似于打开“我的电脑”,然后不断地进入文件夹和返回。
通过几种尝试,我决定使用listbox。

我初始化一个app。用一个frame实现目录的功能,其上只有一个listbox;用另一个frame实现图片展示的功能,两个frame通过app进行信息的传递。

for _dir in os.listdir(dir):

    #do something

其中像os.path.split()、os.path.splitext()、os.path.isdir()等,都是很常用的一些方法。

显示目录就是一个不断地获取你选择的目录,进入目录,读取其下目录,清空listbox,显示目录,更改工作路径的过程。

显示目录的时候,自定义排序功能就来了。通过对图片名称进行处理,转为数字,排序,然后再重新组装回去,从而达到按数字递增的效果。

self.list.Bind(wx.EVT_LISTBOX_DCLICK, self.OnDClick)

同时通过以上方法为listbox绑定了双击事件。若是双击目录则进入目录,否则显示通过app展示图片。

同时这个frame需要具备两个方法,就是获取上一张或下一张图片,为了后续app的调用。

步骤2:显示图片

这个图片展示一开始感觉挺麻烦的,但是弄懂了之后就很简单了。

我用一个frame展示图片。

frame上面空白,有一个wx.StaticBitmap,之后显示图片的时候只需要往这个staticbitmap写bitmap就可以了。一开始我是不断地新建staticbitmap,导致了一些可以看到但是那时候不知道为什么的原因。

然后在这个frame上检测鼠标滚轮事件,通过向上或向下滚轮调用app的GetNextImage和GetPreImage方法并将获得的图片显示出来。

还有图片的大小,我先规定了一个最大值和最小值,将图片约束在一定的范围内。

bmp = image.Scale(size[0], size[1]).ConvertToBitmap()

self.bmp.SetSize(size)#bmp是staticbitmap

self.bmp.SetBitmap(bmp)

但是一个小窗口看图片很不爽,于是将图片窗口全屏化。

self.ShowFullScreen(True, style=wx.FULLSCREEN_ALL)

全屏化就要考虑怎么退出了。我通过按键发送消息,命令窗口关闭或显示。若显示则关闭(其实只是隐藏),若隐藏则显示。注意这个事件要绑定到app上面。

self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)

然后图片需要能够放大和缩小。于是我再次通过按键触发。放大或缩小只需要通过改变staticbitmap的最大值并让bmp适应那个size就可以了。

由于全屏了,那么需要能够移动图片。移动的时候也只需要移动staticbitmap就行了。

#注意这里要将事件绑定到staticbitmap上面

self.bmp.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)

self.bmp.Bind(wx.EVT_LEFT_UP, self.OnLeftUp)

self.bmp.Bind(wx.EVT_MOTION, self.OnMotion)

至此,大概要点都讲完了,下面是全部代码。仔细查阅会发现一些新的用法,

不过这个图片浏览器估计只是够我用,不过其实用起来已经挺爽的了, 全屏的图片展示,还可以随便移动图片,快捷键很方便地退出全屏。

里面还有一些不完善的地方需要改进,希望能跟大家多多交流~

感谢这期间被我大量参考资料的作者们。

#!/usr/bin/env Python

#coding=utf-8
#filename : PictureBrowser.py

#date     : 2012-10-11

import wx

import os

import sys

import string
#你有H盘吗?没有的话在这个初始化函数里修改加载的初始路径

class PBDirFrame(wx.Frame):

    def __init__(self, app):

        wx.Frame.__init__(self, None, -1, "选择文件夹", size=(250,500))
        self.app = app
        #设置字体

        font = wx.Font(12, wx.DEFAULT, wx.NORMAL, wx.NORMAL, False, 'Courier New')

        self.SetFont(font)

        

        #文件夹listbox

        self.list = wx.ListBox(self, -1, (0,0), (200,600), '', wx.LB_SINGLE)

        self.list.Bind(wx.EVT_LISTBOX_DCLICK, self.OnDClick)

        

        #加载当前文件夹

        #curdir = os.getcwd()#在这里修改初始路径,这个是当前工作路径

        curdir = 'H:\\'

        os.chdir(curdir)

        self.LoadDir(curdir)

        

        #绑定事件

        self.Bind(wx.EVT_CLOSE, self.OnClose)


        

        #显示窗口

        self.Show()

    

    def OnClose(self, event):

        self.Destroy()

        self.app.Close()

    

    #listbox双击事件

    def OnDClick(self, event):

        if self.list.GetSelection()==0:#判断是否选择了返回上一层文件夹

            path = os.getcwd()

            pathinfo = os.path.split(path)

            dir = pathinfo[0]

        else:#获得需要进入的下一层文件夹

            dir = self.list.GetStringSelection()

        

        if os.path.isdir(dir):#进入文件夹

            self.LoadDir(dir)

        elif os.path.splitext(dir)[-1]=='.jpg':#显示图片

            self.app.ShowImage(dir)
    #加载文件夹,如果你想定义自己的排序,那么修改这个方法吧~

    def LoadDir(self, dir):

        #不是目录则不进行操作

        if not os.path.isdir(dir):

            return

        

        self.list.Clear()#清空

        self.list.Append('...')#添加返回上一层文件夹标志
        dirs = []

        jpgs = []

        nnjpgs = []

        for _dir in os.listdir(dir):

            if os.path.isdir(dir+os.path.sep+_dir):

                dirs.append(_dir)

            else:

                info = os.path.splitext(_dir)

                if info[-1]=='.jpg':

                    if info[0].isdigit():

                        jpgs.append(string.atoi(info[0]))#转化为数字

                    else:

                        nnjpgs.append(_dir)

        jpgs.sort()

        for _jpgs in jpgs:

            self.list.Append(str(_jpgs)+'.jpg')

        for _nnjpgs in nnjpgs:

            self.list.Append(_nnjpgs)

        for _dirs in dirs:

            self.list.Append(_dirs)
        os.chdir(dir)#设置工作路径
    #获得下一张要显示的图片

    def GetNextImage(self):

        index = self.list.GetSelection()

        i = index

        while i+1<self.list.GetCount():

            i += 1

            if os.path.splitext(self.list.GetString(i))[-1]=='.jpg':

                break

        if i<self.list.GetCount():

            index = i

        self.list.SetSelection(index)

        return self.list.GetStringSelection()
    #获得上一张图片

    def GetPreImage(self):

        index = self.list.GetSelection()

        i = index

        while i-1>0:

            i -= 1

            if os.path.splitext(self.list.GetString(i))[-1]=='.jpg':

                break

        if i>0:

            index = i

        

        self.list.SetSelection(index)

        return self.list.GetStringSelection()


class PBPicFrame(wx.Frame):
    max_width = 400

    max_height = 600
    def __init__(self, app):

        wx.Frame.__init__(self, None, -1, "显示图片", size=(400,400))#, style=wx.SIMPLE_BORDER)
        #是否要移动图片的标志

        self.bmoved = False

        

        self.app = app
        #staticbitmap

        self.bmp = wx.StaticBitmap(self, 0, wx.NullBitmap, (0,0), (400,400))


        self.Bind(wx.EVT_MOUSEWHEEL, self.OnChangeImage)

        self.bmp.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)

        self.bmp.Bind(wx.EVT_LEFT_UP, self.OnLeftUp)

        self.bmp.Bind(wx.EVT_MOTION, self.OnMotion)

        self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)

        

        self.ShowFullScreen(True, style=wx.FULLSCREEN_ALL)

        self.Hide()


    def ShowImage(self, path):

        if os.path.splitext(path)[-1]!='.jpg':

            return

        self.bmppath = path

        image = wx.Image(path, wx.BITMAP_TYPE_JPEG)

        bmp = image.ConvertToBitmap()

        size = self.GetSize(bmp)

        bmp = image.Scale(size[0], size[1]).ConvertToBitmap()

        self.bmp.SetSize(size)

        self.bmp.SetBitmap(bmp)

        self.Show()
    def GetSize(self, bmp):

        width = bmp.GetWidth()

        height = bmp.GetHeight()

        if width>self.max_width:

            height = height*self.max_width/width

            width = self.max_width

        if height>self.max_height:

            width = width*self.max_height/height

            height = self.max_height

        size = width, height

        return size

       
    def OnChangeImage(self, event):

        rotation = event.GetWheelRotation()

        if rotation<0:

            self.app.ShowNextImage()

        else:

            self.app.ShowPreImage()

    

    def OnLeftDown(self, event):

        self.pos = event.GetX(), event.GetY()

        self.bmoved = True
    def OnLeftUp(self, event):

        self.bmoved = False
    def OnMotion(self, event):

        if not self.bmoved:

            return

        pos = event.GetX(), event.GetY()

        dx = pos[0]-self.pos[0]

        dy = pos[1]-self.pos[1]

        pos = self.bmp.GetPosition()

        pos = pos[0]+dx, pos[1]+dy

        self.bmp.SetPosition(pos)
    def OnKeyDown(self, event):

        keycode = event.GetKeyCode()

        if keycode == 49:#数字1放大

            self.SizeUp()

        elif keycode == 50:#数字2缩小

            self.SizeDown()

        event.Skip()#这个貌似很重要,要同时触发app上的快捷键
    def SizeUp(self):

        self.max_width += 50

        self.max_height += 75

        self.ShowImage(self.bmppath)

    def SizeDown(self):

        self.max_width -= 50

        self.max_height -= 75

        self.ShowImage(self.bmppath)
class PBApp(wx.App):

    

    #redirect=False将信息输出到dos界面

    def __init__(self, redirect=False):

        wx.App.__init__(self, redirect)

    

    def OnInit(self):

        

        #显示文件夹列表界面

        self.dirframe = PBDirFrame(self)

        

        #显示图片界面

        self.picframe = PBPicFrame(self)

        

        #绑定事件

        self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)

        return True
    def ShowImage(self, path):

        #print 'showing app img', path

        self.picframe.ShowImage(path)

        self.picframe.SetFocus()

    

    def ShowNextImage(self):

        path = self.dirframe.GetNextImage()

        self.ShowImage(path)
    def ShowPreImage(self):

        path = self.dirframe.GetPreImage()

        self.ShowImage(path)
    def OnKeyDown(self, event):

        keycode = event.GetKeyCode()

        #print keycode

        if keycode == 27:# ESC键

            #切换图片窗体的显示和隐藏

            if self.picframe.IsShown():

                self.picframe.Hide()

            else:

                self.picframe.Show()

    

    def Close(self):

        self.picframe.Close()

    

    

def main():

    app = PBApp()

    app.MainLoop()
if __name__=='__main__':

    main()
Python 相关文章推荐
python检测服务器是否正常
Feb 16 Python
python二分查找算法的递归实现方法
May 12 Python
python调用百度REST API实现语音识别
Aug 30 Python
在Pycharm中执行scrapy命令的方法
Jan 16 Python
PythonWeb项目Django部署在Ubuntu18.04腾讯云主机上
Apr 01 Python
Python Django 前后端分离 API的方法
Aug 28 Python
python监控nginx端口和进程状态
Sep 06 Python
Python2和Python3中@abstractmethod使用方法
Feb 04 Python
python实现银行实战系统
Feb 26 Python
Python 实现 T00ls 自动签到脚本代码(邮件+钉钉通知)
Jul 06 Python
Python 测试框架unittest和pytest的优劣
Sep 26 Python
python SOCKET编程基础入门
Feb 27 Python
Python语言的12个基础知识点小结
Jul 10 #Python
使用Python获取Linux系统的各种信息
Jul 10 #Python
Django中实现一个高性能计数器(Counter)实例
Jul 09 #Python
python实现的登录和操作开心网脚本分享
Jul 09 #Python
python实现的一个火车票转让信息采集器
Jul 09 #Python
python的描述符(descriptor)、装饰器(property)造成的一个无限递归问题分享
Jul 09 #Python
Python中__init__和__new__的区别详解
Jul 09 #Python
You might like
PHP系列学习之日期函数使用介绍
2012/08/18 PHP
php查看请求头信息获取远程图片大小的方法分享
2013/12/25 PHP
thinkphp模板赋值与替换实例简述
2014/11/24 PHP
php实现比较两个字符串日期大小的方法
2015/05/12 PHP
Yii2实现ajax上传图片插件用法
2016/04/28 PHP
php插入含有特殊符号数据的处理方法
2016/11/24 PHP
PHP isset()与empty()的使用区别详解
2017/02/10 PHP
PHP ajax+jQuery 实现批量删除功能实例代码小结
2018/12/06 PHP
Ajax+PHP实现的模拟进度条功能示例
2019/02/11 PHP
基于jQuery的360图片展示实现代码
2012/06/14 Javascript
js自定义事件及事件交互原理概述(二)
2013/02/01 Javascript
javaScript NameSpace 简单说明介绍
2013/07/18 Javascript
不要使用jQuery触发原生事件的方法
2014/03/03 Javascript
jQuery获得页面元素的绝对/相对位置即绝对X,Y坐标
2014/03/06 Javascript
Javascript 运动中Offset的bug解决方案
2014/12/24 Javascript
使用JQuery在线制作ppt并在线演示源码特效
2015/09/08 Javascript
Jquery插件之Fancybox丰富的弹出层效果附源码下载
2015/12/02 Javascript
给before和after伪元素设置js效果的方法
2015/12/04 Javascript
Node.js文件操作方法汇总
2016/03/22 Javascript
关于数据与后端进行交流匹配(点亮星星)
2016/08/03 Javascript
Vue.js路由组件vue-router使用方法详解
2016/12/02 Javascript
Javascript ES6中对象类型Sets的介绍与使用详解
2017/07/17 Javascript
angular+ionic返回上一页并刷新页面
2017/08/08 Javascript
JS添加或删除HTML dom元素的方法实例分析
2019/03/05 Javascript
实例讲解React 组件生命周期
2020/07/08 Javascript
vue实现折线图 可按时间查询
2020/08/21 Javascript
vue elementui tree 任意级别拖拽功能代码
2020/08/31 Javascript
浅谈JSON5解决了JSON的两大痛点
2020/12/14 Javascript
python selenium 弹出框处理的实现
2019/02/26 Python
Python enumerate内置库用法解析
2020/02/24 Python
python 常见的反爬虫策略
2020/09/27 Python
Sunglasses Shop瑞典:欧洲领先的太阳镜网上商店
2018/04/22 全球购物
RIP版本1跟版本2的区别
2013/12/30 面试题
生产内勤岗位职责
2013/12/07 职场文书
台风停课通知
2015/04/24 职场文书
入党申请书怎么写?
2019/06/11 职场文书