~launchpad-p-s/sofastatistics/main

« back to all changes in this revision

Viewing changes to projects.py

  • Committer: Grant Paton-Simpson
  • Date: 2009-05-19 04:21:43 UTC
  • Revision ID: g@ubuntu-20090519042143-p561mbokz3inefvd
Initial import

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
 
 
2
import wx
 
3
import pprint
 
4
import sys
 
5
import os
 
6
 
 
7
import getdata
 
8
import projselect
 
9
import util
 
10
 
 
11
USER_PATH, LOCAL_PATH = util.get_user_paths()
 
12
 
 
13
# http://www.velocityreviews.com/forums/t336564-proper-use-of-file.html
 
14
EMPTY_PROJ_NAME = "GIVE ME A NAME ..."
 
15
SOFA_DEFAULT_DB = "SOFA_Default_db"
 
16
SOFA_DEFAULT_PROJ = "SOFA_Default_Project.proj"
 
17
SOFA_DEFAULT_LBLS = "SOFA_Default_Labels.lbls"
 
18
SOFA_DEFAULT_STYLE = "SOFA_Default_Style.css"
 
19
SOFA_DEFAULT_SCRIPT = "SOFA_Default_Exported_Table_Scripts.py"
 
20
SOFA_DEFAULT_REPORT = "SOFA_Default_New_Tables.htm"
 
21
INTERNAL_FOLDER = "_internal"
 
22
INT_SCRIPT_PATH = os.path.join(LOCAL_PATH, INTERNAL_FOLDER, "script.py")
 
23
INT_REPORT_FILE = "report.htm"
 
24
INT_REPORT_PATH = os.path.join(LOCAL_PATH, INTERNAL_FOLDER, INT_REPORT_FILE)
 
25
 
 
26
def GetProjs():
 
27
    "NB includes .proj at end"
 
28
    proj_fils = os.listdir(os.path.join(LOCAL_PATH, "projs"))
 
29
    proj_fils = [x for x in proj_fils if x.endswith(".proj")]
 
30
    proj_fils.sort()
 
31
    return proj_fils
 
32
 
 
33
def GetProjSettingsDic(proj_name):
 
34
    """
 
35
    Returns proj_dic with keys such as conn_dets, fil_labels etc.
 
36
    proj_name MUST include .proj on end
 
37
    """
 
38
    f = open(os.path.join(LOCAL_PATH, "projs", proj_name), "r")
 
39
    proj_dic = {}
 
40
    exec f in proj_dic # http://docs.python.org/reference/simple_stmts.html
 
41
    f.close()
 
42
    return proj_dic
 
43
 
 
44
def GetLabels(fil_labels):
 
45
    """
 
46
    Get variable and value labels from fil_labels file.
 
47
    Returns var_labels, var_notes, val_dics.
 
48
    """
 
49
    try:
 
50
        fil = file(fil_labels, "r")
 
51
    except IOError:
 
52
        var_labels = {}
 
53
        var_notes = {}
 
54
        val_dics = {}
 
55
        return var_labels, var_notes, val_dics
 
56
    labels = fil.read()
 
57
    fil.close()
 
58
    labels_dic = {}
 
59
    exec labels in labels_dic
 
60
    try:
 
61
        results = labels_dic["var_labels"], labels_dic["var_notes"], \
 
62
                      labels_dic["val_dics"]
 
63
    except Exception, e:
 
64
        raise Exception, "Three variables needed in " + \
 
65
            "'%s': var_labels, var_notes, and val_dics.  " + \
 
66
            "Please check file." % fil_labels
 
67
    return results
 
68
 
 
69
 
 
70
class ProjectDlg(wx.Dialog):
 
71
    def __init__(self, parent, read_only=False, fil_proj=None):
 
72
        wx.Dialog.__init__(self, parent=parent, title="Project Settings",
 
73
                           size=(1090, 580), 
 
74
                           style=wx.CAPTION|wx.CLOSE_BOX|
 
75
                           wx.SYSTEM_MENU, pos=(100, 100))
 
