2015年1月27日 星期二

pygtk圖片檢視器完成了(程式下載)

算是有點水準的圖片檢視器(原碼下載)

下面是在ubuntu1410執行後樣子,OS版本不要太舊,應該都可執行

 








已經找到python file使用中文的方式,開頭給字形
# -*- Mode: Python; coding: utf-8; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- 

圖片拉大後可拉小了,要將viewport先加進捲動視窗(如下圖)


多加了檔案列表類別t_filechosser,處理檔選擇器被選取後,把檔案加入TreeView列表中,當TreeView列表被選取時,信號呼叫重新調整image大小後顯示,其實有些可以用Glade來設計,用glade會有些不滿意,乾脆這部份就不用glade,成一個CLASS,日後也可拿來用,譬如音樂播放氣

另外加強image_viewport這類別的功能,能夠保持等比例縮小或放大圖片

程式碼共有4個檔案(一個glade建立的ui檔,3個用Anjuta IDE編輯的py檔)如下

 pygtk_foobar_u1410.ui

 <?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.18.3 -->
<interface>
  <requires lib="gtk+" version="3.0"/>
  <object class="GtkFileFilter" id="filefilter1">
    <patterns>
      <pattern>*.jpg</pattern>
      <pattern>*.png</pattern>
      <pattern>*.PNG</pattern>
      <pattern>*.JPG</pattern>
    </patterns>
  </object>
  <object class="GtkListStore" id="liststore1">
    <columns>
      <!-- column-name gchararray1 -->
      <column type="gchararray"/>
    </columns>
  </object>
  <object class="GtkWindow" id="window">
    <property name="visible">True</property>
    <property name="can_focus">False</property>
    <property name="hexpand">False</property>
    <property name="vexpand">False</property>
    <property name="title" translatable="yes">圖片檢視器</property>
    <property name="window_position">center</property>
    <property name="default_width">700</property>
    <property name="default_height">502</property>
    <property name="gravity">center</property>
    <signal name="check-resize" handler="IMAGE_RESIZE" object="viewport1" swapped="no"/>
    <signal name="destroy" handler="on_window_destroy" swapped="no"/>
    <child>
      <object class="GtkPaned" id="paned1">
        <property name="visible">True</property>
        <property name="can_focus">True</property>
        <child>
          <object class="GtkBox" id="box1">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <property name="orientation">vertical</property>
            <child>
              <object class="GtkButton" id="button6">
                <property name="label" translatable="yes">  選    擇    圖    片    檔       ...  </property>
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="receives_default">True</property>
                <signal name="clicked" handler="IMAGE_VIEWER" swapped="no"/>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="fill">True</property>
                <property name="position">0</property>
              </packing>
            </child>
            <child>
              <object class="GtkButton" id="button3">
                <property name="label" translatable="yes">離開</property>
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="receives_default">True</property>
                <signal name="clicked" handler="SHOW_IMAGE" object="window" swapped="no"/>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="fill">True</property>
                <property name="pack_type">end</property>
                <property name="position">1</property>
              </packing>
            </child>
            <child>
              <object class="GtkScrolledWindow" id="scrolledwindow1">
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="shadow_type">in</property>
                <child>
                  <placeholder/>
                </child>
              </object>
              <packing>
                <property name="expand">True</property>
                <property name="fill">True</property>
                <property name="position">2</property>
              </packing>
            </child>
            <child>
              <object class="GtkButton" id="button2">
                <property name="label" translatable="yes">隱藏圖片</property>
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="receives_default">True</property>
                <signal name="clicked" handler="SHOW" object="label1" swapped="no"/>
                <signal name="clicked" handler="SHOW_IMAGE" object="image1" swapped="no"/>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="fill">True</property>
                <property name="pack_type">end</property>
                <property name="position">3</property>
              </packing>
            </child>
            <child>
              <object class="GtkLabel" id="label1">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="margin_right">4</property>
                <property name="label" translatable="yes">訊息</property>
                <property name="ellipsize">start</property>
                <property name="max_width_chars">0</property>
                <attributes>
                  <attribute name="font-desc" value="<輸入數值> 18"/>
                  <attribute name="style" value="italic"/>
                  <attribute name="weight" value="bold"/>
                  <attribute name="foreground" value="#cccc00000000"/>
                </attributes>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="fill">True</property>
                <property name="padding">20</property>
                <property name="position">4</property>
              </packing>
            </child>
            <child>
              <object class="GtkButton" id="button1">
                <property name="label" translatable="yes">顯示圖片</property>
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="receives_default">True</property>
                <property name="yalign">0.44999998807907104</property>
                <signal name="clicked" handler="SHOW" object="label1" swapped="no"/>
                <signal name="clicked" handler="SHOW_IMAGE" object="image1" swapped="no"/>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="fill">True</property>
                <property name="pack_type">end</property>
                <property name="position">5</property>
              </packing>
            </child>
            <child>
              <object class="GtkButton" id="button4">
                <property name="label" translatable="yes">縮放</property>
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="receives_default">True</property>
                <signal name="clicked" handler="SHOW" object="label1" swapped="no"/>
                <signal name="clicked" handler="SHOW_TYPE" swapped="no"/>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="fill">True</property>
                <property name="pack_type">end</property>
                <property name="position">6</property>
              </packing>
            </child>
            <child>
              <object class="GtkButton" id="button5">
                <property name="label" translatable="yes">擴展</property>
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="receives_default">True</property>
                <signal name="clicked" handler="SHOW" object="label1" swapped="no"/>
                <signal name="clicked" handler="SHOW_TYPE" swapped="no"/>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="fill">True</property>
                <property name="pack_type">end</property>
                <property name="position">7</property>
              </packing>
            </child>
          </object>
          <packing>
            <property name="resize">False</property>
            <property name="shrink">True</property>
          </packing>
        </child>
        <child>
          <object class="GtkScrolledWindow" id="scrolledwindow2">
            <property name="visible">True</property>
            <property name="can_focus">True</property>
            <property name="shadow_type">in</property>
            <child>
              <object class="GtkViewport" id="viewport1">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="events">GDK_BUTTON_MOTION_MASK | GDK_STRUCTURE_MASK | GDK_PROPERTY_CHANGE_MASK</property>
                <child>
                  <object class="GtkImage" id="image1">
                    <property name="visible">True</property>
                    <property name="can_focus">False</property>
                    <property name="halign">center</property>
                    <property name="valign">center</property>
                    <property name="stock">gtk-missing-image</property>
                  </object>
                </child>
              </object>
            </child>
          </object>
          <packing>
            <property name="resize">True</property>
            <property name="shrink">True</property>
          </packing>
        </child>
      </object>
    </child>
  </object>
