mirror of
https://github.com/wxWidgets/Phoenix.git
synced 2025-12-16 09:40:07 +01:00
In Python 3.10, a change[1] was implemented where extension functions
that take integer arguments will no longer silently accept non-integer
arguments (e.g., floats) that can only be converted to integers with a
loss of precision. This PR fixes most of these issues in the pure-Python
classes and demos by explicitly converting the parameters to int before
passing them to wxWidgets. There is loss of precision, but this was
happening before (automatically) anyway as most wxWidgets DeviceContext
functions operate using integers.
Additionally, the PR fixes a few sizing issues, mostly with SpinCtrls being
too small on GTK3.
This is an example of the relevant exception:
Traceback (most recent call last):
File "/usr/lib64/python3.10/site-packages/wx/lib/agw/pygauge.py", line 355, in OnPaint
r.width = w
TypeError: 'float' object cannot be interpreted as an integer
Fixes #2038.
[1] https://bugs.python.org/issue37999
854 lines
27 KiB
Python
854 lines
27 KiB
Python
#----------------------------------------------------------------------------
|
|
# Name: BrowseImage.py
|
|
# Purpose: Display and Select Image Files
|
|
#
|
|
# Original Author: Lorne White
|
|
#
|
|
# Version: 2.0
|
|
# Date: June 16, 2007
|
|
# Licence: wxWindows license
|
|
# Tags: phoenix-port
|
|
#----------------------------------------------------------------------------
|
|
# 2.0 Release - Bill Baxter (wbaxter@gmail.com)
|
|
# Date: June 16, 2007
|
|
# o Changed to use sizers instead of fixed placement.
|
|
# o Made dialog resizeable
|
|
# o Added a splitter between file list and view pane
|
|
# o Made directory path editable
|
|
# o Added an "up" button" to go to the parent dir
|
|
# o Changed to show directories in file list
|
|
# o Don't select images on double click any more
|
|
# o Added a 'broken image' display for files that wx fails to identify
|
|
# o Redesigned appearance -- using bitmap buttons now, and rearranged things
|
|
# o Fixed display of masked gifs
|
|
# o Fixed zooming logic to show shrunken images at correct aspect ratio
|
|
# o Added different background modes for preview (white/grey/dark/checkered)
|
|
# o Added framing modes for preview (no frame/box frame/tinted border)
|
|
#
|
|
#----------------------------------------------------------------------------
|
|
#
|
|
# 12/08/2003 - Jeff Grimmett (grimmtooth@softhome.net)
|
|
#
|
|
# o Updated for wx namespace
|
|
# o Corrected a nasty bug or two - see comments below.
|
|
# o There was a duplicate ImageView.DrawImage() method. Que?
|
|
#
|
|
#----------------------------------------------------------------------------
|
|
# 1.0 Release - Lorne White
|
|
# Date: January 29, 2002
|
|
# Create list of all available image file types
|
|
# View "All Image" File Types as default filter
|
|
# Sort the file list
|
|
# Use newer "re" function for patterns
|
|
#
|
|
# Tags: phoenix-port, documented, unittest
|
|
#---------------------------------------------------------------------------
|
|
"""
|
|
This module provides the :class:`~lib.imagebrowser.ImageDialog` which allows to view and select
|
|
an image.
|
|
|
|
|
|
Description
|
|
===========
|
|
|
|
The :class:`wx.ImageDialog` allows the user to view images and select one.
|
|
|
|
Usage
|
|
=====
|
|
|
|
A simple usage would be::
|
|
|
|
import wx
|
|
import wx.lib.mixins.inspection as wit
|
|
import wx.lib.imagebrowser as ib
|
|
|
|
app = wit.InspectableApp()
|
|
|
|
with ib.ImageDialog(None) as dlg:
|
|
if dlg.ShowModal() == wx.ID_OK:
|
|
# show the selected file
|
|
print("You Selected File: " + dlg.GetFile())
|
|
else:
|
|
print("You pressed Cancel")
|
|
|
|
app.MainLoop()
|
|
|
|
"""
|
|
|
|
import os
|
|
import wx
|
|
|
|
if 'wxOSX' in wx.PlatformInfo:
|
|
# The wx.BitmapButton doesn't do so well on OSX with very small bitmaps,
|
|
# so use a custom class there.
|
|
import wx.lib.buttons
|
|
class BitmapButton(wx.lib.buttons.GenBitmapButton):
|
|
def __init__(self, *args, **kw):
|
|
super(BitmapButton, self).__init__(*args, **kw)
|
|
self.SetBezelWidth(1)
|
|
self.SetInitialSize()
|
|
print(self.GetSize(), self.GetBestSize())
|
|
|
|
else:
|
|
BitmapButton = wx.BitmapButton
|
|
|
|
|
|
_ = wx.GetTranslation
|
|
|
|
#---------------------------------------------------------------------------
|
|
|
|
BAD_IMAGE = -1
|
|
ID_WHITE_BG = wx.NewIdRef()
|
|
ID_BLACK_BG = wx.NewIdRef()
|
|
ID_GREY_BG = wx.NewIdRef()
|
|
ID_CHECK_BG = wx.NewIdRef()
|
|
ID_NO_FRAME = wx.NewIdRef()
|
|
ID_BOX_FRAME = wx.NewIdRef()
|
|
ID_CROP_FRAME = wx.NewIdRef()
|
|
|
|
def ConvertBMP(file_nm):
|
|
"""
|
|
Convert file
|
|
|
|
:param string `file_nm`: path to file
|
|
|
|
:return: :class:`wx.Image` or BAD_IMAGE
|
|
"""
|
|
if file_nm is None:
|
|
return None
|
|
|
|
fl_fld = os.path.splitext(file_nm)
|
|
ext = fl_fld[1]
|
|
ext = ext[1:].lower()
|
|
|
|
# Don't try to create it directly because wx throws up
|
|
# an annoying messasge dialog if the type isn't supported.
|
|
if wx.Image.CanRead(file_nm):
|
|
image = wx.Image(file_nm, wx.BITMAP_TYPE_ANY)
|
|
return image
|
|
|
|
# BAD_IMAGE means a bad image, None just means no image (i.e. directory)
|
|
return BAD_IMAGE
|
|
|
|
|
|
def GetCheckeredBitmap(blocksize=8, ntiles=4, rgb0=b'\xFF', rgb1=b'\xCC'):
|
|
"""
|
|
Creates a square RGB checkered bitmap using the two specified colors.
|
|
|
|
The bitmap returned will have width = height = blocksize*ntiles*2
|
|
|
|
:param int `blocksize`: the number of pixels in each solid color square
|
|
:param int `ntiles1`: the number of tiles along width and height. Each
|
|
tile is 2x2 blocks.
|
|
:param `rbg0`: the first color, as 3-character bytes object.
|
|
:param `rgb1`: the second color, as 3-character bytes object. If only 1
|
|
character is provided, it is treated as a grey value.
|
|
|
|
:return: :class:`wx.Bitmap`
|
|
|
|
"""
|
|
assert isinstance(rgb0, bytes)
|
|
assert isinstance(rgb1, bytes)
|
|
|
|
size = blocksize*ntiles*2
|
|
|
|
if len(rgb0)==1:
|
|
rgb0 = rgb0 * 3
|
|
if len(rgb1)==1:
|
|
rgb1 = rgb1 * 3
|
|
|
|
strip0 = (rgb0*blocksize + rgb1*blocksize)*(ntiles*blocksize)
|
|
strip1 = (rgb1*blocksize + rgb0*blocksize)*(ntiles*blocksize)
|
|
band = strip0 + strip1
|
|
data = band * ntiles
|
|
return wx.Bitmap.FromBuffer(size, size, data)
|
|
|
|
def GetNamedBitmap(name):
|
|
return IMG_CATALOG[name].GetBitmap()
|
|
|
|
|
|
class ImageView(wx.Window):
|
|
def __init__(self, parent, id=-1, pos=wx.DefaultPosition, size=wx.DefaultSize,
|
|
style=wx.BORDER_SUNKEN
|
|
):
|
|
wx.Window.__init__(self, parent, id, pos, size, style=style)
|
|
|
|
self.image = None
|
|
|
|
self.check_bmp = None
|
|
self.check_dim_bmp = None
|
|
|
|
# dark_bg is the brush/bitmap to use for painting in the whole background
|
|
# lite_bg is the brush/bitmap/pen to use for painting the image rectangle
|
|
self.dark_bg = None
|
|
self.lite_bg = None
|
|
|
|
self.border_mode = ID_CROP_FRAME
|
|
self.SetBackgroundMode( ID_WHITE_BG )
|
|
self.SetBorderMode( ID_NO_FRAME )
|
|
|
|
# Changed API of wx uses tuples for size and pos now.
|
|
self.Bind(wx.EVT_PAINT, self.OnPaint)
|
|
self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
|
|
self.Bind(wx.EVT_SIZE, self.OnSize)
|
|
|
|
|
|
def SetValue(self, file_nm): # display the selected file in the panel
|
|
image = ConvertBMP(file_nm)
|
|
self.image = image
|
|
self.Refresh()
|
|
|
|
def SetBackgroundMode(self, mode):
|
|
self.bg_mode = mode
|
|
self._updateBGInfo()
|
|
|
|
def _updateBGInfo(self):
|
|
bg = self.bg_mode
|
|
border = self.border_mode
|
|
|
|
self.dark_bg = None
|
|
self.lite_bg = None
|
|
|
|
if border == ID_BOX_FRAME:
|
|
self.lite_bg = wx.BLACK_PEN
|
|
|
|
if bg == ID_WHITE_BG:
|
|
if border == ID_CROP_FRAME:
|
|
self.SetBackgroundColour('LIGHT GREY')
|
|
self.lite_bg = wx.WHITE_BRUSH
|
|
else:
|
|
self.SetBackgroundColour('WHITE')
|
|
|
|
elif bg == ID_GREY_BG:
|
|
if border == ID_CROP_FRAME:
|
|
self.SetBackgroundColour('GREY')
|
|
self.lite_bg = wx.LIGHT_GREY_BRUSH
|
|
else:
|
|
self.SetBackgroundColour('LIGHT GREY')
|
|
|
|
elif bg == ID_BLACK_BG:
|
|
if border == ID_BOX_FRAME:
|
|
self.lite_bg = wx.WHITE_PEN
|
|
if border == ID_CROP_FRAME:
|
|
self.SetBackgroundColour('GREY')
|
|
self.lite_bg = wx.BLACK_BRUSH
|
|
else:
|
|
self.SetBackgroundColour('BLACK')
|
|
|
|
else:
|
|
if self.check_bmp is None:
|
|
self.check_bmp = GetCheckeredBitmap()
|
|
self.check_dim_bmp = GetCheckeredBitmap(rgb0=b'\x7F', rgb1=b'\x66')
|
|
if border == ID_CROP_FRAME:
|
|
self.dark_bg = self.check_dim_bmp
|
|
self.lite_bg = self.check_bmp
|
|
else:
|
|
self.dark_bg = self.check_bmp
|
|
|
|
self.Refresh()
|
|
|
|
def SetBorderMode(self, mode):
|
|
self.border_mode = mode
|
|
self._updateBGInfo()
|
|
|
|
def OnSize(self, event):
|
|
event.Skip()
|
|
self.Refresh()
|
|
|
|
def OnPaint(self, event):
|
|
dc = wx.PaintDC(self)
|
|
self.DrawImage(dc)
|
|
|
|
def OnEraseBackground(self, evt):
|
|
if self.bg_mode != ID_CHECK_BG:
|
|
evt.Skip()
|
|
return
|
|
dc = evt.GetDC()
|
|
if dc:
|
|
self.PaintBackground(dc, self.dark_bg)
|
|
|
|
def PaintBackground(self, dc, painter, rect=None):
|
|
if painter is None:
|
|
return
|
|
if rect is None:
|
|
pos = self.GetPosition()
|
|
sz = self.GetSize()
|
|
else:
|
|
pos = rect.Position
|
|
sz = rect.Size
|
|
|
|
if type(painter)==wx.Brush:
|
|
dc.SetPen(wx.TRANSPARENT_PEN)
|
|
dc.SetBrush(painter)
|
|
dc.DrawRectangle(pos.x,pos.y,sz.width,sz.height)
|
|
elif type(painter)==wx.Pen:
|
|
dc.SetPen(painter)
|
|
dc.SetBrush(wx.TRANSPARENT_BRUSH)
|
|
dc.DrawRectangle(pos.x-1,pos.y-1,sz.width+2,sz.height+2)
|
|
else:
|
|
self.TileBackground(dc, painter, pos.x,pos.y,sz.width,sz.height)
|
|
|
|
|
|
def TileBackground(self, dc, bmp, x,y,w,h):
|
|
"""Tile bmp into the specified rectangle"""
|
|
bw = bmp.GetWidth()
|
|
bh = bmp.GetHeight()
|
|
|
|
dc.SetClippingRegion(x,y,w,h)
|
|
|
|
# adjust so 0,0 so we always match with a tiling starting at 0,0
|
|
dx = x % bw
|
|
x = x - dx
|
|
w = w + dx
|
|
|
|
dy = y % bh
|
|
y = y - dy
|
|
h = h + dy
|
|
|
|
tx = x
|
|
x2 = x+w
|
|
y2 = y+h
|
|
|
|
while tx < x2:
|
|
ty = y
|
|
while ty < y2:
|
|
dc.DrawBitmap(bmp, tx, ty)
|
|
ty += bh
|
|
tx += bw
|
|
|
|
def DrawImage(self, dc):
|
|
|
|
if not hasattr(self,'image') or self.image is None:
|
|
return
|
|
|
|
wwidth,wheight = self.GetSize()
|
|
image = self.image
|
|
bmp = None
|
|
if image != BAD_IMAGE and image.IsOk():
|
|
iwidth = image.GetWidth() # dimensions of image file
|
|
iheight = image.GetHeight()
|
|
else:
|
|
bmp = wx.ArtProvider.GetBitmap(wx.ART_MISSING_IMAGE, wx.ART_MESSAGE_BOX, (64,64))
|
|
iwidth = bmp.GetWidth()
|
|
iheight = bmp.GetHeight()
|
|
|
|
# squeeze iwidth x iheight image into window, preserving aspect ratio
|
|
|
|
xfactor = float(wwidth) / iwidth
|
|
yfactor = float(wheight) / iheight
|
|
|
|
if xfactor < 1.0 and xfactor < yfactor:
|
|
scale = xfactor
|
|
elif yfactor < 1.0 and yfactor < xfactor:
|
|
scale = yfactor
|
|
else:
|
|
scale = 1.0
|
|
|
|
owidth = int(scale*iwidth)
|
|
oheight = int(scale*iheight)
|
|
|
|
diffx = (wwidth - owidth)//2 # center calc
|
|
diffy = (wheight - oheight)//2 # center calc
|
|
|
|
if not bmp:
|
|
if owidth!=iwidth or oheight!=iheight:
|
|
sc_image = sc_image = image.Scale(owidth,oheight)
|
|
else:
|
|
sc_image = image
|
|
bmp = sc_image.ConvertToBitmap()
|
|
|
|
if image != BAD_IMAGE and image.IsOk():
|
|
self.PaintBackground(dc, self.lite_bg, wx.Rect(diffx,diffy,owidth,oheight))
|
|
|
|
dc.DrawBitmap(bmp, diffx, diffy, useMask=True) # draw the image to window
|
|
|
|
|
|
class ImagePanel(wx.Panel):
|
|
def __init__(self, parent, id=-1, pos=wx.DefaultPosition, size=wx.DefaultSize,
|
|
style=wx.NO_BORDER
|
|
):
|
|
wx.Panel.__init__(self, parent, id, pos, size, style=style)
|
|
|
|
vbox = wx.BoxSizer(wx.VERTICAL)
|
|
self.SetSizer(vbox)
|
|
|
|
self.view = ImageView(self)
|
|
vbox.Add(self.view, 1, wx.GROW|wx.ALL, 0)
|
|
|
|
hbox_ctrls = wx.BoxSizer(wx.HORIZONTAL)
|
|
vbox.Add(hbox_ctrls, 0, wx.ALIGN_RIGHT|wx.TOP, 4)
|
|
|
|
bmp = GetNamedBitmap('White')
|
|
btn = BitmapButton(self, ID_WHITE_BG, bmp, style=wx.BU_EXACTFIT)
|
|
self.Bind(wx.EVT_BUTTON, self.OnSetImgBackground, btn)
|
|
btn.SetToolTip(_("Set background to white"))
|
|
hbox_ctrls.Add(btn, 0, wx.ALIGN_LEFT|wx.LEFT, 4)
|
|
|
|
bmp = GetNamedBitmap('Grey')
|
|
btn = BitmapButton(self, ID_GREY_BG, bmp, style=wx.BU_EXACTFIT)
|
|
self.Bind(wx.EVT_BUTTON, self.OnSetImgBackground, btn)
|
|
btn.SetToolTip(_("Set background to grey"))
|
|
hbox_ctrls.Add(btn, 0, wx.ALIGN_LEFT|wx.LEFT, 4)
|
|
|
|
bmp = GetNamedBitmap('Black')
|
|
btn = BitmapButton(self, ID_BLACK_BG, bmp, style=wx.BU_EXACTFIT)
|
|
self.Bind(wx.EVT_BUTTON, self.OnSetImgBackground, btn)
|
|
btn.SetToolTip(_("Set background to black"))
|
|
hbox_ctrls.Add(btn, 0, wx.ALIGN_LEFT|wx.LEFT, 4)
|
|
|
|
bmp = GetNamedBitmap('Checked')
|
|
btn = BitmapButton(self, ID_CHECK_BG, bmp, style=wx.BU_EXACTFIT)
|
|
self.Bind(wx.EVT_BUTTON, self.OnSetImgBackground, btn)
|
|
btn.SetToolTip(_("Set background to checkered pattern"))
|
|
hbox_ctrls.Add(btn, 0, wx.ALIGN_LEFT|wx.LEFT, 4)
|
|
|
|
|
|
hbox_ctrls.AddSpacer(7)
|
|
|
|
bmp = GetNamedBitmap('NoFrame')
|
|
btn = BitmapButton(self, ID_NO_FRAME, bmp, style=wx.BU_EXACTFIT)
|
|
self.Bind(wx.EVT_BUTTON, self.OnSetBorderMode, btn)
|
|
btn.SetToolTip(_("No framing around image"))
|
|
hbox_ctrls.Add(btn, 0, wx.ALIGN_LEFT|wx.LEFT, 4)
|
|
|
|
bmp = GetNamedBitmap('BoxFrame')
|
|
btn = BitmapButton(self, ID_BOX_FRAME, bmp, style=wx.BU_EXACTFIT)
|
|
self.Bind(wx.EVT_BUTTON, self.OnSetBorderMode, btn)
|
|
btn.SetToolTip(_("Frame image with a box"))
|
|
hbox_ctrls.Add(btn, 0, wx.ALIGN_LEFT|wx.LEFT, 4)
|
|
|
|
bmp = GetNamedBitmap('CropFrame')
|
|
btn = BitmapButton(self, ID_CROP_FRAME, bmp, style=wx.BU_EXACTFIT)
|
|
self.Bind(wx.EVT_BUTTON, self.OnSetBorderMode, btn)
|
|
btn.SetToolTip(_("Frame image with a dimmed background"))
|
|
hbox_ctrls.Add(btn, 0, wx.ALIGN_LEFT|wx.LEFT, 4)
|
|
|
|
|
|
def SetValue(self, file_nm): # display the selected file in the panel
|
|
self.view.SetValue(file_nm)
|
|
|
|
def SetBackgroundMode(self, mode):
|
|
self.view.SetBackgroundMode(mode)
|
|
|
|
def SetBorderMode(self, mode):
|
|
self.view.SetBorderMode(mode)
|
|
|
|
def OnSetImgBackground(self, event):
|
|
mode = event.GetId()
|
|
self.SetBackgroundMode(mode)
|
|
|
|
def OnSetBorderMode(self, event):
|
|
mode = event.GetId()
|
|
self.SetBorderMode(mode)
|
|
|
|
|
|
|
|
class ImageDialog(wx.Dialog):
|
|
"""
|
|
:class:`wx.ImageDialog` derived from :class:`Dialog` allows the user
|
|
to display images and to select an image.
|
|
"""
|
|
def __init__(self, parent, set_dir = None):
|
|
"""
|
|
Default class constructor.
|
|
|
|
:param wx.Window `parent`: parent window.
|
|
:param string `set_dir`: path to set as working directory
|
|
|
|
"""
|
|
wx.Dialog.__init__(self, parent, -1, _("Image Browser"),
|
|
wx.DefaultPosition, (400, 400),
|
|
style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
|
|
|
|
self.set_dir = os.getcwd()
|
|
self.set_file = None
|
|
|
|
if set_dir is not None:
|
|
if os.path.exists(set_dir): # set to working directory if nothing set
|
|
self.set_dir = set_dir
|
|
|
|
vbox_top = wx.BoxSizer(wx.VERTICAL)
|
|
self.SetSizer(vbox_top)
|
|
|
|
hbox_loc = wx.BoxSizer(wx.HORIZONTAL)
|
|
vbox_top.Add(hbox_loc, 0, wx.GROW|wx.ALIGN_LEFT|wx.ALL, 0)
|
|
|
|
loc_label = wx.StaticText( self, -1, "Folder:")
|
|
hbox_loc.Add(loc_label, 0, wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL|wx.ALL|wx.ADJUST_MINSIZE, 5)
|
|
|
|
self.dir = wx.TextCtrl( self, -1, self.set_dir, style=wx.TE_RICH|wx.TE_PROCESS_ENTER)
|
|
self.Bind(wx.EVT_TEXT_ENTER, self.OnDirectoryTextSet, self.dir)
|
|
hbox_loc.Add(self.dir, 1, wx.GROW|wx.ALIGN_LEFT|wx.ALL, 5)
|
|
|
|
up_bmp = wx.ArtProvider.GetBitmap(wx.ART_GO_DIR_UP, wx.ART_BUTTON, (16,16))
|
|
btn = BitmapButton(self, -1, up_bmp, style=wx.BU_EXACTFIT)
|
|
btn.SetHelpText(_("Up one level"))
|
|
btn.SetToolTip(_("Up one level"))
|
|
self.Bind(wx.EVT_BUTTON, self.OnUpDirectory, btn)
|
|
hbox_loc.Add(btn, 0, wx.ALIGN_CENTER_VERTICAL|wx.RIGHT, 2)
|
|
|
|
folder_bmp = wx.ArtProvider.GetBitmap(wx.ART_FOLDER_OPEN, wx.ART_BUTTON, (16,16))
|
|
btn = BitmapButton(self, -1, folder_bmp, style=wx.BU_EXACTFIT)
|
|
btn.SetHelpText(_("Browse for a &folder..."))
|
|
btn.SetToolTip(_("Browse for a folder..."))
|
|
self.Bind(wx.EVT_BUTTON, self.OnChooseDirectory, btn)
|
|
hbox_loc.Add(btn, 0, wx.ALIGN_CENTER_VERTICAL|wx.RIGHT, 5)
|
|
|
|
hbox_nav = wx.BoxSizer(wx.HORIZONTAL)
|
|
vbox_top.Add(hbox_nav, 0, wx.ALIGN_LEFT|wx.ALL, 0)
|
|
|
|
|
|
label = wx.StaticText( self, -1, "Files of type:")
|
|
hbox_nav.Add(label, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT, 5)
|
|
|
|
self.fl_ext = '*.bmp' # initial setting for file filtering
|
|
self.GetFiles() # get the file list
|
|
|
|
self.fl_ext_types = (
|
|
# display, filter
|
|
("All supported formats", "All"),
|
|
("BMP (*.bmp)", "*.bmp"),
|
|
("GIF (*.gif)", "*.gif"),
|
|
("PNG (*.png)", "*.png"),
|
|
("JPEG (*.jpg)", "*.jpg"),
|
|
("ICO (*.ico)", "*.ico"),
|
|
("PNM (*.pnm)", "*.pnm"),
|
|
("PCX (*.pcx)", "*.pcx"),
|
|
("TIFF (*.tif)", "*.tif"),
|
|
("All Files", "*.*"),
|
|
)
|
|
self.set_type,self.fl_ext = self.fl_ext_types[0] # initial file filter setting
|
|
self.fl_types = [ x[0] for x in self.fl_ext_types ]
|
|
self.sel_type = wx.ComboBox( self, -1, self.set_type,
|
|
wx.DefaultPosition, wx.DefaultSize, self.fl_types,
|
|
wx.CB_DROPDOWN )
|
|
# after this we don't care about the order any more
|
|
self.fl_ext_types = dict(self.fl_ext_types)
|
|
|
|
self.Bind(wx.EVT_COMBOBOX, self.OnSetType, self.sel_type)
|
|
hbox_nav.Add(self.sel_type, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5)
|
|
|
|
splitter = wx.SplitterWindow( self, -1, wx.DefaultPosition, wx.Size(100, 100), 0 )
|
|
splitter.SetMinimumPaneSize(100)
|
|
|
|
split_left = wx.Panel( splitter, -1, wx.DefaultPosition, wx.DefaultSize,
|
|
wx.NO_BORDER|wx.TAB_TRAVERSAL )
|
|
vbox_left = wx.BoxSizer(wx.VERTICAL)
|
|
split_left.SetSizer(vbox_left)
|
|
|
|
|
|
self.tb = tb = wx.ListBox( split_left, -1, wx.DefaultPosition, wx.DefaultSize,
|
|
self.fl_list, wx.LB_SINGLE )
|
|
self.Bind(wx.EVT_LISTBOX, self.OnListClick, tb)
|
|
self.Bind(wx.EVT_LISTBOX_DCLICK, self.OnListDClick, tb)
|
|
vbox_left.Add(self.tb, 1, wx.GROW|wx.ALL, 0)
|
|
|
|
width, height = self.tb.GetSize()
|
|
|
|
split_right = wx.Panel( splitter, -1, wx.DefaultPosition, wx.DefaultSize,
|
|
wx.NO_BORDER|wx.TAB_TRAVERSAL )
|
|
vbox_right = wx.BoxSizer(wx.VERTICAL)
|
|
split_right.SetSizer(vbox_right)
|
|
|
|
self.image_view = ImagePanel( split_right )
|
|
vbox_right.Add(self.image_view, 1, wx.GROW|wx.ALL, 0)
|
|
|
|
splitter.SplitVertically(split_left, split_right, 150)
|
|
vbox_top.Add(splitter, 1, wx.GROW|wx.ALL, 5)
|
|
|
|
hbox_btns = wx.BoxSizer(wx.HORIZONTAL)
|
|
vbox_top.Add(hbox_btns, 0, wx.ALIGN_RIGHT|wx.ALL, 5)
|
|
|
|
ok_btn = wx.Button( self, wx.ID_OPEN, "", wx.DefaultPosition, wx.DefaultSize, 0 )
|
|
self.Bind(wx.EVT_BUTTON, self.OnOk, ok_btn)
|
|
#ok_btn.SetDefault()
|
|
hbox_btns.Add(ok_btn, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5)
|
|
|
|
cancel_btn = wx.Button( self, wx.ID_CANCEL, "",
|
|
wx.DefaultPosition, wx.DefaultSize, 0 )
|
|
hbox_btns.Add(cancel_btn, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5)
|
|
|
|
self.ResetFiles()
|
|
|
|
def ChangeFileTypes(self, ft_tuple):
|
|
"""
|
|
Change list of file types to be supported
|
|
|
|
:param tuple `ft_tuple`: tuple of files types, e.g. to support just
|
|
.gif and .png you would use:
|
|
|
|
(("GIF (*.gif)", "*.gif"),
|
|
("PNG (*.png)", "*.png"))
|
|
|
|
"""
|
|
|
|
self.fl_ext_types = ft_tuple
|
|
self.set_type, self.fl_ext = self.fl_ext_types[0] # initial file filter setting
|
|
self.fl_types = [ x[0] for x in self.fl_ext_types ]
|
|
self.sel_type.Clear()
|
|
self.sel_type.AppendItems(self.fl_types)
|
|
self.sel_type.SetSelection(0)
|
|
self.fl_ext_types = dict(self.fl_ext_types)
|
|
|
|
def GetFiles(self):
|
|
# get the file list using directory and extension values
|
|
if self.fl_ext == "All":
|
|
all_files = []
|
|
|
|
if self.fl_types[-1] == 'All Files':
|
|
allTypes = self.fl_types[-1:]
|
|
else:
|
|
allTypes = self.fl_types[1:]
|
|
for ftypes in allTypes: # get list of all
|
|
filter = self.fl_ext_types[ftypes]
|
|
#print("filter = ", filter)
|
|
self.fl_val = FindFiles(self, self.set_dir, filter)
|
|
all_files = all_files + self.fl_val.files # add to list of files
|
|
|
|
self.fl_list = all_files
|
|
else:
|
|
self.fl_val = FindFiles(self, self.set_dir, self.fl_ext)
|
|
self.fl_list = self.fl_val.files
|
|
|
|
|
|
self.fl_list.sort() # sort the file list
|
|
# prepend the directories
|
|
self.fl_ndirs = len(self.fl_val.dirs)
|
|
self.fl_list = sorted(self.fl_val.dirs) + self.fl_list
|
|
|
|
def DisplayDir(self):
|
|
# display the working directory
|
|
if self.dir:
|
|
ipt = self.dir.GetInsertionPoint()
|
|
self.dir.SetValue(self.set_dir)
|
|
self.dir.SetInsertionPoint(ipt)
|
|
|
|
def OnSetType(self, event):
|
|
val = event.GetString() # get file type value
|
|
self.fl_ext = self.fl_ext_types[val]
|
|
self.ResetFiles()
|
|
|
|
def OnListDClick(self, event):
|
|
self.OnOk('dclick')
|
|
|
|
def OnListClick(self, event):
|
|
val = event.GetSelection()
|
|
self.SetListValue(val)
|
|
|
|
def SetListValue(self, val):
|
|
file_nm = self.fl_list[val]
|
|
self.set_file = file_val = os.path.join(self.set_dir, file_nm)
|
|
if val>=self.fl_ndirs:
|
|
self.image_view.SetValue(file_val)
|
|
else:
|
|
self.image_view.SetValue(None)
|
|
|
|
def OnDirectoryTextSet(self,event):
|
|
event.Skip()
|
|
path = event.GetString()
|
|
if os.path.isdir(path):
|
|
self.set_dir = path
|
|
self.ResetFiles()
|
|
return
|
|
|
|
if os.path.isfile(path):
|
|
dname,fname = os.path.split(path)
|
|
if os.path.isdir(dname):
|
|
self.ResetFiles()
|
|
# try to select fname in list
|
|
try:
|
|
idx = self.fl_list.index(fname)
|
|
self.tb.SetSelection(idx)
|
|
self.SetListValue(idx)
|
|
return
|
|
except ValueError:
|
|
pass
|
|
|
|
wx.Bell()
|
|
|
|
def OnUpDirectory(self, event):
|
|
sdir = os.path.split(self.set_dir)[0]
|
|
self.set_dir = sdir
|
|
self.ResetFiles()
|
|
|
|
def OnChooseDirectory(self, event):
|
|
# set the new directory
|
|
dlg = wx.DirDialog(self)
|
|
dlg.SetPath(self.set_dir)
|
|
|
|
if dlg.ShowModal() == wx.ID_OK:
|
|
self.set_dir = dlg.GetPath()
|
|
self.ResetFiles()
|
|
|
|
dlg.Destroy()
|
|
|
|
def ResetFiles(self):
|
|
# refresh the display with files and initial image
|
|
self.DisplayDir()
|
|
self.GetFiles()
|
|
|
|
# Changed 12/8/03 jmg
|
|
#
|
|
# o Clear listbox first
|
|
# o THEN check to see if there are any valid files of the selected
|
|
# type,
|
|
# o THEN if we have any files to display, set the listbox up,
|
|
#
|
|
# OTHERWISE
|
|
#
|
|
# o Leave it cleared
|
|
# o Clear the image viewer.
|
|
#
|
|
# This avoids a nasty assert error.
|
|
#
|
|
self.tb.Clear()
|
|
|
|
if len(self.fl_list):
|
|
self.tb.Set(self.fl_list)
|
|
|
|
for idir in range(self.fl_ndirs):
|
|
d = self.fl_list[idir]
|
|
# mark directories as 'True' with client data
|
|
self.tb.SetClientData(idir, True)
|
|
self.tb.SetString(idir,'['+d+']')
|
|
|
|
try:
|
|
self.tb.SetSelection(0)
|
|
self.SetListValue(0)
|
|
except Exception:
|
|
self.image_view.SetValue(None)
|
|
else:
|
|
self.image_view.SetValue(None)
|
|
|
|
def GetFile(self):
|
|
"""
|
|
Get selected file
|
|
|
|
:return: File selected or None
|
|
"""
|
|
return self.set_file
|
|
|
|
def GetDirectory(self):
|
|
"""
|
|
Get directory
|
|
|
|
:return: get the current directory
|
|
|
|
"""
|
|
return self.set_dir
|
|
|
|
def OnCancel(self, event):
|
|
self.result = None
|
|
self.EndModal(wx.ID_CANCEL)
|
|
|
|
def OnOk(self, event):
|
|
if os.path.isdir(self.set_file):
|
|
sdir = os.path.split(self.set_file)
|
|
|
|
#os.path.normapth?
|
|
if sdir and sdir[-1]=='..':
|
|
sdir = os.path.split(sdir[0])[0]
|
|
sdir = os.path.split(sdir)
|
|
self.set_dir = os.path.join(*sdir)
|
|
self.set_file = None
|
|
self.ResetFiles()
|
|
elif event != 'dclick':
|
|
self.result = self.set_file
|
|
self.EndModal(wx.ID_OK)
|
|
|
|
|
|
|
|
class FindFiles:
|
|
def __init__(self, parent, dir, mask, with_dirs=True):
|
|
filelist = []
|
|
dirlist = [".."]
|
|
self.dir = dir
|
|
self.file = ""
|
|
mask = mask.upper()
|
|
pattern = self.MakeRegex(mask)
|
|
|
|
for i in os.listdir(dir):
|
|
if i == "." or i == "..":
|
|
continue
|
|
|
|
path = os.path.join(dir, i)
|
|
|
|
if os.path.isdir(path):
|
|
dirlist.append(i)
|
|
continue
|
|
|
|
path = path.upper()
|
|
value = i.upper()
|
|
|
|
if pattern.match(value) is not None:
|
|
filelist.append(i)
|
|
|
|
|
|
self.files = filelist
|
|
if with_dirs:
|
|
self.dirs = dirlist
|
|
|
|
def MakeRegex(self, pattern):
|
|
import re
|
|
f = "" # Set up a regex for file names
|
|
|
|
for ch in pattern:
|
|
if ch == "*":
|
|
f = f + ".*"
|
|
elif ch == ".":
|
|
f = f + "\."
|
|
elif ch == "?":
|
|
f = f + "."
|
|
else:
|
|
f = f + ch
|
|
|
|
return re.compile(f+'$')
|
|
|
|
def StripExt(self, file_nm):
|
|
fl_fld = os.path.splitext(file_nm)
|
|
fl_name = fl_fld[0]
|
|
ext = fl_fld[1]
|
|
return ext[1:]
|
|
|
|
|
|
#----------------------------------------------------------------------
|
|
# This part of the file was generated by C:\Python25\Scripts\img2py
|
|
# then edited slightly.
|
|
|
|
from wx.lib.embeddedimage import PyEmbeddedImage
|
|
|
|
IMG_CATALOG = {}
|
|
|
|
IMG_CATALOG['White'] = PyEmbeddedImage(
|
|
"iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAA3NCSVQICAjb4U/gAAAAIUlE"
|
|
"QVQYlWNgIAIwMjAw/P//H58KRkYmYkwaVUScIqIAAMjRAxRV8+5MAAAAAElFTkSuQmCC")
|
|
|
|
IMG_CATALOG['Grey'] = PyEmbeddedImage(
|
|
"iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAA3NCSVQICAjb4U/gAAAAIklE"
|
|
"QVQYlWNgIAIwMjAwnDlzBo8KExMTJmJMGlVEnCKiAAC24wMULFLZGAAAAABJRU5ErkJggg==")
|
|
|
|
IMG_CATALOG['Black'] = PyEmbeddedImage(
|
|
"iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAA3NCSVQICAjb4U/gAAAADklE"
|
|
"QVQYlWNgGAVDFQAAAbwAATN8mzYAAAAASUVORK5CYII=")
|
|
|
|
IMG_CATALOG['Checked'] = PyEmbeddedImage(
|
|
"iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAA3NCSVQICAjb4U/gAAAAMUlE"
|
|
"QVQYlWNgIAIwMjAwnDlzBlnI2NgYRQUjIxMxJtFZEQsDhkvPnj07sG4iShFRAAAougYW+urT"
|
|
"ZwAAAABJRU5ErkJggg==")
|
|
|
|
IMG_CATALOG['NoFrame'] = PyEmbeddedImage(
|
|
"iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAA3NCSVQICAjb4U/gAAAANklE"
|
|
"QVQYla2PQQoAIBACnej/X7ZbUEQtkudhVKkQJNm+EdAqpggCgB+m44kFml1bY39q0k15Bsuc"
|
|
"CR/z8ajiAAAAAElFTkSuQmCC")
|
|
|
|
IMG_CATALOG['BoxFrame'] = PyEmbeddedImage(
|
|
"iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAA3NCSVQICAjb4U/gAAAAQ0lE"
|
|
"QVQYlZ2O0QoAIAgDd9L//7I9CFEhJu1psmNOaghJ7l4RYJ0m1U0R2X4vevcHVOiG0tcHBABh"
|
|
"8nWpIhpPLtn0rwm4WyD966x3sgAAAABJRU5ErkJggg==")
|
|
|
|
IMG_CATALOG['CropFrame'] = PyEmbeddedImage(
|
|
"iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAA3NCSVQICAjb4U/gAAAASUlE"
|
|
"QVQYlb2QMQrAQAgEZ0P+q0/RF5tCuIMUh2myhcgyjCAMIiAiDoS7XxPTCLrXZmaAJKCqgMz8"
|
|
"YHpD7ThBkvpcz93z6wtGeQD/sQ8bfXs8NAAAAABJRU5ErkJggg==")
|
|
|