76
        y_start = self.GetClientSize()[1] - self.GetSize()[1]
 
77
        self.panel_top = wx.Panel(self, pos=(0,0))
 
78
        self.scroll_conn_dets = wx.PyScrolledWindow(self, pos=(10, 280 + y_start), 
 
79
                                                    size=(1070, 240),
 
80
                                                    style=wx.SUNKEN_BORDER)
 
81
        self.scroll_conn_dets.SetScrollbars(10, 10, -1, -1) # otherwise no scrollbars
 
82
        self.scroll_conn_dets.SetVirtualSize((1270, 460))
 
83
        self.panel_bottom = wx.Panel(self, pos=(0, 525 + y_start))
 
84
        self.parent = parent
 
85
        self.szrTop = wx.BoxSizer(wx.VERTICAL)
 
86
        self.szrConn_Dets = wx.BoxSizer(wx.VERTICAL)
 
87
        self.szrBottom = wx.BoxSizer(wx.VERTICAL)
 
88
        # get available settings
 
89
        self.read_only = read_only
 
90
        if fil_proj:
 
91
            self.new_proj = False
 
92
            self.GetProjSettings(fil_proj)
 
93
        else:
 
94
            self.new_proj = True
 
95
        try:
 
96
            self.proj_name
 
97
        except AttributeError:
 
98
            self.proj_name = EMPTY_PROJ_NAME
 
99
        try:
 
100
            self.proj_notes
 
101
        except AttributeError:
 
102
            self.proj_notes = ""
 
103
        try:
 
104
            self.fil_labels
 
105
        except AttributeError:
 
106
            # make empty labels file if necessary
 
107
            fil_default_lbls = os.path.join(LOCAL_PATH, "lbls", 
 
108
                                            SOFA_DEFAULT_LBLS)
 
109
            if not os.path.exists(fil_default_lbls):
 
110
                f = open(fil_default_lbls, "w")
 
111
                f.write("# add labels here")
 
112
                f.close()
 
113
            self.fil_labels = fil_default_lbls
 
114
        try:            
 
115
            self.fil_css
 
116
        except AttributeError:
 
117
            self.fil_css = os.path.join(LOCAL_PATH, "css", 
 
118
                                        SOFA_DEFAULT_STYLE)
 
119
        try:            
 
120
            self.fil_report
 
121
        except AttributeError:       
 
122
            self.fil_report = os.path.join(LOCAL_PATH, "reports", 
 
123
                                           SOFA_DEFAULT_REPORT)
 
124
        try:            
 
125
            self.fil_script
 
126
        except AttributeError: 
 
127
            self.fil_script = os.path.join(LOCAL_PATH, "scripts", 
 
128
                                           SOFA_DEFAULT_SCRIPT)
 
129
        try:
 
130
            self.default_dbe
 
131
        except AttributeError:
 
132
            self.default_dbe = os.path.join(getdata.DBE_SQLITE)
 
133
        getdata.setConnDetDefaults(self)
 
134
        # misc
 
135
        lblfont = wx.Font(11, wx.SWISS, wx.NORMAL, wx.BOLD)
 
136
        # Project Name
 
137
        szrName = wx.BoxSizer(wx.HORIZONTAL)
 
138
        lblName = wx.StaticText(self.panel_top, -1, "Project Name:")
 
139
        lblName.SetFont(lblfont)
 
140
        self.txtName = wx.TextCtrl(self.panel_top, -1, self.proj_name, 
 
141
                                   size=(200, -1))
 
142
        self.txtName.Enable(not self.read_only)
 
143
        szrName.Add(lblName, 0, wx.RIGHT, 10)
 
144
        szrName.Add(self.txtName)
 
145
        # project notes
 
146
        lblProjNotes = wx.StaticText(self.panel_top, -1, "Project Notes:")
 
147
        lblProjNotes.SetFont(lblfont)
 
148
        self.txtProjNotes = wx.TextCtrl(self.panel_top, -1, self.proj_notes,
 
149
                                        size=(540, 60), style=wx.TE_MULTILINE)
 
