使用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爬虫利用cookie实现模拟登陆实例详解
Jan 12 Python
Python3 模块、包调用&amp;路径详解
Oct 25 Python
python安装numpy&amp;安装matplotlib&amp; scipy的教程
Nov 02 Python
Python实现PS滤镜碎片特效功能示例
Jan 24 Python
理想高通滤波实现Python opencv示例
Jan 30 Python
Python人工智能之路 之PyAudio 实现录音 自动化交互实现问答
Aug 13 Python
关于python3中setup.py小概念解析
Aug 22 Python
Python根据服务获取端口号的方法
Sep 25 Python
Python实现大数据收集至excel的思路详解
Jan 03 Python
tensorflow常用函数API介绍
Apr 19 Python
python redis存入字典序列化存储教程
Jul 16 Python
python 多态 协议 鸭子类型详解
Nov 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实现按照权重随机排序数据的方法
2015/01/09 PHP
PHP中的闭包(匿名函数)浅析
2015/02/07 PHP
CI(CodeIgniter)框架视图中加载视图的方法
2017/03/24 PHP
PHP 7.4中使用预加载的方法详解
2019/07/08 PHP
创建一个复制UBB软件信息的链接或按钮的js代码
2008/01/06 Javascript
Javascript 实用小技巧
2010/04/07 Javascript
JavaScrip单线程引擎工作原理分析
2010/09/04 Javascript
浅谈Javascript事件模拟
2012/06/27 Javascript
JavaScript Promise启示录
2014/08/12 Javascript
Javascript中call与apply的学习笔记
2014/09/22 Javascript
jquery调取json数据实现省市级联的方法
2015/01/29 Javascript
JavaScript事件 &quot;事件对象&quot;的注意要点
2016/01/14 Javascript
Laravel中常见的错误与解决方法小结
2016/08/30 Javascript
Vue表单实例代码
2016/09/05 Javascript
webpack-dev-server自动更新页面方法
2018/02/22 Javascript
详解在React项目中安装并使用Less(用法总结)
2019/03/18 Javascript
vue实现图片懒加载的方法分析
2020/02/05 Javascript
js实现单元格拖拽效果
2020/02/10 Javascript
JS获取表格视图所选行号的ids过程解析
2020/02/21 Javascript
详解Vue.js3.0 组件是如何渲染为DOM的
2020/11/10 Javascript
python引用DLL文件的方法
2015/05/11 Python
python操作字典类型的常用方法(推荐)
2016/05/16 Python
Python冒泡排序注意要点实例详解
2016/09/09 Python
CentOS 7下Python 2.7升级至Python3.6.1的实战教程
2017/07/06 Python
Python简单计算数组元素平均值的方法示例
2017/12/26 Python
python 请求服务器的实现代码(http请求和https请求)
2018/05/25 Python
python UDP(udp)协议发送和接收的实例
2019/07/22 Python
CSS3 text shadow字体阴影效果
2016/01/08 HTML / CSS
使用CSS3 制作一个material-design 风格登录界面实例
2016/12/12 HTML / CSS
安全生产承诺书
2014/03/26 职场文书
小学生纪念九一八事变演讲稿
2014/09/14 职场文书
四风对照检查材料思想汇报
2014/09/20 职场文书
股东大会通知
2015/04/24 职场文书
加班费申请报告
2015/05/15 职场文书
母亲去世追悼词
2015/06/23 职场文书
导游词之河北邯郸
2019/09/12 职场文书