使用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 相关文章推荐
Django中实现点击图片链接强制直接下载的方法
May 14 Python
python实现list元素按关键字相加减的方法示例
Jun 09 Python
python 输出上个月的月末日期实例
Apr 11 Python
Tensorflow卷积神经网络实例
May 24 Python
解决Python获取字典dict中不存在的值时出错问题
Oct 17 Python
Python基础教程之if判断,while循环,循环嵌套
Apr 25 Python
python批量修改图片尺寸,并保存指定路径的实现方法
Jul 04 Python
windows安装TensorFlow和Keras遇到的问题及其解决方法
Jul 10 Python
Python中 CSV格式清洗与转换的实例代码
Aug 29 Python
浅谈pymysql查询语句中带有in时传递参数的问题
Jun 05 Python
python Zmail模块简介与使用示例
Dec 19 Python
python自动化八大定位元素讲解
Jul 09 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/06/07 PHP
php使用cookie保存用户登录的用户名实例
2015/01/26 PHP
又十个超级有用的PHP代码片段
2015/09/24 PHP
PHP框架Laravel学习心得体会
2015/10/28 PHP
Zend Framework教程之Zend_Db_Table用法详解
2016/03/21 PHP
php常用字符函数实例小结
2016/12/29 PHP
Yii2框架自定义类统一处理url操作示例
2019/05/25 PHP
自用js开发框架小成 学习js的朋友可以看看
2010/11/16 Javascript
AngularJS语法详解
2015/01/23 Javascript
js实现点击图片将图片地址复制到粘贴板的方法
2015/02/16 Javascript
JS中cookie的使用及缺点讲解
2017/05/13 Javascript
JavaScript条件判断_动力节点Java学院整理
2017/06/26 Javascript
JavaScript实现短暂提示框功能
2018/04/04 Javascript
r.js来合并压缩css文件的示例
2018/04/26 Javascript
vue 弹窗时 监听手机返回键关闭弹窗功能(页面不跳转)
2019/05/10 Javascript
JavaScript实现单英文金山打字通
2020/07/24 Javascript
Vuejs通过拖动改变元素宽度实现自适应
2020/09/02 Javascript
原生JavaScript实现换肤
2021/02/19 Javascript
用Python生成器实现微线程编程的教程
2015/04/13 Python
python创建关联数组(字典)的方法
2015/05/04 Python
django上传图片并生成缩略图方法示例
2017/12/11 Python
Python多进程multiprocessing.Pool类详解
2018/04/27 Python
numpy中以文本的方式存储以及读取数据方法
2018/06/04 Python
基于python实现名片管理系统
2018/11/30 Python
Python实现的登录验证系统完整案例【基于搭建的MVC框架】
2019/04/12 Python
如何通过python的fabric包完成代码上传部署
2019/07/29 Python
使用Python实现分别输出每个数组
2019/12/06 Python
解决Pycharm 运行后没有输出的问题
2021/02/05 Python
详解Django中的FBV和CBV对比分析
2021/03/01 Python
联想瑞士官方网站:Lenovo Switzerland
2017/11/19 全球购物
关于VPN
2012/06/10 面试题
党的群众路线教育实践活动对照检查材料(个人)
2014/09/24 职场文书
结婚通知短信怎么写
2015/04/17 职场文书
2016年法制宣传月活动总结
2016/04/01 职场文书
求职信:会计求职的写作技巧
2019/04/24 职场文书
Linux在两个服务器直接传文件的操作方法
2022/08/05 Servers