150
        self.txtProjNotes.Enable(not self.read_only)
 
151
        # Data config details
 
152
        lblLabelPath = wx.StaticText(self.panel_top, -1, "Labels:")
 
153
        lblLabelPath.SetFont(lblfont)
 
154
        self.txtLabelsFile = wx.TextCtrl(self.panel_top, -1, self.fil_labels, 
 
155
                                         size=(320,-1))
 
156
        self.txtLabelsFile.Enable(not self.read_only)
 
157
        btnLabelPath = wx.Button(self.panel_top, -1, "Browse ...")
 
158
        btnLabelPath.Bind(wx.EVT_BUTTON, self.OnButtonLabelPath)
 
159
        btnLabelPath.Enable(not self.read_only)
 
160
        # CSS style config details
 
161
        lblCssPath = wx.StaticText(self.panel_top, -1, "CSS:")
 
162
        lblCssPath.SetFont(lblfont)
 
163
        self.txtCssFile = wx.TextCtrl(self.panel_top, -1, self.fil_css, 
 
164
                                      size=(320,-1))
 
165
        self.txtCssFile.Enable(not self.read_only)
 
166
        btnCssPath = wx.Button(self.panel_top, -1, "Browse ...")
 
167
        btnCssPath.Bind(wx.EVT_BUTTON, self.OnButtonCssPath)
 
168
        btnCssPath.Enable(not self.read_only)
 
169
        # Output details
 
170
        # report
 
171
        lblReportPath = wx.StaticText(self.panel_top, -1, "Report:")
 
172
        lblReportPath.SetFont(lblfont)
 
173
        self.txtReportFile = wx.TextCtrl(self.panel_top, -1, self.fil_report, 
 
174
                                         size=(320,-1))
 
175
        self.txtReportFile.Enable(not self.read_only)
 
176
        btnReportPath = wx.Button(self.panel_top, -1, "Browse ...")
 
177
        btnReportPath.Bind(wx.EVT_BUTTON, self.OnButtonReportPath)
 
178
        btnReportPath.Enable(not self.read_only)
 
179
        # script
 
180
        lblScriptPath = wx.StaticText(self.panel_top, -1, "Script:")
 
181
        lblScriptPath.SetFont(lblfont)
 
182
        self.txtScriptFile = wx.TextCtrl(self.panel_top, -1, self.fil_script, 
 
183
                                   size=(320,-1))
 
184
        self.txtScriptFile.Enable(not self.read_only)
 
185
        btnScriptPath = wx.Button(self.panel_top, -1, "Browse ...")
 
186
        btnScriptPath.Bind(wx.EVT_BUTTON, self.OnButtonScriptPath)
 
187
        btnScriptPath.Enable(not self.read_only)
 
188
        # DATA CONNECTIONS
 
189
        lblDataConnDets = wx.StaticText(self.panel_top, -1, 
 
190
                                        "Data Connection Details:")
 
191
        # default dbe
 
192
        lblDefault_Dbe = wx.StaticText(self.scroll_conn_dets, -1, 
 
193
                                       "Default Database Engine:")
 
194
        lblDefault_Dbe.SetFont(lblfont)
 
195
        self.dropDefault_Dbe = wx.Choice(self.scroll_conn_dets, -1, 
 
196
                                         choices=getdata.DBES)
 
197
        sel_dbe_id = getdata.DBES.index(self.default_dbe)
 
198
        self.dropDefault_Dbe.SetSelection(sel_dbe_id)
 
199
        self.dropDefault_Dbe.Bind(wx.EVT_CHOICE, self.OnDbeChoice)
 
200
        self.dropDefault_Dbe.Enable(not self.read_only)
 
201
        # NOTES
 
202
        szrNotes = wx.BoxSizer(wx.HORIZONTAL)
 
203
        szrNotes.Add(lblProjNotes, 0, wx.RIGHT, 5)
 
204
        szrNotes.Add(self.txtProjNotes, 1, wx.GROW)
 