</interface>


treeview_filechooser.py


#!/usr/bin/env python
# -*- Mode: Python; coding: utf-8; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*-
# treeview_filechosser.py
#
# Copyright (C) 2015 - root
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#給頂層視窗window參數,容器add_widget參數,和處理圖片的物件view參數,可加入TreeView到容器dd_widget
#call_resize方法中,參數view物件須提供方法i_image_resize,此方法只是來處理,TreeView選項被選取後會呼叫
#此treeview,提供File_open()來打開檔案圖片,選取後加入treeview的表單
#_type_若是"DELETE",選取file,加入treeview會先清空treeview內容,否則一直加上去
from gi.repository import Gtk, GdkPixbuf, Gdk
import sys,os
from gi.repository.GdkPixbuf import Pixbuf, InterpType
class t_filechosser():
 #建構式,用來初始化物件
 def __init__(self,add_widget,window,view,_type_,ifile):
        #inital data
        self._TYPE_=_type_
        self.add_tree_w=add_widget
        self.window=window
        self.image_viewport_new=view
        self.file=ifile
        #新Treeview,要先有store來存放資料
        self.store = Gtk.ListStore(str)
        self.tt=Gtk.TreeView(self.store)
        #Treeview加入
        self.add_tree_w.add(self.tt)
        #建立signal connect
        self.tt.connect('cursor-changed', self.SHOW_ITEM)
        #記得要給TreeViewColumn,否則TreeView無法顯示
        column = Gtk.TreeViewColumn("-----檔名-----", Gtk.CellRendererText(), text=0)
        column.set_sort_column_id(0)
        self.tt.append_column(column)


   
 def File_open(self):
        self.dlg = Gtk.FileChooserDialog(
                "Select Picture",
                self.window,
                Gtk.FileChooserAction.OPEN,
                (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK) )
        self.dlg.show()
        print("開啟檔案選擇器")
        self.dlg.set_default_response(Gtk.ResponseType.CANCEL)
        self.dlg. set_select_multiple(True)


        #可用glade加檔案過濾器
        #self.dlg.add_filter(self.filefilter1)
        #加檔案過濾器'*.jpg','*png','*PNG','*.JPG'   
        lfilter = Gtk.FileFilter()
        lfilter.set_name('Picture')
        flist=list(('*.jpg','*png','*PNG','*.JPG'))
        for fl in flist:
         lfilter.add_pattern(fl)
         print(fl)
        self.dlg.add_filter(lfilter)
        self.dlg.set_filter(lfilter)
        #設定filechosserdialog按鈕事件,按OK加入list,否則destroy
          response = self.dlg.run()
          if response == Gtk.ResponseType.OK:
         if self._TYPE_=="DELETE":self.store.clear()
         #取得選取file list 
         lfile=self.dlg.get_filenames()
           #print(lfile[0])
           #加入treeview list
           for x in lfile:
           #self.liststore1.append([x])
           self.store.append([x])
           #self.image_viewport_new.i_image_resize(lfile[0])
           self.tt.set_cursor(0)
       
         self.dlg.destroy()
          elif response == Gtk.ResponseType.CANCEL:
              print("Cancel clicked")
        #注意最後要destroy,讓使用者按左上x能關閉 
          self.dlg.destroy()                                                 
       
    #連結信號,設定顯示的樣式
 def SHOW_ITEM(self, widget,*event):
        try :
         sel=self.tt.get_selection()
         (model,iter)=sel.get_selected()
         self.file=list(model[iter])[0]
         #取得image_viewport_new物件的i_image_resize來重設圖片大小
         #self.image_viewport_new.i_image_resize(self.file)
         self.call_resize()
         print(list(model[iter])[0])
        except:pass
 def call_resize(self):
     try :
        self.image_viewport_new.i_image_resize(self.file)
     except:print("請給一個物件的方法i_image_resize,來處理TreeView選項被選取")



