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