205
        #2 CONFIG
 
206
        szrConfig = wx.BoxSizer(wx.HORIZONTAL)
 
207
        #3 DATA CONFIG
 
208
        bxDataConfig = wx.StaticBox(self.panel_top, -1, "Data Config")
 
209
        szrDataConfig = wx.StaticBoxSizer(bxDataConfig, wx.HORIZONTAL)
 
210
        #3 DATA CONFIG INNER
 
211
        szrDataConfigInner = wx.BoxSizer(wx.HORIZONTAL)
 
212
        szrDataConfigInner.Add(lblLabelPath, 0, wx.LEFT|wx.RIGHT, 5)
 
213
        szrDataConfigInner.Add(self.txtLabelsFile, 1, wx.GROW|wx.RIGHT, 10)
 
214
        szrDataConfigInner.Add(btnLabelPath, 0)
 
215
        szrDataConfig.Add(szrDataConfigInner, 1)
 
216
        szrConfig.Add(szrDataConfig, 1, wx.RIGHT, 10)
 
217
        #3 CSS CONFIG
 
218
        bxCssConfig = wx.StaticBox(self.panel_top, -1, "Table Style")
 
219
        szrCssConfig = wx.StaticBoxSizer(bxCssConfig, wx.HORIZONTAL)
 
220
        #3 CSS CONFIG INNER
 
221
        szrCssConfigInner = wx.BoxSizer(wx.HORIZONTAL)
 
222
        szrCssConfigInner.Add(lblCssPath, 0, wx.LEFT|wx.RIGHT, 5)
 
223
        szrCssConfigInner.Add(self.txtCssFile, 1, wx.GROW|wx.RIGHT, 10)
 
224
        szrCssConfigInner.Add(btnCssPath, 0)
 
225
        szrCssConfig.Add(szrCssConfigInner, 1)
 
226
        szrConfig.Add(szrCssConfig, 1)
 
227
        #2 OUTPUT
 
228
        bxOutput = wx.StaticBox(self.panel_top, -1, "Output")
 
229
        szrOutput = wx.StaticBoxSizer(bxOutput, wx.HORIZONTAL)
 
230
        #3 OUTPUT INNER
 
231
        szrOutputInner = wx.BoxSizer(wx.HORIZONTAL)
 
232
        # report 
 
233
        szrOutputInner.Add(lblReportPath, 0, wx.LEFT|wx.RIGHT, 5)
 
234
        szrOutputInner.Add(self.txtReportFile, 1, wx.GROW|wx.RIGHT, 10)
 
235
        szrOutputInner.Add(btnReportPath, 0, wx.RIGHT, 10)
 
236
        # script
 
237
        szrOutputInner.Add(lblScriptPath, 0, wx.LEFT|wx.RIGHT, 5)
 
238
        szrOutputInner.Add(self.txtScriptFile, 1, wx.GROW|wx.RIGHT, 10)
 
239
        szrOutputInner.Add(btnScriptPath, 0)
 
240
        szrOutput.Add(szrOutputInner, 1)
 
241
        # default dbe
 
242
        szrDefault_Dbe = wx.BoxSizer(wx.HORIZONTAL)
 
243
        szrDefault_Dbe.Add(lblDefault_Dbe, 0, wx.LEFT|wx.RIGHT, 5)
 
244
        szrDefault_Dbe.Add(self.dropDefault_Dbe, 0)
 
245
        # Close
 
246
        self.SetupButtons()
 
247
        # sizers
 
248
        # TOP
 
249
        self.szrTop.Add(szrName, 0, wx.GROW|wx.ALL, 10)
 
250
        self.szrTop.Add(szrNotes, 1, wx.GROW|wx.ALL, 10)
 
251
        self.szrTop.Add(szrConfig, 0, wx.GROW|wx.LEFT|wx.RIGHT, 10)
 
252
        self.szrTop.Add(szrOutput, 0, wx.GROW|wx.ALL, 10)
 
253
        self.szrTop.Add(lblDataConnDets, 0, wx.LEFT|wx.RIGHT, 10)
 