size_image.py



#!/usr/bin/env python
# -*- Mode: Python; coding: utf-8; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*-
#image_viewport類別,是用來重設圖片大小
#給一個image參數, viewport參數,i_type參數,產生物件,此物件提供調整圖片大小的i_image_resize方法
#image是顯示該圖片的widget
#viewport是放image的容器
#i_type給任何字串,圖片會調到和viewprt一樣大,若設定"縮放",照圖片等比例調整
#i_image_resize(self,filepath)給圖片filepath,重設圖片大小
#is_viewpot_change(self,widget)給viewport,判定viewport是否改變
#為什要判定viewport是否改變,譬如給check-resize信號用來判斷,當使用者拉大視窗一下,
#連結調整圖片,載入圖片時,會觸發check-resizecheck-resize又會呼叫調整圖片,變成迴圈

from gi.repository import Gtk, GdkPixbuf, Gdk
import sys,os
from gi.repository.GdkPixbuf import Pixbuf, InterpType
class image_viewport():
 #建構式,用來初始化物件
 def __init__(self,image, viewport,i_type):
        #inital data
        self.w_size=0
        self.h_size=0
        self.image = image
        self.viewport = viewport
        self.i_type=i_type

 #定義了一些取得設定物件成員值
 def i_get_viewport_h_size(self):
       return self.h_size
 def i_get_viewport_w_size(self):
       return self.w_size
 def i_set_show_type(self,i_type):
       self.i_type= i_type
 def i_get_show_type(self):
       return self.i_type
 def is_viewpot_change(self,widget):
        #目前viewport size
        boolean=False
        w_size=widget.get_allocation().width
        h_size=widget.get_allocation().height       
        #檢查viewport是否改變,若是回傳值True
        if(self.w_size!=w_size or self.h_size!=h_size ):
           boolean=True
        return boolean   

 #定義重設視窗的方法      
 def i_image_resize(self,filepath):
        self.h_size=self.viewport.get_allocation().height
        self.w_size=self.viewport.get_allocation().width
        #從filepath產生圖片buffer
        pixbuf = Pixbuf.new_from_file(filepath)
        #orange image size
        iw=pixbuf.get_width()
        ih=pixbuf.get_height()
        #參數先轉成float
        w,h=change_width_height(self,float(iw),float(ih),float(self.w_size),float(self.h_size))
        print("width" ,w)
        print("height",h)
       
        #重設圖片buffer寬高
        pixbuf = pixbuf.scale_simple(w, h, InterpType.BILINEAR)
        #把圖片buffer放到image widget
        self.image.set_from_pixbuf(pixbuf)
       

#定義方法,用來計算圖片新的寬高,並回傳寬和高
def change_width_height(self,ix,iy,vx,vy):

      #預設回傳viewport寬和高
      width=vx
      height=vy
      cy=iy/vy
      cx=ix/vx
      #print(cy)
      #print(self.i_type)

      #判斷image與viewport比例,顯示若是EXTEND形式,設定新的寬和高
      if cx >= cy and self.i_type=="縮放":
      
       try:
        #new size
        width=ix/cx
        height=iy/cx
       except:pass
      if cy >= cx and self.i_type=="縮放":
       try:
        #new size
        width=ix/cy
        height=iy/cy
       except:pass
      
      return width,height
      #結束def change_width_height end



 主程式pygtk_foobar_u1410.py









