1 #!/usr/bin/env python
2
3 """
4 This sample comes from an IBM developerWorks article at
5 http://www-106.ibm.com/developerworks/library/l-wxpy/index.html
6
7 This small program was adapted to demonstrate the current guide lines
8 on http://wiki.wxpython.org/index.cgi/wxPython_20Style_20Guide.
9 Changes are noted in readme.txt.
10 """
11
12 import sys, os
13 import wx
14
15
16 # Process the command line. Not much to do;
17 # just get the name of the project file if it's given. Simple.
18 projfile = 'Unnamed'
19 if len(sys.argv) > 1:
20 projfile = sys.argv[1]
21
22
23 def MsgDlg(window, string, caption='wxProject', style=wx.YES_NO|wx.CANCEL):
24 """Common MessageDialog."""
25 dlg = wx.MessageDialog(window, string, caption, style)
26 result = dlg.ShowModal()
27 dlg.Destroy()
28 return result
29
30
31 class main_window(wx.Frame):
32 """wxProject MainFrame."""
33 def __init__(self, parent, title):
34 """Create the wxProject MainFrame."""
35 wx.Frame.__init__(self, parent, title=title, size=(500, 500),
36 style=wx.DEFAULT_FRAME_STYLE|wx.NO_FULL_REPAINT_ON_RESIZE)
37
38
39 # Set up menu bar for the program.
40 self.mainmenu = wx.MenuBar() # Create menu bar.
41
42 # Make the 'Project' menu.
43 menu = wx.Menu()
44
45 item = menu.Append(wx.ID_OPEN, '&Open', 'Open project') # Append a new menu
46 self.Bind(wx.EVT_MENU, self.OnProjectOpen, item) # Create and assign a menu event.
47
48 item = menu.Append(wx.ID_NEW, '&New', 'New project')
49 self.Bind(wx.EVT_MENU, self.OnProjectNew, item)
50
51 item = menu.Append(wx.ID_EXIT, 'E&xit', 'Exit program')
52 self.Bind(wx.EVT_MENU, self.OnProjectExit, item)
53
54 self.mainmenu.Append(menu, '&Project') # Add the project menu to the menu bar.
55
56 # Make the 'File' menu.
57 menu = wx.Menu()
58
59 item = menu.Append(wx.ID_ANY, '&Add', 'Add file to project')
60 self.Bind(wx.EVT_MENU, self.OnFileAdd, item)
61
62 item = menu.Append(wx.ID_ANY, '&Remove', 'Remove file from project')
63 self.Bind(wx.EVT_MENU, self.OnFileRemove, item)
64
65 item = menu.Append(wx.ID_ANY, '&Open', 'Open file for editing')
66 self.Bind(wx.EVT_MENU, self.OnFileOpen, item)
67
68 item = menu.Append(wx.ID_ANY, '&Save', 'Save file')
69 self.Bind(wx.EVT_MENU, self.OnFileSave, item)
70
71 self.mainmenu.Append(menu, '&File') # Add the file menu to the menu bar.
72
73 # Attach the menu bar to the window.
74 self.SetMenuBar(self.mainmenu)
75
76 # Create the splitter window.
77 splitter = wx.SplitterWindow(self, style=wx.NO_3D|wx.SP_3D)
78 splitter.SetMinimumPaneSize(1)
79
80 # Create the tree on the left.
81 self.tree = wx.TreeCtrl(splitter, style=wx.TR_DEFAULT_STYLE)
82 self.tree.Bind(wx.EVT_TREE_BEGIN_LABEL_EDIT, self.OnTreeLabelEdit)
83 self.tree.Bind(wx.EVT_TREE_END_LABEL_EDIT, self.OnTreeLabelEditEnd)
84 self.tree.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnTreeItemActivated)
85
86 # Create the editor on the right.
87 self.editor = wx.TextCtrl(splitter, style=wx.TE_MULTILINE)
88 self.editor.Enable(0)
89
90 # Install the tree and the editor.
91 splitter.SplitVertically(self.tree, self.editor)
92 splitter.SetSashPosition(180, True)
93
94 # Some global state variables.
95 self.projectdirty = False
96 self.root = None
97 self.close = False
98
99 self.Bind(wx.EVT_CLOSE, self.OnProjectExit)
100
101 self.Show(True)
102
103 # ----------------------------------------------------------------------------------------
104 # Some nice little handlers.
105 # ----------------------------------------------------------------------------------------
106
107 def project_open(self, project_file):
108 """Open and process a wxProject file."""
109 try:
110 input = open(project_file, 'r')
111 self.tree.DeleteAllItems()
112
113 self.project_file = project_file
114 name = input.readline().replace ('\n', '')
115 self.SetTitle(name)
116
117 # create the file elements in the tree control.
118 self.root = self.tree.AddRoot(name)
119 self.activeitem = self.root
120 for line in input:
121 self.tree.AppendItem(self.root, line.replace ('\n', ''))
122 input.close()
123 self.tree.Expand(self.root)
124
125 self.editor.Clear()
126 self.editor.Enable(False)
127
128 self.projectdirty = False
129 except IOError:
130 pass
131
132 def project_save(self):
133 """Save a wxProject file."""
134 try:
135 output = open(self.project_file, 'w+')
136 output.write(self.tree.GetItemText(self.root) + '\n')
137 count = self.tree.GetChildrenCount(self.root) # collect all file (tree) items.
138 iter = 0
139 child = ''
140 for i in range(count):
141 if i == 0:
142 child, cookie = self.tree.GetFirstChild(self.root)
143 else:
144 child, cookie = self.tree.GetNextChild(self.root, cookie)
145 output.write(self.tree.GetItemText(child) + '\n')
146 output.close()
147 self.projectdirty = False
148 except IOError:
149 MsgDlg(self, 'There was an error saving the new project file.', 'Error!', wx.OK)
150
151 def CheckProjectDirty(self):
152 """Were the current project changed? If so, save it before."""
153 open_it = True
154 if self.projectdirty:
155 # save the current project file first.
156 result = MsgDlg(self, 'The project has been changed. Save?')
157 if result == wx.ID_YES:
158 self.project_save()
159 if result == wx.ID_CANCEL:
160 open_it = False
161 return open_it
162
163 def CheckTreeRootItem(self):
164 """Is there any root item?"""
165 if not self.root:
166 MsgDlg(self, 'Please create or open a project before.', 'Error!', wx.OK)
167 return False
168 return True
169
170 # ----------------------------------------------------------------------------------------
171 # Event handlers from here on out.
172 # ----------------------------------------------------------------------------------------
173
174 def OnProjectOpen(self, event):
175 """Open a wxProject file."""
176 open_it = self.CheckProjectDirty()
177 if open_it:
178 dlg = wx.FileDialog(self, 'Choose a project to open', '.', '', '*.wxp', wx.OPEN)
179 if dlg.ShowModal() == wx.ID_OK:
180 self.project_open(dlg.GetPath())
181 dlg.Destroy()
182
183 def OnProjectNew(self, event):
184 """Create a new wxProject."""
185 open_it = self.CheckProjectDirty()
186 if open_it:
187 dlg = wx.TextEntryDialog(self, 'Name for new project:', 'New Project',
188 'New project', wx.OK|wx.CANCEL)
189 if dlg.ShowModal() == wx.ID_OK:
190 newproj = dlg.GetValue()
191 dlg.Destroy()
192 dlg = wx.FileDialog(self, 'Place to store new project.', '.', '', '*.wxp', wx.SAVE)
193 if dlg.ShowModal() == wx.ID_OK:
194 try:
195 # save the project file.
196 proj = open(dlg.GetPath(), 'w')
197 proj.write(newproj + '\n')
198 proj.close()
199 self.project_open(dlg.GetPath())
200 except IOError:
201 MsgDlg(self, 'There was an error saving the new project file.', 'Error!', wx.OK)
202 dlg.Destroy()
203
204 def SaveCurrentFile(self):
205 """Check and save current file."""
206 go_ahead = True
207 if self.root:
208 if self.activeitem != self.root:
209 if self.editor.IsModified(): # Save modified file before
210 result = MsgDlg(self, 'The edited file has changed. Save it?')
211 if result == wx.ID_YES:
212 self.editor.SaveFile(self.tree.GetItemText(self.activeitem))
213 if result == wx.ID_CANCEL:
214 go_ahead = False
215 if go_ahead:
216 self.tree.SetItemBold(self.activeitem, 0)
217 return go_ahead
218
219 def OnProjectExit(self, event):
220 """Quit the program."""
221 if not self.close:
222 self.close = True
223 if not self.SaveCurrentFile():
224 self.close = False
225 if self.projectdirty and self.close:
226 result = MsgDlg(self, 'The project has been changed. Save?')
227 if result == wx.ID_YES:
228 self.project_save()
229 if result == wx.ID_CANCEL:
230 self.close = False
231 if self.close:
232 self.Close()
233 else:
234 event.Skip()
235
236 def OnFileAdd(self, event):
237 """Adds a file to the current project."""
238 if not self.CheckTreeRootItem():
239 return
240
241 dlg = wx.FileDialog(self, 'Choose a file to add.', '.', '', '*.*', wx.OPEN)
242 if dlg.ShowModal() == wx.ID_OK:
243 path = os.path.split(dlg.GetPath())
244 self.tree.AppendItem(self.root, path[1])
245 self.tree.Expand(self.root)
246 self.project_save()
247
248 def OnFileRemove(self, event):
249 """Removes a file to the current project."""
250 if not self.CheckTreeRootItem():
251 return
252 item = self.tree.GetSelection()
253 if item != self.root:
254 self.tree.Delete(item)
255 self.project_save()
256
257 def OnFileOpen(self, event):
258 """Opens current selected file."""
259 if self.root:
260 item = self.tree.GetSelection()
261 if item != self.root:
262 self.OnTreeItemActivated(None, item)
263 return
264 MsgDlg(self, 'There is no file to load.', 'Error!', wx.OK)
265
266 def OnFileSave(self, event):
267 """Saves current selected file."""
268 if self.root:
269 if self.activeitem != self.root:
270 self.editor.SaveFile(self.tree.GetItemText(self.activeitem))
271 return
272 MsgDlg(self, 'There is no file to save.', 'Error!', wx.OK)
273
274
275 def OnTreeLabelEdit(self, event):
276 """Edit tree label (only root label can be edited)."""
277 item = event.GetItem()
278 if item != self.root:
279 event.Veto()
280
281 def OnTreeLabelEditEnd(self, event):
282 """End editing the tree label."""
283 self.projectdirty = True
284
285
286 def OnTreeItemActivated(self, event, item=None):
287 """Tree item was activated: try to open this file."""
288 go_ahead = self.SaveCurrentFile()
289
290 if go_ahead:
291 if event:
292 item = event.GetItem()
293 self.activeitem = item
294 if item != self.root:
295 # load the current selected file
296 self.tree.SetItemBold(item, 1)
297 self.editor.Enable(1)
298 self.editor.LoadFile(self.tree.GetItemText(item))
299 self.editor.SetInsertionPoint(0)
300 self.editor.SetFocus()
301 else:
302 self.editor.Clear()
303 self.editor.Enable(0)
304
305
306 class App(wx.App):
307 """wxProject Application."""
308 def OnInit(self):
309 """Create the wxProject Application."""
310 frame = main_window(None, 'wxProject - ' + projfile)
311 if projfile != 'Unnamed':
312 frame.project_open(projfile)
313 return True
314
315
316 if __name__ == '__main__':
317 app = App(0)
318 app.MainLoop()
This file is also in the doc package in wxPython:
"wxPythonx.y Docs and Demos/samples/wxProject/wxProject.py"