254
        self.panel_top.SetSizer(self.szrTop)
 
255
        self.szrTop.SetSizeHints(self.panel_top)
 
256
        # CONN DETS
 
257
        self.szrConn_Dets.Add(szrDefault_Dbe, 0, wx.LEFT|wx.RIGHT|wx.TOP, 10)
 
258
        getdata.setDataConnGui(parent=self, read_only=self.read_only, 
 
259
                               scroll=self.scroll_conn_dets, 
 
260
                               szr=self.szrConn_Dets, lblfont=lblfont)
 
261
        self.scroll_conn_dets.SetSizer(self.szrConn_Dets)
 
262
        # NEVER SetSizeHints or else grows beyond size!!!!   
 
263
        self.szrConn_Dets.SetVirtualSizeHints(self.scroll_conn_dets)
 
264
        #self.scroll_conn_dets.FitInside() # no effect
 
265
        # BOTTOM        
 
266
        self.szrBottom.Add(self.szrButtons, 0, wx.ALL, 10)
 
267
        self.panel_bottom.SetSizer(self.szrBottom)
 
268
        self.szrBottom.SetSizeHints(self.panel_bottom)
 
269
        # FINAL
 
270
        self.Layout()
 
271
        self.sqlite_grid.grid.SetFocus()
 
272
 
 
273
    def GetProjSettings(self, fil_proj):
 
274
        f = open(os.path.join(LOCAL_PATH, "projs", fil_proj), "r")
 
275
        proj_dic = {}
 
276
        exec f in proj_dic
 
277
        f.close()
 
278
        self.proj_name = fil_proj[:-5]
 
279
        # Taking settings from proj file (via exec and proj_dic)
 
280
        #   and adding them to this frame ready for use.
 
281
        # Must always be stored, even if only ""
 
282
        self.proj_notes = proj_dic["proj_notes"]
 
283
        self.fil_labels = proj_dic["fil_labels"]
 
284
        self.fil_css = proj_dic["fil_css"]
 
285
        self.fil_report = proj_dic["fil_report"]
 
286
        self.fil_script = proj_dic["fil_script"]
 
287
        self.default_dbe = proj_dic["default_dbe"]
 
288
        getdata.getProjConnSettings(self, proj_dic)
 
289
        
 
290
    # report output
 
291
    def OnButtonReportPath(self, event):
 
292
        "Open dialog and takes the report file selected (if any)"
 
293
        dlgGetFile = wx.FileDialog(self, "Choose a report output file:", 
 
294
            defaultDir=os.path.join(LOCAL_PATH, "reports"), 
 
295
            defaultFile="", 
 
296
            wildcard="HTML files (*.htm)|*.htm|HTML files (*.html)|*.html")
 
297
            #MUST have a parent to enforce modal in Windows
 
298
        if dlgGetFile.ShowModal() == wx.ID_OK:
 
299
            self.fil_report = "%s" % dlgGetFile.GetPath()
 
300
            self.txtReportFile.SetValue(self.fil_report)
 
301
        dlgGetFile.Destroy()
 
302
        
 
303
    # script output
 
304
    def OnButtonScriptPath(self, event):
 
305
        "Open dialog and takes the script file selected (if any)"
 
306
        dlgGetFile = wx.FileDialog(self, "Choose a file to export scripts to:", 
 
307
            defaultDir=os.path.join(LOCAL_PATH, "scripts"), 
 
308
            defaultFile="", wildcard="Scripts (*.py)|*.py")
 
309
            #MUST have a parent to enforce modal in Windows
 
310
        if dlgGetFile.ShowModal() == wx.ID_OK:
 
311
            self.fil_script = "%s" % dlgGetFile.GetPath()
 
312
            self.txtScriptFile.SetValue(self.fil_script)
 
313
        dlgGetFile.Destroy()
 
314
 
 
315
    # label config
 
316
    def OnButtonLabelPath(self, event):
 
317
        "Open dialog and takes the labels file selected (if any)"
 