#!/usr/bin/env python
# -*- Mode: Python; coding: utf-8; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*-
#
# main.py
# Copyright (C) 2015 yplin
#
# pygtk-foobar_u1410 is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# pygtk-foobar_u1410 is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program.  If not, see .

from gi.repository import Gtk, GdkPixbuf, Gdk

import os, sys ,size_image,treeview_filechooser
import glob
from gi.repository.GdkPixbuf import Pixbuf, InterpType

#Comment the first line and uncomment the second before installing
#or making the tarball (alternatively, use project variables)
UI_FILE = "src/pygtk_foobar_u1410.ui"
#UI_FILE = "/usr/local/share/pygtk_foobar_u1410/ui/pygtk_foobar_u1410.ui"


class GUI:
    def __init__(self):
        #新建了一個xml-gtk
        self.builder = Gtk.Builder()
        #把UI_FILE加到gtk,glade產生的xml
        self.builder.add_from_file(UI_FILE)
        #建立xml信號連結
        self.builder.connect_signals(self)
        #取得xml UI上所有元件
        self.file="src/logo.png"
        self.test_w=0
        self.test_h=0
        self.label=self.builder.get_object('label1')
        self.button2=self.builder.get_object('button2')
        self.button1=self.builder.get_object('button1')
        self.button3=self.builder.get_object('button3')
        self.window=self.builder.get_object('window')
        self.fixed=self.builder.get_object('fixed1')
        self.image=self.builder.get_object('image1')
        self.viewport=self.builder.get_object('viewport1')
        self.treeview=self.builder.get_object('treeview1')
        self.liststore1=self.builder.get_object('liststore1')
        self.filefilter1=self.builder.get_object('filefilter1')
        self.scrolledwindow1=self.builder.get_object('scrolledwindow1')
        self.image.set_from_file(self.file)
        #self.dlg=self.builder.get_object('filechooserdialog1')
       

        #初始化
       
        #self.treeview.append_column(column)
        #初始化,產生自定義image_viewport ,t_filechosser CLASS
        self.image_viewport_new=size_image.image_viewport(self.image,self.viewport,"擴展")
        self.t_filechosser=treeview_filechooser.t_filechosser(self.scrolledwindow1,self.window,self.image_viewport_new,"DELETE",self.file)
       
    def on_window_destroy(self, widget, *event):
        Gtk.main_quit()
    def on_window_destroy1(self, widget, *event):
        print("HHHHHHHHHHH")


   
   
    #button click 連結信號   
    def SHOW(self, widget, *event):
        print("DDDD")
        #self.label.set_text(self.button2.get_label())
        try:
         #get_focus可取得激活對象
         bt_active_label=self.window.get_focus().get_label()
         #widget是指信號處理對象,用GALADE創建UI時,是為label1
         widget.set_text(bt_active_label)
        except:pass
        #除錯技巧,印出
        #重設label位置
        #self.fixed.put(self.label, 80, 40)


       
       
    #button click 連結信號       
    def SHOW_IMAGE(self, widget, *event):
        #取得激活對象按鈕名稱
        bt_active_label=self.window.get_focus().get_label()
        #根據按鈕名稱,顯示或隱藏圖片
        if bt_active_label=='隱藏圖片':widget.hide()
        if bt_active_label=='顯示圖片':widget.show()
        if bt_active_label=="離開":Gtk.main_quit()
       

    #Window chec-resize連結信號,重設圖片大小
    def IMAGE_RESIZE(self, widget, *event):
        #目前viewport size
        #w_size=self.viewport.get_allocation().width
        #h_size=self.viewport.get_allocation().height
        #存在記憶體viewport size
        #wv=self.image_viewport_new.i_get_viewport_w_size()
        #hv=self.image_viewport_new.i_get_viewport_h_size()

       
        #檢查viewport是否改變,重設size,否則成為無窮迴圈
        if self.image_viewport_new.is_viewpot_change(widget) :
           #print(str(h_size))
           #resize方法,來重設大小方式
           self.t_filechosser.call_resize()

    #連結信號,設定顯示的樣式
    def SHOW_TYPE(self, *event):
        b_label=self.window.get_focus().get_label()
        self.image_viewport_new.i_set_show_type(b_label)
        #resize方法,重設大小方式
        self.t_filechosser.call_resize()
       
        print(0 > 3)
       
    def IMAGE_VIEWER(self, *event):
       
        self.t_filechosser.File_open()
       
       
def main():
    app = GUI()
    app.window.show_all()
    Gtk.main()
   
   
if __name__ == "__main__":
    sys.exit(main())

     

沒有留言: