source: moxy/trunk/src/moxy/root.py @ 635

Last change on this file since 635 was 635, checked in by jemian, 12 years ago

fix the imports, add some TODO items - more to come

  • Property svn:eol-style set to native
  • Property svn:executable set to *
  • Property svn:keywords set to Date Revision Author HeadURL Id
File size: 33.7 KB
Line 
1#!/usr/bin/env python
2#Boa:Frame:root
3
4#*************************************************************************
5# Copyright (c) 2009-2010 The University of Chicago, as Operator of Argonne
6#     National Laboratory.
7# Copyright (c) 2009-2010 The Regents of the University of California, as
8#     Operator of Los Alamos National Laboratory.
9# This file is distributed subject to a Software License Agreement found
10# in the file LICENSE that is included with this distribution.
11#*************************************************************************
12
13'''root: Define the GUI elements and interface (this is the main code)
14
15########### SVN repository information ###################
16# $Date: 2011-09-09 17:29:58 +0000 (Fri, 09 Sep 2011) $
17# $Author: jemian $
18# $Revision: 635 $
19# $URL: moxy/trunk/src/moxy/root.py $
20# $Id: root.py 635 2011-09-09 17:29:58Z jemian $
21########### SVN repository information ###################
22
23@note: for an undo example, see: http://wiki.wxpython.org/AnotherTutorial
24'''
25
26
27import os, datetime, copy, inspect, wx
28from wx.lib.wordwrap import wordwrap
29import pair
30import tab
31import row
32import moxy_xml
33import pvsetup
34import version
35import htmlview
36
37
38def create(parent):
39    '''created by Boa-constructor'''
40    return root(parent)
41
42#################################
43###       added methods       ###
44#################################
45
46
47[wxID_ROOT, wxID_ROOTPAGEBOOK, wxID_ROOTSTATUSBAR1, 
48] = [wx.NewId() for _init_ctrls in range(3)]
49
50[wxID_ROOTMENUFILECLOSE, wxID_ROOTMENUFILEEXIT, wxID_ROOTMENUFILEEXPORT, 
51 wxID_ROOTMENUFILEIMPORT, wxID_ROOTMENUFILENEW, wxID_ROOTMENUFILEOPEN, 
52 wxID_ROOTMENUFILEPREFERENCES, wxID_ROOTMENUFILESAVE, wxID_ROOTMENUFILESAVEAS, 
53] = [wx.NewId() for _init_coll_menuFile_Items in range(9)]
54
55[wxID_ROOTMENUABOUTABOUT, wxID_ROOTMENUABOUTHELP, 
56] = [wx.NewId() for _init_coll_menuAbout_Items in range(2)]
57
58[wxID_ROOTMENUPAGECHOICECHANGEPAIRTITLE, 
59 wxID_ROOTMENUPAGECHOICECHANGETABTITLE, wxID_ROOTMENUPAGECHOICEDELETEPAIR, 
60 wxID_ROOTMENUPAGECHOICEDELETETAB, wxID_ROOTMENUPAGECHOICEEPICSCONFIG, 
61 wxID_ROOTMENUPAGECHOICENEWPAIR, wxID_ROOTMENUPAGECHOICENEWROW, 
62 wxID_ROOTMENUPAGECHOICENEWTAB, 
63] = [wx.NewId() for _init_coll_menuPage_Items in range(8)]
64
65
66class root(wx.Frame):
67    '''moxy: Define the GUI elements and interface'''
68
69    # see:  http://wiki.wxpython.org/BoaFAQ   
70    _custom_classes = {'wx.Panel': ['XYpair']} 
71
72    def _init_coll_menuBar1_Menus(self, parent):
73        # generated method, don't edit
74
75        parent.Append(menu=self.menuFile, title=u'File')
76        parent.Append(menu=self.menuEdit, title=u'Edit')
77        parent.Append(menu=self.menuPage, title=u'Page')
78        parent.Append(menu=self.menuAbout, title=u'About')
79
80    def _init_coll_menuPage_Items(self, parent):
81        # generated method, don't edit
82
83        parent.Append(help='Create settings for a new X,Y pair of EPICS motors',
84              id=wxID_ROOTMENUPAGECHOICENEWPAIR, kind=wx.ITEM_NORMAL,
85              text='Create New X,Y pair\tCtrl+p')
86        parent.Append(help=u'Delete settings for a new X,Y pair of EPICS motors',
87              id=wxID_ROOTMENUPAGECHOICEDELETEPAIR, kind=wx.ITEM_NORMAL,
88              text='Delete this X,Y pair\tCtrl+Shift+p')
89        parent.Append(help=u'Change the title for this X,Y pair',
90              id=wxID_ROOTMENUPAGECHOICECHANGEPAIRTITLE, kind=wx.ITEM_NORMAL,
91              text='Change X,Y pair title\tCtrl+Shift+m')
92        parent.AppendSeparator()
93        parent.Append(help='Create a new tab of settings for this X,Y pair of EPICS motors',
94              id=wxID_ROOTMENUPAGECHOICENEWTAB, kind=wx.ITEM_NORMAL,
95              text='Create new tab\tCtrl+t')
96        parent.Append(help='Delete this tab of settings for this X,Y pair of EPICS motors',
97              id=wxID_ROOTMENUPAGECHOICEDELETETAB, kind=wx.ITEM_NORMAL,
98              text='Delete tab\tCtrl+Shift+t')
99        parent.Append(help='', id=wxID_ROOTMENUPAGECHOICECHANGETABTITLE,
100              kind=wx.ITEM_NORMAL, text='Change tab title\tCtrl+m')
101        parent.AppendSeparator()
102        parent.Append(help='Create a new row for settings for this X,Y pair of EPICS motors',
103              id=wxID_ROOTMENUPAGECHOICENEWROW, kind=wx.ITEM_NORMAL,
104              text='Create new row\tCtrl+r')
105        parent.AppendSeparator()
106        parent.Append(help='Configure the EPICS PVs for this X,Y pair',
107              id=wxID_ROOTMENUPAGECHOICEEPICSCONFIG, kind=wx.ITEM_NORMAL,
108              text=u'EPICS configuration\tCtrl+Shift+e')
109        self.Bind(wx.EVT_MENU, self.OnMenuPageChoicenewpairMenu,
110              id=wxID_ROOTMENUPAGECHOICENEWPAIR)
111        self.Bind(wx.EVT_MENU, self.OnMenuPageChoicedeletepairMenu,
112              id=wxID_ROOTMENUPAGECHOICEDELETEPAIR)
113        self.Bind(wx.EVT_MENU, self.OnMenuPageChoicechangetitleMenu,
114              id=wxID_ROOTMENUPAGECHOICECHANGEPAIRTITLE)
115        self.Bind(wx.EVT_MENU, self.OnMenuPageChoicenewrowMenu,
116              id=wxID_ROOTMENUPAGECHOICENEWROW)
117        self.Bind(wx.EVT_MENU, self.OnMenuPageChoicenewtabMenu,
118              id=wxID_ROOTMENUPAGECHOICENEWTAB)
119        self.Bind(wx.EVT_MENU, self.OnMenuPageChoicedeletetabMenu,
120              id=wxID_ROOTMENUPAGECHOICEDELETETAB)
121        self.Bind(wx.EVT_MENU, self.OnMenuPageChoicechangetabtitleMenu,
122              id=wxID_ROOTMENUPAGECHOICECHANGETABTITLE)
123        self.Bind(wx.EVT_MENU, self.OnMenuPageChoiceepicsconfigMenu,
124              id=wxID_ROOTMENUPAGECHOICEEPICSCONFIG)
125
126    def _init_coll_menuFile_Items(self, parent):
127        # generated method, don't edit
128
129        parent.Append(help=u'Create a new set', id=wxID_ROOTMENUFILENEW,
130              kind=wx.ITEM_NORMAL, text=u'New\tCtrl+n')
131        parent.Append(help=u'Open a configuration file',
132              id=wxID_ROOTMENUFILEOPEN, kind=wx.ITEM_NORMAL,
133              text=u'Open ...\tCtrl+o')
134        parent.Append(help=u'Close the current file', id=wxID_ROOTMENUFILECLOSE,
135              kind=wx.ITEM_NORMAL, text=u'Close\tCtrl+w')
136        parent.AppendSeparator()
137        parent.Append(help=u'Record the settings to the current file',
138              id=wxID_ROOTMENUFILESAVE, kind=wx.ITEM_NORMAL,
139              text=u'Save\tCtrl+s')
140        parent.Append(help=u'Choose a file to record the settings',
141              id=wxID_ROOTMENUFILESAVEAS, kind=wx.ITEM_NORMAL,
142              text=u'Save As ...\tCtrl+Shift+s')
143        parent.AppendSeparator()
144        parent.Append(help=u'Import Row data (label, x, y) from a tab-separated file',
145              id=wxID_ROOTMENUFILEIMPORT, kind=wx.ITEM_NORMAL,
146              text=u'Import Rows ...\tCtrl+i')
147        parent.Append(help=u'Export Row data (label, x, y) to a tab-separated file',
148              id=wxID_ROOTMENUFILEEXPORT, kind=wx.ITEM_NORMAL,
149              text=u'Export Rows ...\tCtrl+e')
150        parent.Append(help='Manage the program defaults',
151              id=wxID_ROOTMENUFILEPREFERENCES, kind=wx.ITEM_NORMAL,
152              text='Preferences ...\tAlt+p')
153        parent.AppendSeparator()
154        parent.Append(help=u'Quit the moxy application',
155              id=wxID_ROOTMENUFILEEXIT, kind=wx.ITEM_NORMAL, text=u'Exit')
156        self.Bind(wx.EVT_MENU, self.OnMenuFileExitMenu,
157              id=wxID_ROOTMENUFILEEXIT)
158        self.Bind(wx.EVT_MENU, self.OnMenuFileCloseMenu,
159              id=wxID_ROOTMENUFILECLOSE)
160        self.Bind(wx.EVT_MENU, self.OnMenuFileImportMenu,
161              id=wxID_ROOTMENUFILEIMPORT)
162        self.Bind(wx.EVT_MENU, self.OnMenuFileNewMenu, id=wxID_ROOTMENUFILENEW)
163        self.Bind(wx.EVT_MENU, self.OnMenuFileOpenMenu,
164              id=wxID_ROOTMENUFILEOPEN)
165        self.Bind(wx.EVT_MENU, self.OnMenuFileSaveMenu,
166              id=wxID_ROOTMENUFILESAVE)
167        self.Bind(wx.EVT_MENU, self.OnMenuFileSaveasMenu,
168              id=wxID_ROOTMENUFILESAVEAS)
169        self.Bind(wx.EVT_MENU, self.OnMenuFilePreferencesMenu,
170              id=wxID_ROOTMENUFILEPREFERENCES)
171        self.Bind(wx.EVT_MENU, self.OnMenuFileExportMenu,
172              id=wxID_ROOTMENUFILEEXPORT)
173
174    def _init_coll_menuAbout_Items(self, parent):
175        # generated method, don't edit
176
177        parent.Append(help=u'Not ready yet', id=wxID_ROOTMENUABOUTHELP,
178              kind=wx.ITEM_NORMAL, text=u'Help')
179        parent.Append(help=u'General information about moxy',
180              id=wxID_ROOTMENUABOUTABOUT, kind=wx.ITEM_NORMAL,
181              text=u'About ...')
182        self.Bind(wx.EVT_MENU, self.ShowAbout, id=wxID_ROOTMENUABOUTABOUT)
183        self.Bind(wx.EVT_MENU, self.OnMenuAboutHelpMenu,
184              id=wxID_ROOTMENUABOUTHELP)
185
186    def _init_coll_statusBar1_Fields(self, parent):
187        # generated method, don't edit
188        parent.SetFieldsCount(1)
189
190        parent.SetStatusText(number=0, text=u'status')
191
192        parent.SetStatusWidths([-1])
193
194    def _init_utils(self):
195        # generated method, don't edit
196        self.menuFile = wx.Menu(title='')
197
198        self.menuEdit = wx.Menu(title='')
199
200        self.menuPage = wx.Menu(title='')
201
202        self.menuAbout = wx.Menu(title='')
203
204        self.menuBar1 = wx.MenuBar()
205
206        self._init_coll_menuFile_Items(self.menuFile)
207        self._init_coll_menuPage_Items(self.menuPage)
208        self._init_coll_menuAbout_Items(self.menuAbout)
209        self._init_coll_menuBar1_Menus(self.menuBar1)
210
211    def _init_ctrls(self, prnt):
212        # generated method, don't edit
213        wx.Frame.__init__(self, id=wxID_ROOT, name=u'root', parent=prnt,
214              pos=wx.Point(312, 25), size=wx.Size(416, 504),
215              style=wx.DEFAULT_FRAME_STYLE, title=u'moxy')
216        self._init_utils()
217        self.SetClientSize(wx.Size(408, 470))
218        self.SetMinSize(wx.Size(408, 420))
219        self.SetMenuBar(self.menuBar1)
220
221        self.statusBar1 = wx.StatusBar(id=wxID_ROOTSTATUSBAR1,
222              name='statusBar1', parent=self, style=0)
223        self._init_coll_statusBar1_Fields(self.statusBar1)
224        self.SetStatusBar(self.statusBar1)
225
226        self.pagebook = wx.Notebook(id=wxID_ROOTPAGEBOOK, name=u'pagebook',
227              parent=self, pos=wx.Point(0, 0), size=wx.Size(408, 427), style=0)
228        self.pagebook.SetToolTipString(u'Each "page" describes a different X,Y pair of EPICS motors')
229
230    def __init__(self, parent, settingsFile = None):
231        '''This is the main application window and class.
232            @param parent: object that owns this window
233            @param settingsFile: [string] name of the XML file'''
234        self.paircounter = 0  # incrementing index to create page names
235        self._init_ctrls(parent)
236        self.title = self.GetTitle()
237        self._dirty(False)    # settings need to be saved to a file if True
238        self.pwd = os.getcwd()
239        self.settingsFile = settingsFile
240        if self.settingsFile == None:
241            self.NewPair()        # by default, make a starting space
242        else:
243            self.SetStatusText('opening: %s' % self.settingsFile)
244            self.OpenSettingsFile(self.settingsFile)
245        # disable these menu items until they are implemented
246        #self.menuAbout.FindItemById(wxID_ROOTMENUABOUTHELP).Enable(False)
247        self.menuFile.FindItemById(wxID_ROOTMENUFILEEXPORT).Enable(False)
248        self.menuFile.FindItemById(wxID_ROOTMENUFILEPREFERENCES).Enable(False)
249
250
251# ################################
252# ##       added methods       ###
253# ################################
254
255    def PairHandler(self, thePair, theTab, theRow, command):
256        '''Callback function to handle a command from a pair
257            @param thePair: pair.XYpair object
258            @param theTab: tab.Tab object
259            @param theRow: row.Row object
260            @param command: [string] action from Row button'''
261        commandSet = ['delete', 'set', 'go', 'stop']
262        if command not in commandSet:
263            self.SetStatusText('Unknown command: %s' % command)
264            return
265        if command == 'delete':
266            self.DeleteRow(thePair, theTab, theRow)
267        if command == 'set':
268            self.SetRow(thePair, theTab, theRow)
269        if command == 'go':
270            self.GoRow(thePair, theTab, theRow)
271        if command == 'stop':
272            self.StopPair(thePair)
273
274    def _dirty(self, state=True):
275        '''Declare the settings dirty (means that changes are unsaved)
276            @param state: [Boolean] True means there are unsaved changes'''
277        self.dirty = state
278        title = self.title
279        if state == True:
280            title = '* ' + title + ' *'
281        self.SetTitle(title)
282
283    def NewPair(self, newtab=True):
284        '''Create a page for a new X,Y pair
285            @param newtab: [Boolean] option to create a first tab'''
286        self.paircounter += 1   # unique for each new page
287        name = 'panel' + repr(self.paircounter)
288        text = 'pair ' + repr(self.paircounter)
289        panel = pair.XYpair(name=name, parent=self.pagebook,
290                   root=self, rootCallback=self.PairHandler, newtab=newtab)
291        self.pagebook.AddPage(imageId=-1, page=panel, select=True, text=text)
292        panel.SetPageTitle(text)
293        self.SetStatusText('Created page titled: %s' % text)
294        self.Layout()
295        self._dirty()
296        return panel
297
298    def GetPairText(self, pairnum):
299        '''@param pairnum: [int] index number of the XY_pair
300            @return: text of the pair numbered pairnum'''
301        return self.pagebook.GetPageText(pairnum)
302
303    def SetPairText(self, pairnum, text):
304        '''set the text of the pair numbered pairnum
305            @param pairnum: [int] index number of the XY_pair
306            @param text: [string] name of the XYpair'''
307        self.pagebook.SetPageText(pairnum, text)
308        panel = self.pagebook.GetPage(pairnum)
309        panel.SetPageTitle(text)
310
311    def DeletePairnum(self, pairnum):
312        '''Delete the selected X,Y pair and settings
313            @param pairnum: [int] index number of the XY_pair'''
314        try:
315            self.pagebook.DeletePage(pairnum)
316        except:
317            self.SetStatusText('Could not delete that pair')
318
319    def GetEpicsConfig(self, pairnum):
320        '''Return the EPICS PV configuration for the indexed X,Y pair
321            @param pairnum: [int] index number of the XY_pair'''
322        panel = self.pagebook.GetPage(pairnum)
323        config = panel.GetEpicsConfig()
324        return config
325
326    def SetEpicsConfig(self, pairnum, config):
327        '''Define the EPICS PVs for the indexed X,Y pair
328            @param pairnum: [int] index number of the XY_pair
329            @param config: Python dictionary of EPICS PV configuration'''
330        panel = self.pagebook.GetPage(pairnum)
331        panel.SetEpicsConfig(config)
332        panel.ConnectEpics()
333
334    def RequestConfirmation(self, command, text):
335        '''Present a dialog asking user to confirm step
336            @param command: [string] action to be confirmed
337            @param text: [string] message to user'''
338        # confirm this step
339        self.SetStatusText('Request Confirmation')
340        dlg = wx.MessageDialog(self, text, 
341                'Confirm %s' % command, 
342                wx.YES|wx.NO)
343        result = dlg.ShowModal()
344        dlg.Destroy()           # destroy first
345        if result == wx.ID_YES:
346            self.SetStatusText('accepted request: %s' % command)
347        else:
348            self.SetStatusText('canceled request: %s' % command)
349        return result
350
351    def DeleteRow(self, thePair, theTab, theRow):
352        '''Process a 'delete' command from a row button
353            @param thePair: pair.XYpair object
354            @param theTab: tab.Tab object
355            @param theRow: row.Row object'''
356        text = 'Delete row labeled: %s' % theRow.GetLabel()
357        # confirm this step
358        result = self.RequestConfirmation('Delete Row', text + '?')
359        if result != wx.ID_YES:
360            return
361        self.SetStatusText(text)
362        theTab.DeleteRow(theRow)
363        self._dirty()
364
365    def SetRow(self, thePair, theTab, theRow):
366        '''Process a 'set' command from a row button
367            @param thePair: pair.XYpair object
368            @param theTab: tab.Tab object
369            @param theRow: row.Row object'''
370        text = theRow.GetLabel()
371        self.SetStatusText('Set X, Y on row labeled: %s' % text)
372        x, y = thePair.GetRbvXY()
373        theRow.SetXY(x, y)
374        if len(theRow.GetLabel().strip()) == 0:
375            t = datetime.datetime.now()
376            yyyymmdd = t.strftime("%Y-%m-%d")
377            hhmmss = t.strftime("%H:%M:%S")
378            theRow.SetLabel(yyyymmdd + ',' + hhmmss)
379        self._dirty()
380
381    def GoRow(self, thePair, theTab, theRow):
382        '''Process a 'go' command from a row button
383            @param thePair: pair.XYpair object
384            @param theTab: tab.Tab object
385            @param theRow: row.Row object'''
386        text = theRow.GetLabel()
387        self.SetStatusText('Move EPICS motors on row labeled: %s' % text)
388        x_txt, y_txt = theRow.GetXY()
389        try:
390            x = float(x_txt)
391            y = float(y_txt)
392        except:
393            self.SetStatusText('X or Y not a number, will not move')
394            return
395        # identify EPICS motors and send them the move commands
396        thePair.MoveAxes(x, y)
397        title = thePair.GetPageTitle()
398        self.SetStatusText('Move %s to (%s, %s)' % (title, x_txt, y_txt))
399
400    def StopPair(self, thePair):
401        '''Process a 'stop' command from a stop button.
402           Need to stop the two associated positioners.
403            @param thePair: pair.XYpair object'''
404        thePair.StopAxes()
405        title = thePair.GetPageTitle()
406        self.SetStatusText('Stop ' + title)
407
408    def ImportRows(self, rowfile):
409        '''Import a row file into the current tab (make a tab if none exists)
410            @param rowfile: [string] name of the 3-column tab-separated file'''
411        self.SetStatusText('file: %s' % rowfile)
412        try:
413            fp = open(rowfile, 'r')
414            buf = fp.read()
415            fp.close()
416        except:
417            self.SetStatusText('Could not read file: %s' % rowfile)
418            return
419        #
420        if self.pagebook.GetSelection() < 0:
421            self.NewPair(newtab=False)  # need to make a new page
422        pagenum = self.pagebook.GetSelection()
423        pair = self.pagebook.GetPage(pagenum)
424        if pair.GetSelection() < 0:
425            pair.NewTab(newrow=False)
426        tabnum = pair.GetSelection()
427        tab = pair.table.GetPage(tabnum)
428        linenum = 0
429        for line in buf.strip().split('\n'):
430            linenum += 1
431            try:
432                label, x, y = line.strip().split('\t')
433                row = tab.NewRow()
434                row.SetLabel(label)
435                row.SetXY(x, y)
436            except:
437                self.SetStatusText('Problem with row: %d' % linenum)
438        tab.Remap()     # adjust for changes
439
440    def OpenSettingsFile(self, settingsFile):
441        '''Open the named settings file and replace all the current settings
442            @param settingsFile: [string] name of the XML file'''
443        try:
444            rc = moxy_xml.Settings(settingsFile)
445            result = rc.ReadXmlFile()
446            if result != None:
447                return result
448        except:
449            self.SetStatusText('Could not open: %s' % settingsFile)
450            return
451        # safe to proceed now
452        self.SetStatusText('Opened: %s' % settingsFile)
453        self.pagebook.DeleteAllPages()
454        self.settingsFile = settingsFile
455        selectedpairnum = rc.GetSelectedPair()
456        for pairnum in range(rc.CountPairs()):
457            pairnode = self.NewPair(newtab=False)
458            self.SetPairText(pairnum, rc.GetPairTitle(pairnum))
459            self.SetEpicsConfig(pairnum, rc.GetEpicsConfig(pairnum))
460            selectedtabnum = rc.GetSelectedTab(pairnum)
461            for tabnum in range(rc.CountTabs(pairnum)):
462                tabnode = pairnode.NewTab(newrow=False)
463                pairnode.SetTabText(tabnum, rc.GetTabTitle(pairnum, tabnum))
464                selectedrownum = rc.GetSelectedRow(pairnum, tabnum)
465                for rownum in range(rc.CountRows(pairnum, tabnum)):
466                    label = rc.GetRowTitle(pairnum, tabnum, rownum)
467                    x, y = rc.GetRowXY(pairnum, tabnum, rownum)
468                    rownode = tabnode.NewRow()
469                    rownode.SetLabel(label)
470                    rownode.SetXY(x, y)
471                tabnode.Remap()
472                if selectedrownum >= 0:
473                    # none of these work correctly in ScrolledPanel
474                    #tabnode.ChangeSelection(selectedrownum)
475                    #tabnode.Scroll(1, 1+selectedrownum*25)
476                    #row = tabnode.sizer.GetItem(rownum).GetWindow()
477                    #tabnode.ScrollChildIntoView(row)
478                    pass
479            if selectedtabnum >= 0:
480                pairnode.table.ChangeSelection(selectedtabnum)
481        if selectedpairnum >= 0:
482            self.pagebook.ChangeSelection(selectedpairnum)
483        self._dirty(False)
484
485    def SaveSettingsFile(self, settingsFile):
486        '''Save the current settings to the named settings file
487            @param settingsFile: [string] name of the XML file'''
488        rc = moxy_xml.Settings(settingsFile)
489        selectedpair = self.pagebook.GetSelection()
490        for pairnum in range(self.pagebook.GetPageCount()):
491            rc.NewPair(self.GetPairText(pairnum))
492            if selectedpair == pairnum:
493                rc.SelectPair(pairnum)
494            pair = self.pagebook.GetPage(pairnum)
495            config = self.GetEpicsConfig(pairnum)
496            rc.SetEpicsConfig(pairnum, config)
497            selectedtab = pair.GetSelection()
498            for tabnum in range(pair.table.GetPageCount()):
499                rc.NewTab(pairnum, pair.GetTabText(tabnum))
500                if selectedtab == tabnum:
501                    rc.SelectTab(pairnum, tabnum)
502                tab = pair.table.GetPage(tabnum)
503                #selectedrow = tab.GetSelection()
504                for rownum in range(len(tab.GetChildren())):
505                    rc.NewRow(pairnum, tabnum, tab.GetRowLabel(rownum))
506                    x, y = tab.GetRowXY(rownum)
507                    rc.SetRowXY(pairnum, tabnum, rownum, x, y)
508        rc.SetSettingsFile(settingsFile)
509        rc.SaveXmlFile()
510        self.settingsFile = settingsFile
511        self._dirty(False)
512
513    def PostNotice(self, title, message, flags):
514        '''post a message dialog box
515            @param title: [string] window title bar
516            @param message: [string] message message text
517            @param flags: dialog box flags (such as wx.OK | wx.ICON_INFORMATION)'''
518        dlg = wx.MessageDialog(None, message, title, flags)
519        dlg.ShowModal()
520        dlg.Destroy()
521
522# ################################
523# ##  event handling routines  ###
524# ################################
525
526    def ShowAbout(self, event):
527        '''describe this application
528           @param event: wxPython event object'''
529        # derived from http://wiki.wxpython.org/Using%20wxPython%20Demo%20Code
530        # First we create and fill the info object
531        info = wx.AboutDialogInfo()
532        info.Name = version.__summary__
533        info.Version = version.__version__
534        info.Copyright = version.__copyright__
535        description = ''
536        for line in version.__documentation__.strip().splitlines():
537            item = line.strip()
538            if len(item) > 0:
539                description += ' ' + line.strip()
540            else:
541                description += '\n\n'
542        info.Description = wordwrap(description, 400, wx.ClientDC(self))
543        URL = version.__url__
544        info.WebSite = (URL, version.__svndesc__)
545        author = version.__author__
546        author += ", " + version.__author_email__
547        others = [ "author: ", author ]
548        others.extend(version.__contributor_credits__)
549        info.Developers = others
550        info.License = version.__license__
551        # Then we call wx.AboutBox giving it the info object
552        wx.AboutBox(info)
553
554    def OnMenuFileNewMenu(self, event):
555        '''Requested new settings
556           @param event: wxPython event object'''
557        self.SetStatusText('Requested new settings')
558        if self.dirty:
559            # confirm this step
560            result = self.RequestConfirmation('New',
561                  'There are unsaved changes.  Create new settings anyway?')
562            if result != wx.ID_YES:
563                return
564        self.pagebook.DeleteAllPages()
565        self.NewPair()
566        self._dirty(False)
567        self.settingsFile = None
568
569    def OnMenuFileOpenMenu(self, event):
570        '''Requested to open XML settings file
571           @param event: wxPython event object'''
572        if self.dirty:
573            # confirm this step
574            result = self.RequestConfirmation('Open ...',
575                  'There are unsaved changes.  Open anyway?')
576            if result != wx.ID_YES:
577                return
578        #---
579        self.SetStatusText('Requested to open XML settings file')
580        wildcard = "XML files (*.xml)|*.xml|" \
581                   "All files (*)|*"
582        instruction = "Choose an XML file with full settings." \
583            " (The selected file will be verified before it is loaded.)"
584        dlg = wx.FileDialog(None, instruction, 
585            self.pwd, "", wildcard, wx.OPEN)
586        if dlg.ShowModal() == wx.ID_OK:
587            self.pwd = os.path.dirname(dlg.GetPath())
588            self.OpenSettingsFile(dlg.GetPath())
589        dlg.Destroy()
590
591    def OnMenuFileSaveMenu(self, event):
592        '''Requested to save settings to XML file
593           @param event: wxPython event object'''
594        if self.settingsFile == None:
595            self.OnMenuFileSaveasMenu(event)
596        else:
597            self.SaveSettingsFile(self.settingsFile)
598
599    def OnMenuFileSaveasMenu(self, event):
600        '''Requested to save settings to new XML file
601           @param event: wxPython event object'''
602        self.SetStatusText('Save current settings to XML file')
603        wildcard = "XML files (*.xml)|*.xml|" \
604                   "All files (*)|*"
605        instruction = "Save current settings to XML file" \
606            " (either existing or new)"
607        dlg = wx.FileDialog(None, instruction, 
608            self.pwd, "", wildcard, wx.SAVE|wx.OVERWRITE_PROMPT)
609        if dlg.ShowModal() == wx.ID_OK:
610            filename = dlg.GetPath()
611            self.pwd = os.path.dirname(filename)
612            self.SaveSettingsFile(filename)
613        dlg.Destroy()
614        self._dirty(False)
615
616    def OnMenuFileCloseMenu(self, event):
617        '''User requested to close the settings file
618           @param event: wxPython event object'''
619        if self.pagebook.GetPageCount() > 0:
620            if self.dirty:
621                # confirm this step
622                result = self.RequestConfirmation('Close All',
623                      'There are unsaved changes.  Close All anyway?')
624                if result != wx.ID_YES:
625                    return
626            self.SetStatusText('Close All requested')
627            self.pagebook.DeleteAllPages()
628        else:
629            self.SetStatusText('Nothing to close')
630        self._dirty(False)
631
632    def OnMenuFileImportMenu(self, event):
633        '''user requested to import a table of settings from a file
634           @param event: wxPython event object'''
635        wildcard = "text files (*.txt)|*.txt|" \
636                   "All files (*)|*"
637        instruction = "Choose a file with row settings"
638        dlg = wx.FileDialog(None, instruction, 
639            self.pwd, "", wildcard, wx.OPEN)
640            # what about changing self.pwd here?
641        if dlg.ShowModal() == wx.ID_OK:
642            self.pwd = os.path.dirname(dlg.GetPath())
643            self.ImportRows(dlg.GetPath())
644        dlg.Destroy()
645
646    def OnMenuFileExportMenu(self, event):
647        '''user requested to export a Tab
648           @param event: wxPython event object
649           @note: Not implemented yet'''
650        event.Skip()
651
652    def OnMenuFilePreferencesMenu(self, event):
653        '''user requested to view/edit preferences
654           @param event: wxPython event object
655           @note: Not implemented yet'''
656        self.SetStatusText('Requested to edit Preferences')
657        self.PostNotice("Construction Zone!",
658                "'Preferences ...' menu item not implemented yet.",
659                wx.OK | wx.ICON_INFORMATION)
660
661    def OnMenuFileExitMenu(self, event):
662        '''User requested to quit the application
663           @param event: wxPython event object'''
664        if self.dirty:
665            # confirm this step
666            result = self.RequestConfirmation('Exit (Quit)',
667                  'There are unsaved changes.  Exit (Quit) anyway?')
668            if result != wx.ID_YES:
669                return
670        self.Close()
671
672    def OnMenuPageChoicenewpairMenu(self, event):
673        '''User requested a new X,Y pair
674           @param event: wxPython event object'''
675        self.NewPair()
676        self._dirty()
677
678    def OnMenuPageChoicedeletepairMenu(self, event):
679        '''User requested to delete the X,Y pair
680           @param event: wxPython event object'''
681        pagenum = self.pagebook.GetSelection()
682        if pagenum < 0:
683            self.SetStatusText('no page to delete')
684            return
685        text = self.pagebook.GetPageText(pagenum)
686        # confirm this step
687        requestText = 'Delete X,Y page [%s]?' % text
688        result = self.RequestConfirmation('Delete X,Y page?',
689              requestText)
690        if result == wx.ID_YES:
691            self.DeletePairnum(pagenum)
692            self.SetStatusText('page was deleted')
693            self._dirty()
694
695    def OnMenuPageChoicechangetitleMenu(self, event):
696        '''User requested to change the page title
697           @param event: wxPython event object'''
698        pagenum = self.pagebook.GetSelection()
699        if pagenum >= 0:
700            self.SetStatusText('requested X,Y page name change')
701            response = wx.GetTextFromUser(parent=self,
702                message='Rename this page of settings:', 
703                caption='Rename this page',
704                default_value=self.pagebook.GetPageText(pagenum))
705            if len(response) > 0:
706                self.SetStatusText('New page title: %s' % response)
707                self.SetPairText(pagenum, response)
708                self._dirty()
709            else:
710                self.SetStatusText('Rename was canceled')
711        else:
712            self.SetStatusText('no page to rename')
713
714    def OnMenuPageChoicenewtabMenu(self, event):
715        '''user requested a new tab
716           @param event: wxPython event object'''
717        pagenum = self.pagebook.GetSelection()
718        if pagenum < 0:
719            self.SetStatusText('No pages now!  Cannot create a tab.')
720            return      # early
721        pair = self.pagebook.GetPage(pagenum)
722        pair.NewTab()
723        self.SetStatusText('Created new tab.')
724        self._dirty()
725
726    def OnMenuPageChoicedeletetabMenu(self, event):
727        '''user requested to delete a tab
728           @param event: wxPython event object'''
729        pagenum = self.pagebook.GetSelection()
730        if pagenum < 0:
731            self.SetStatusText('No pages now!  Cannot delete a tab.')
732            return      # early
733        pair = self.pagebook.GetPage(pagenum)
734        tabnum = pair.GetSelection()
735        if tabnum < 0:
736            self.SetStatusText('No tab to delete.')
737            return
738        text = pair.GetTabText(tabnum)
739        # confirm this step
740        requestText = 'Delete tab [%s]?' % text
741        result = self.RequestConfirmation('Delete tab?',
742              requestText)
743        if result == wx.ID_YES:
744            self.SetStatusText(pair.DeleteTab())
745            self._dirty()
746
747    def OnMenuPageChoicechangetabtitleMenu(self, event):
748        '''user requested to rename tab
749           @param event: wxPython event object'''
750        pagenum = self.pagebook.GetSelection()
751        if pagenum < 0:
752            self.SetStatusText('No pages now!  Cannot rename a tab.')
753            return      # early
754        pair = self.pagebook.GetPage(pagenum)
755        tabnum = pair.GetSelection()
756        if tabnum < 0:
757            self.SetStatusText('No tab to rename.')
758            return
759        text = pair.GetTabText(tabnum)
760        self.SetStatusText('requested tab name change')
761        response = wx.GetTextFromUser(parent=self,
762            message='Rename this tab:', 
763            caption='Rename this tab',
764            default_value=text)
765        if len(response) > 0:
766            self.SetStatusText('New tab name: %s' % response)
767            pair.SetTabText(tabnum, response)
768            self._dirty()
769        else:
770            self.SetStatusText('Rename tab was canceled')
771
772    def OnMenuPageChoicenewrowMenu(self, event):
773        '''user requested to create a new row of settings in the current table
774           @param event: wxPython event object'''
775        pagenum = self.pagebook.GetSelection()
776        if pagenum < 0:
777            self.SetStatusText('No pages now!  Cannot create a row.')
778            return      # early
779        pair = self.pagebook.GetPage(pagenum)
780        tabnum = pair.GetSelection()
781        if tabnum < 0:
782            self.SetStatusText('No tabs now!  Cannot create a row.')
783            return
784        tab = pair.table.GetPage(tabnum)
785        tab.NewRow()
786        tab.Remap()
787        self.SetStatusText('Created new row.')
788        self._dirty()
789
790    def OnMenuAboutHelpMenu(self, event):
791        '''user requested help
792           @param event: wxPython event object
793           @note: Not implemented yet'''
794        self.SetStatusText('starting HTML Help Viewer ...')
795        page = 'index.html'
796        root_dir = os.path.split(inspect.getsourcefile(root))[0]
797        fullname = os.path.join(root_dir, page)
798        htmlview.HtmlView(parent=None, 
799            homepage=fullname, id=-1, 
800            title='HtmlView: '+page).Show()
801
802    def OnMenuPageChoiceepicsconfigMenu(self, event):
803        '''user requested to view/change EPICS PV configuration
804           @param event: wxPython event object'''
805        self.SetStatusText('Modifying EPICS PV configuration details')
806        pagenum = self.pagebook.GetSelection()
807        if pagenum < 0:
808            self.SetStatusText('No pages now!  Cannot modify EPICS PV configuration.')
809            return      # early
810        orig_cfg = copy.deepcopy(self.GetEpicsConfig(pagenum))
811        dlg = pvsetup.PvDialog(None, orig_cfg)
812        try:
813            result = dlg.ShowModal()
814        finally:
815            if result == wx.ID_OK:
816                new_cfg = copy.deepcopy(dlg.GetConfiguration())
817                # smarter way is to compare orig_cfg and new_fg
818                self._dirty()
819                self.SetEpicsConfig(pagenum, new_cfg)
820            dlg.Destroy()
Note: See TracBrowser for help on using the repository browser.