318
        dlgGetFile = wx.FileDialog(self, "Choose a label config file:", 
 
319
            defaultDir=os.path.join(LOCAL_PATH, "lbls"), 
 
320
            defaultFile="", wildcard="Config files (*.lbls)|*.lbls")
 
321
            #MUST have a parent to enforce modal in Windows
 
322
        if dlgGetFile.ShowModal() == wx.ID_OK:
 
323
            fil_labels = "%s" % dlgGetFile.GetPath()
 
324
            self.txtLabelsFile.SetValue(fil_labels)
 
325
        dlgGetFile.Destroy()
 
326
 
 
327
    # css table style
 
328
    def OnButtonCssPath(self, event):
 
329
        "Open dialog and takes the css file selected (if any)"
 
330
        dlgGetFile = wx.FileDialog(self, "Choose a css table style file:", 
 
331
            defaultDir=os.path.join(LOCAL_PATH, "css"), 
 
332
            defaultFile="", 
 
333
            wildcard="CSS files (*.css)|*.css")
 
334
            #MUST have a parent to enforce modal in Windows
 
335
        if dlgGetFile.ShowModal() == wx.ID_OK:
 
336
            fil_css = "%s" % dlgGetFile.GetPath()
 
337
            self.txtCssFile.SetValue(fil_css)
 
338
        dlgGetFile.Destroy()
 
339
    
 
340
    def UpdateCss(self):
 
341
        "Update css, including for demo table"
 
342
        self.fil_css = self.txtCssFile.GetValue()
 
343
        self.demo_tab.fil_css = self.fil_css
 
344
    
 
345
    def OnDbeChoice(self, event):
 
346
        sel_dbe_id = self.dropDefault_Dbe.GetSelection()
 
347
        self.default_dbe = getdata.DBES[sel_dbe_id]
 
348
        event.Skip()
 
349
    
 
350
    def SetupButtons(self):
 
351
        """
 
352
        Must have ID of wx.ID_... to trigger validators (no event binding 
 
353
            needed) and for std dialog button layout.
 
354
        NB can only add some buttons as part of standard sizer to be realised.
 
355
        Insert or Add others after the Realize() as required.
 
356
        See http://aspn.activestate.com/ASPN/Mail/Message/wxpython-users/3605904
 
357
        and http://aspn.activestate.com/ASPN/Mail/Message/wxpython-users/3605432
 
358
        """
 
359
        btnDelete = wx.Button(self.panel_bottom, wx.ID_DELETE)
 
360
        btnDelete.Bind(wx.EVT_BUTTON, self.OnDelete)
 
361
        btnCancel = wx.Button(self.panel_bottom, wx.ID_CANCEL) # 
 
362
        btnCancel.Bind(wx.EVT_BUTTON, self.OnCancel)
 
363
        if self.read_only:
 
364
            btnDelete.Disable()
 
365
            btnCancel.Disable()
 
366
        btnOK = wx.Button(self.panel_bottom, wx.ID_OK)
 
367
        btnOK.Bind(wx.EVT_BUTTON, self.OnOK)
 
368
        self.szrButtons = wx.StdDialogButtonSizer()
 
369
        self.szrButtons.AddButton(btnCancel)
 
370
        self.szrButtons.AddButton(btnOK)
 
371
        self.szrButtons.Realize()
 
372
        self.szrButtons.Insert(0, btnDelete, 0)
 
373
 
 
374
    def OnDelete(self, event):
 
375
        proj_name = self.txtName.GetValue()
 
376
        if wx.MessageBox("Deleting a project cannot be undone.  " + \
 
377
                "Do you want to delete the \"%s\" project?" % \
 
378
                proj_name, 
 
379
                style=wx.YES|wx.NO|wx.ICON_EXCLAMATION|wx.NO_DEFAULT) == wx.NO:
 
380
            return
 
381
        try:
 
382
            fil_to_delete = os.path.join(LOCAL_PATH, "projs", 
 
383
                                   "%s.proj" % self.txtName.GetValue())
 
