]> git.saurik.com Git - wxWidgets.git/blob - wxPython/wx/lib/filebrowsebutton.py
Play nice with sizers
[wxWidgets.git] / wxPython / wx / lib / filebrowsebutton.py
1 #----------------------------------------------------------------------
2 # Name: wxPython.lib.filebrowsebutton
3 # Purpose: Composite controls that provide a Browse button next to
4 # either a wxTextCtrl or a wxComboBox. The Browse button
5 # launches a wxFileDialog and loads the result into the
6 # other control.
7 #
8 # Author: Mike Fletcher
9 #
10 # RCS-ID: $Id$
11 # Copyright: (c) 2000 by Total Control Software
12 # Licence: wxWindows license
13 #----------------------------------------------------------------------
14 # 12/02/2003 - Jeff Grimmett (grimmtooth@softhome.net)
15 #
16 # o 2.5 Compatability changes
17 #
18
19 import os
20 import types
21
22 import wx
23
24 #----------------------------------------------------------------------
25
26 class FileBrowseButton(wx.Panel):
27 """
28 A control to allow the user to type in a filename or browse with
29 the standard file dialog to select file
30 """
31 def __init__ (self, parent, id= -1,
32 pos = wx.DefaultPosition,
33 size = wx.DefaultSize,
34 style = wx.TAB_TRAVERSAL,
35 labelText= "File Entry:",
36 buttonText= "Browse",
37 toolTip= "Type filename or click browse to choose file",
38 # following are the values for a file dialog box
39 dialogTitle = "Choose a file",
40 startDirectory = ".",
41 initialValue = "",
42 fileMask = "*.*",
43 fileMode = wx.OPEN,
44 # callback for when value changes (optional)
45 changeCallback= lambda x:x
46 ):
47 """
48 :param labelText: Text for label to left of text field
49 :param buttonText: Text for button which launches the file dialog
50 :param toolTip: Help text
51 :param dialogTitle: Title used in file dialog
52 :param startDirectory: Default directory for file dialog startup
53 :param fileMask: File mask (glob pattern, such as *.*) to use in file dialog
54 :param fileMode: wx.OPEN or wx.SAVE, indicates type of file dialog to use
55 :param changeCallback: Optional callback called for all changes in value of the control
56 """
57
58 # store variables
59 self.labelText = labelText
60 self.buttonText = buttonText
61 self.toolTip = toolTip
62 self.dialogTitle = dialogTitle
63 self.startDirectory = startDirectory
64 self.initialValue = initialValue
65 self.fileMask = fileMask
66 self.fileMode = fileMode
67 self.changeCallback = changeCallback
68 self.callCallback = True
69
70
71 # get background to match it
72 try:
73 self._bc = parent.GetBackgroundColour()
74 except:
75 pass
76
77 # create the dialog
78 self.createDialog(parent, id, pos, size, style )
79 # Setting a value causes the changeCallback to be called.
80 # In this case that would be before the return of the
81 # constructor. Not good. So a default value on
82 # SetValue is used to disable the callback
83 self.SetValue( initialValue, 0)
84
85
86 def createDialog( self, parent, id, pos, size, style ):
87 """Setup the graphic representation of the dialog"""
88 wx.Panel.__init__ (self, parent, id, pos, size, style)
89 self.SetMinSize(size) # play nice with sizers
90
91 # try to set the background colour
92 try:
93 self.SetBackgroundColour(self._bc)
94 except:
95 pass
96
97 box = wx.BoxSizer(wx.HORIZONTAL)
98
99 self.label = self.createLabel( )
100 box.Add( self.label, 0, wx.CENTER )
101
102 self.textControl = self.createTextControl()
103 box.Add( self.textControl, 1, wx.LEFT|wx.CENTER, 5)
104
105 self.browseButton = self.createBrowseButton()
106 box.Add( self.browseButton, 0, wx.LEFT|wx.CENTER, 5)
107
108 # add a border around the whole thing and resize the panel to fit
109 outsidebox = wx.BoxSizer(wx.VERTICAL)
110 outsidebox.Add(box, 1, wx.EXPAND|wx.ALL, 3)
111 outsidebox.Fit(self)
112
113 self.SetAutoLayout(True)
114 self.SetSizer( outsidebox )
115 self.Layout()
116 if type( size ) == types.TupleType:
117 size = apply( wx.Size, size)
118 self.SetDimensions(-1, -1, size.width, size.height, wx.SIZE_USE_EXISTING)
119
120 # if size.width != -1 or size.height != -1:
121 # self.SetSize(size)
122
123 def SetBackgroundColour(self,color):
124 wx.Panel.SetBackgroundColour(self,color)
125 self.label.SetBackgroundColour(color)
126
127 def createLabel( self ):
128 """Create the label/caption"""
129 label = wx.StaticText(self, -1, self.labelText, style =wx.ALIGN_RIGHT )
130 font = label.GetFont()
131 w, h, d, e = self.GetFullTextExtent(self.labelText, font)
132 label.SetSize((w+5, h))
133 return label
134
135 def createTextControl( self):
136 """Create the text control"""
137 textControl = wx.TextCtrl(self, -1)
138 textControl.SetToolTipString( self.toolTip )
139 if self.changeCallback:
140 textControl.Bind(wx.EVT_TEXT, self.OnChanged)
141 textControl.Bind(wx.EVT_COMBOBOX, self.OnChanged)
142 return textControl
143
144 def OnChanged(self, evt):
145 if self.callCallback and self.changeCallback:
146 self.changeCallback(evt)
147
148 def createBrowseButton( self):
149 """Create the browse-button control"""
150 button =wx.Button(self, -1, self.buttonText)
151 button.SetToolTipString( self.toolTip )
152 button.Bind(wx.EVT_BUTTON, self.OnBrowse)
153 return button
154
155
156 def OnBrowse (self, event = None):
157 """ Going to browse for file... """
158 current = self.GetValue()
159 directory = os.path.split(current)
160 if os.path.isdir( current):
161 directory = current
162 current = ''
163 elif directory and os.path.isdir( directory[0] ):
164 current = directory[1]
165 directory = directory [0]
166 else:
167 directory = self.startDirectory
168 dlg = wx.FileDialog(self, self.dialogTitle, directory, current,
169 self.fileMask, self.fileMode)
170
171 if dlg.ShowModal() == wx.ID_OK:
172 self.SetValue(dlg.GetPath())
173 dlg.Destroy()
174
175
176 def GetValue (self):
177 """
178 retrieve current value of text control
179 """
180 return self.textControl.GetValue()
181
182 def SetValue (self, value, callBack=1):
183 """set current value of text control"""
184 save = self.callCallback
185 self.callCallback = callBack
186 self.textControl.SetValue(value)
187 self.callCallback = save
188
189
190 def Enable (self, value):
191 """ Convenient enabling/disabling of entire control """
192 self.label.Enable (value)
193 self.textControl.Enable (value)
194 return self.browseButton.Enable (value)
195
196 def GetLabel( self ):
197 """ Retrieve the label's current text """
198 return self.label.GetLabel()
199
200 def SetLabel( self, value ):
201 """ Set the label's current text """
202 rvalue = self.label.SetLabel( value )
203 self.Refresh( True )
204 return rvalue
205
206
207
208
209 class FileBrowseButtonWithHistory( FileBrowseButton ):
210 """
211 with following additions:
212 __init__(..., history=None)
213
214 history -- optional list of paths for initial history drop-down
215 (must be passed by name, not a positional argument)
216 If history is callable it will must return a list used
217 for the history drop-down
218
219 changeCallback -- as for FileBrowseButton, but with a work-around
220 for win32 systems which don't appear to create wx.EVT_COMBOBOX
221 events properly. There is a (slight) chance that this work-around
222 will cause some systems to create two events for each Combobox
223 selection. If you discover this condition, please report it!
224
225 As for a FileBrowseButton.__init__ otherwise.
226
227 GetHistoryControl()
228 Return reference to the control which implements interfaces
229 required for manipulating the history list. See GetHistoryControl
230 documentation for description of what that interface is.
231
232 GetHistory()
233 Return current history list
234
235 SetHistory( value=(), selectionIndex = None )
236 Set current history list, if selectionIndex is not None, select that index
237
238 """
239 def __init__( self, *arguments, **namedarguments):
240 self.history = namedarguments.get( "history" )
241 if self.history:
242 del namedarguments["history"]
243
244 self.historyCallBack=None
245 if callable(self.history):
246 self.historyCallBack=self.history
247 self.history=None
248 apply( FileBrowseButton.__init__, ( self,)+arguments, namedarguments)
249
250
251 def createTextControl( self):
252 """Create the text control"""
253 textControl = wx.ComboBox(self, -1, style = wx.CB_DROPDOWN )
254 textControl.SetToolTipString( self.toolTip )
255 textControl.Bind(wx.EVT_SET_FOCUS, self.OnSetFocus)
256 if self.changeCallback:
257 textControl.Bind(wx.EVT_TEXT, self.changeCallback)
258 textControl.Bind(wx.EVT_COMBOBOX, self.changeCallback)
259 if self.history:
260 history=self.history
261 self.history=None
262 self.SetHistory( history, control=textControl)
263 return textControl
264
265
266 def GetHistoryControl( self ):
267 """
268 Return a pointer to the control which provides (at least)
269 the following methods for manipulating the history list:
270
271 Append( item ) -- add item
272 Clear() -- clear all items
273 Delete( index ) -- 0-based index to delete from list
274 SetSelection( index ) -- 0-based index to select in list
275
276 Semantics of the methods follow those for the wxComboBox control
277 """
278 return self.textControl
279
280
281 def SetHistory( self, value=(), selectionIndex = None, control=None ):
282 """Set the current history list"""
283 if control is None:
284 control = self.GetHistoryControl()
285 if self.history == value:
286 return
287 self.history = value
288 # Clear history values not the selected one.
289 tempValue=control.GetValue()
290 # clear previous values
291 control.Clear()
292 control.SetValue(tempValue)
293 # walk through, appending new values
294 for path in value:
295 control.Append( path )
296 if selectionIndex is not None:
297 control.SetSelection( selectionIndex )
298
299
300 def GetHistory( self ):
301 """Return the current history list"""
302 if self.historyCallBack != None:
303 return self.historyCallBack()
304 else:
305 return list( self.history )
306
307
308 def OnSetFocus(self, event):
309 """When the history scroll is selected, update the history"""
310 if self.historyCallBack != None:
311 self.SetHistory( self.historyCallBack(), control=self.textControl)
312 event.Skip()
313
314
315 if wx.Platform == "__WXMSW__":
316 def SetValue (self, value, callBack=1):
317 """ Convenient setting of text control value, works
318 around limitation of wx.ComboBox """
319 save = self.callCallback
320 self.callCallback = callBack
321 self.textControl.SetValue(value)
322 self.callCallback = save
323
324 # Hack to call an event handler
325 class LocalEvent:
326 def __init__(self, string):
327 self._string=string
328 def GetString(self):
329 return self._string
330 if callBack==1:
331 # The callback wasn't being called when SetValue was used ??
332 # So added this explicit call to it
333 self.changeCallback(LocalEvent(value))
334
335
336 class DirBrowseButton(FileBrowseButton):
337 def __init__(self, parent, id = -1,
338 pos = wx.DefaultPosition, size = wx.DefaultSize,
339 style = wx.TAB_TRAVERSAL,
340 labelText = 'Select a directory:',
341 buttonText = 'Browse',
342 toolTip = 'Type directory name or browse to select',
343 dialogTitle = '',
344 startDirectory = '.',
345 changeCallback = None,
346 dialogClass = wx.DirDialog):
347 FileBrowseButton.__init__(self, parent, id, pos, size, style,
348 labelText, buttonText, toolTip,
349 dialogTitle, startDirectory,
350 changeCallback = changeCallback)
351 self.dialogClass = dialogClass
352 #
353
354 def OnBrowse(self, ev = None):
355 dialog = self.dialogClass(self,
356 message = self.dialogTitle,
357 defaultPath = self.startDirectory)
358 if dialog.ShowModal() == wx.ID_OK:
359 self.SetValue(dialog.GetPath())
360 dialog.Destroy()
361 #
362
363
364 #----------------------------------------------------------------------
365
366
367 if __name__ == "__main__":
368 #from skeletonbuilder import rulesfile
369 class SimpleCallback:
370 def __init__( self, tag ):
371 self.tag = tag
372 def __call__( self, event ):
373 print self.tag, event.GetString()
374 class DemoFrame( wx.Frame ):
375 def __init__(self, parent):
376 wx.Frame.__init__(self, parent, -1, "File entry with browse", size=(500,260))
377 self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
378 panel = wx.Panel (self,-1)
379 innerbox = wx.BoxSizer(wx.VERTICAL)
380 control = FileBrowseButton(
381 panel,
382 initialValue = "z:\\temp",
383 )
384 innerbox.Add( control, 0, wx.EXPAND )
385 middlecontrol = FileBrowseButtonWithHistory(
386 panel,
387 labelText = "With History",
388 initialValue = "d:\\temp",
389 history = ["c:\\temp", "c:\\tmp", "r:\\temp","z:\\temp"],
390 changeCallback= SimpleCallback( "With History" ),
391 )
392 innerbox.Add( middlecontrol, 0, wx.EXPAND )
393 middlecontrol = FileBrowseButtonWithHistory(
394 panel,
395 labelText = "History callback",
396 initialValue = "d:\\temp",
397 history = self.historyCallBack,
398 changeCallback= SimpleCallback( "History callback" ),
399 )
400 innerbox.Add( middlecontrol, 0, wx.EXPAND )
401 self.bottomcontrol = control = FileBrowseButton(
402 panel,
403 labelText = "With Callback",
404 style = wx.SUNKEN_BORDER|wx.CLIP_CHILDREN ,
405 changeCallback= SimpleCallback( "With Callback" ),
406 )
407 innerbox.Add( control, 0, wx.EXPAND)
408 self.bottommostcontrol = control = DirBrowseButton(
409 panel,
410 labelText = "Simple dir browse button",
411 style = wx.SUNKEN_BORDER|wx.CLIP_CHILDREN)
412 innerbox.Add( control, 0, wx.EXPAND)
413 ID = wx.NewId()
414 innerbox.Add( wx.Button( panel, ID,"Change Label", ), 1, wx.EXPAND)
415 self.Bind(wx.EVT_BUTTON, self.OnChangeLabel , id=ID)
416 ID = wx.NewId()
417 innerbox.Add( wx.Button( panel, ID,"Change Value", ), 1, wx.EXPAND)
418 self.Bind(wx.EVT_BUTTON, self.OnChangeValue, id=ID )
419 panel.SetAutoLayout(True)
420 panel.SetSizer( innerbox )
421 self.history={"c:\\temp":1, "c:\\tmp":1, "r:\\temp":1,"z:\\temp":1}
422
423 def historyCallBack(self):
424 keys=self.history.keys()
425 keys.sort()
426 return keys
427
428 def OnFileNameChangedHistory (self, event):
429 self.history[event.GetString ()]=1
430
431 def OnCloseMe(self, event):
432 self.Close(True)
433 def OnChangeLabel( self, event ):
434 self.bottomcontrol.SetLabel( "Label Updated" )
435 def OnChangeValue( self, event ):
436 self.bottomcontrol.SetValue( "r:\\somewhere\\over\\the\\rainbow.htm" )
437
438 def OnCloseWindow(self, event):
439 self.Destroy()
440
441 class DemoApp(wx.App):
442 def OnInit(self):
443 wx.InitAllImageHandlers()
444 frame = DemoFrame(None)
445 frame.Show(True)
446 self.SetTopWindow(frame)
447 return True
448
449 def test( ):
450 app = DemoApp(0)
451 app.MainLoop()
452 print 'Creating dialog'
453 test( )
454
455
456