384
            #print fil_to_delete # debug
 
385
            os.remove(fil_to_delete)
 
386
        except Exception:
 
387
            pass
 
388
        self.Destroy()
 
389
        self.SetReturnCode(wx.ID_DELETE) # only for dialogs 
 
390
        # (MUST come after Destroy)
 
391
 
 
392
    def OnCancel(self, event):
 
393
        "Close returning us to wherever we came from"
 
394
        self.Destroy()
 
395
        self.SetReturnCode(wx.ID_CANCEL) # only for dialogs 
 
396
        # (MUST come after Destroy)
 
397
 
 
398
    def getFileName(self, path):
 
399
        "Works on Windows paths as well"
 
400
        path = path.replace("\\", "/")
 
401
        return os.path.split(path)[1]
 
402
       
 
403
    def OnOK(self, event):
 
404
        # get the data (separated for easier debugging)
 
405
        if not self.read_only:
 
406
            proj_name = self.txtName.GetValue()
 
407
            if proj_name == EMPTY_PROJ_NAME:
 
408
                wx.MessageBox("Please provide a project name")
 
409
                self.txtName.SetFocus()
 
410
                return
 
411
            try:
 
412
                # only needed if returning to projselect form
 
413
                # so OK to fail otherwise
 
414
               self.parent.StoreProjName("%s.proj" % proj_name)
 
415
            except Exception:
 
416
                print "Failed to change to %s.proj" % proj_name
 
417
                pass
 
418
            proj_notes = self.txtProjNotes.GetValue()
 
419
            fil_labels = self.txtLabelsFile.GetValue()
 
420
            fil_css = self.txtCssFile.GetValue()
 
421
            fil_report = self.txtReportFile.GetValue()
 
422
            fil_script = self.txtScriptFile.GetValue()
 
423
            default_dbe = getdata.DBES[self.dropDefault_Dbe.GetSelection()]
 
424
            default_dbs = {}
 
425
            default_tbls = {}
 
426
            conn_dets = {}
 
427
            any_incomplete, any_conns, completed_dbes = \
 
428
                getdata.processConnDets(self, default_dbs, default_tbls, 
 
429
                                        conn_dets)
 
430
            if any_incomplete:
 
431
                return
 
432
            enough_completed = proj_name and any_conns
 
433
            if not enough_completed:
 
434
                wx.MessageBox("Not enough details completed to " + \
 
435
                              "save a project file")
 
436
                return
 
437
            default_dbe_lacks_conn = default_dbe not in completed_dbes
 
438
            if default_dbe_lacks_conn:
 
439
                wx.MessageBox("Connection details need to be completed " + \
 
440
                      "for the default database engine (%s) to save a " + \
 
441
                      "project file" % default_dbe)
 
442
                return
 
443
            # write the data
 
444
            fil_name = os.path.join(LOCAL_PATH, "projs", "%s.proj" % \
 
445
                                  proj_name)
 
446
            f = open(fil_name, "w")
 
447
            f.write("proj_notes = \"%s\"" % proj_notes)
 
448
            f.write("\nfil_labels = r\"%s\"" % fil_labels)
 
449
            f.write("\nfil_css = r\"%s\"" % fil_css)
 
450
            f.write("\nfil_report = r\"%s\"" % fil_report)
 
451
            f.write("\nfil_script = r\"%s\"" % fil_script)
 
452
            f.write("\ndefault_dbe = \"%s\"" % default_dbe)
 
453
            f.write("\n\ndefault_dbs = " + pprint.pformat(default_dbs))
 
454
            f.write("\n\ndefault_tbls = " + pprint.pformat(default_tbls))
 
455
            f.write("\n\nconn_dets = " + pprint.pformat(conn_dets))
 
456
            f.close()
 
457
            if self.new_proj:
 
458
                self.parent.parent.SetProj(proj_name)
 
459
        self.Destroy()
 
460
        self.SetReturnCode(wx.ID_OK) # only for dialogs 
 
461
        # (MUST come after Destroy)
 
462