source: branches/GUIdev/DuchampParameterGUI.py @ 1441

Last change on this file since 1441 was 1331, checked in by KelvinHsu, 11 years ago

Some clean up of code before splitting into files.

  • Property svn:executable set to *
File size: 36.2 KB
RevLine 
[1307]1#!/usr/bin/env python
2
3#-------------------------------------------------------------------------------
[1325]4# Name:        DuchampParameterGUI
[1307]5# Purpose:
6#
7# Author:      Kelvin
8#
9# Created:     4/12/2013
10#-------------------------------------------------------------------------------
11
[1310]12import os
13
[1316]14# Python 2 packages
15try:
16    from Tkinter import *
17    import tkFileDialog
18
19# Python 3 packages
20except ImportError:
21    from tkinter import *
22    import tkinter.filedialog as tkFileDialog
23
24
[1308]25# Determine the current directory.
[1307]26directory = os.getcwd()
27
[1318]28# Define the default file path.
[1325]29defaultParamFileName = '/InputComplete'
[1331]30defaultParamFilePath = directory + defaultParamFileName
[1307]31
32
[1329]33# This reads parameter files with the specified format.
[1307]34def readParamFile(paramFilePath):
35
[1329]36    # Create a dictionary that maps parameter names to its value and a list to keep track of its order.
[1307]37    parameters = {}
[1316]38    orderedParameters = []
[1307]39
40    try:
41        # Open the given parameter file.
42        paramFile = open(paramFilePath)
43
44        # Load files for lines of parameter data in the specified format and
45        # store them into the dictionary.
46        for line in paramFile:
[1325]47            if line[0] != '#' and len(line.split()) > 1:
[1307]48                linebits = line.split()
49                parameters[linebits[0]] = linebits[1]
[1316]50                orderedParameters = orderedParameters + [linebits[0]]
[1307]51
52        paramFile.close()
[1308]53
54    # Note that if the file could not be read, then the dictionary will stay empty.
55    # The GUI window can make use of this fact.
[1307]56    except IOError:
57        print("Cannot open file")
58
[1316]59    return(parameters, orderedParameters)
[1307]60
[1311]61
[1329]62# This reads the default file for all relevant information of parameter classes, description, type, and format.
[1311]63def readParamByClass(paramFilePath):
64
[1329]65    # Create a diciontary that maps parameter names to its class and a list to keep track of class orders.
[1311]66    classOfParameter = {}
[1329]67    classNames = []
[1311]68
[1329]69    # Create dictionaries that maps parameter names to its description, value type, and value format.
[1316]70    description = {}
[1325]71    paramType = {}
72    paramFormat = {}
[1316]73
[1311]74    try:
75        # Open the given parameter file.
76        paramFile = open(paramFilePath)
77
78        # Load files for lines of parameter data in the specified format and
[1329]79        # store them into the dictionaries.
[1311]80        for line in paramFile:
81
[1325]82            if line[0:3] == '###' and len(line.split('###')) > 1:
83                className = line.strip('###').strip()
[1316]84                classNames = classNames + [className]
85
[1325]86            elif line[0:2] == '#*' and len(line.split()) > 1 and '--' in line:
87                param = line.split()[1].strip()
88                paramType[param] = line.split('[')[1].split(']')[0].strip()
89                paramFormat[param] = line.split('{')[1].split('}')[0].strip()
90                description[param] = '--'.join(line.split('--')[1:len(line.split('--'))]).strip()
[1316]91               
[1325]92            elif line[0] != '#' and len(line.split()) > 1:
[1311]93                linebits = line.split()
94                classOfParameter[linebits[0]] = className
95
96        paramFile.close()
97
98    # Note that if the file could not be read, then the dictionary will stay empty.
99    # The GUI window can make use of this fact.
100    except IOError:
101        print("Cannot open file")
102                 
[1325]103    return(classOfParameter, classNames, description, paramType, paramFormat)
[1311]104
105
[1307]106# Define a function to create a parameter file with the specified format.
[1329]107# It will return 0 if the file already exists and will not overwrite the file just yet (unless write = 1)
108#                1 if the file is successfully created (or overwriten)
109#               -1 if the file was not successfully created (or overwriten) due to some error
[1316]110def writeParamFile(newParamFilePath, parameters, parameterOrder, write):
[1307]111
[1308]112    # If we want to make sure if it is okay to write, do the following.
[1329]113    if write != 1:
114       
[1308]115        # First, try opening the file for reading.
116        # If the file exists for reading, then close it and then return a 0,
117        # which the GUI window use to warn the user that a file with that name already exists.
118        try:
[1316]119            newParamFile = open(newParamFilePath)
[1308]120            newParamFile.close()
121            return(0)
[1307]122
[1308]123        # If we have trouble reading the file, then the file doesn't exist and we're safe to write to it.
124        except IOError:
125            write = 1
[1307]126
[1308]127    # If we are sure we can write to the file, then do it.
128    if write == 1:
[1307]129
[1308]130        try:
[1316]131            newParamFile = open(newParamFilePath, 'w')
[1308]132           
133            # Write the editted set of parameters into the new file.
[1316]134            for par in parameterOrder:
[1308]135                newParamFile.write('%s    %s\n'%(par, parameters[par]))
[1307]136
[1308]137            newParamFile.close()
138            return(1)
139       
140        except IOError:
141            return(-1)
[1307]142
[1329]143
[1331]144# This function determines the longest key in a dictionary.
145def longestKeyLength(dictionary):
[1329]146    return(len(max(dictionary.keys(), key = len)))
[1307]147
[1308]148# This function determines the longest value in a dictionary.
149def longestValueLength(dictionary):
[1329]150    return(len(max(dictionary.values(), key = len)))
[1308]151
[1331]152# This is the minimum space between the a key and its value in a dictionary when we want to display it.
[1307]153minimumSpace = 5
154
[1331]155# This appends the appropriate space after a single key in a dictionary.
[1308]156# Note: Can this be optimised?
[1331]157def tabbedDictionaryLine(dictionary, key, star):
[1307]158
[1331]159    if key in dictionary:
[1307]160
[1308]161        # Determine the residual space our word will have compared to the longest word.
[1331]162        residualSpace = longestKeyLength(dictionary) - len(key)
[1311]163       
164        if star == 0:
[1307]165
[1311]166            # Add extra space after the word based on the minimum separatio between the word and its definition.
167            space = ' '*(minimumSpace + residualSpace)
[1331]168            tabbedKey = key + space
[1307]169
[1311]170            # Return the word with the appropriate spaces appended.
[1331]171            return(tabbedKey)
[1311]172        elif star == 1:
[1308]173
[1311]174            star = ' (*) '
175           
176            # Add extra space after the word based on the minimum separatio between the word and its definition.
177            space = star + ' '*residualSpace + ' '*(minimumSpace - len(star))
[1331]178            tabbedKey = key + space
[1311]179
180            # Return the word with the appropriate spaces appended.
[1331]181            return(tabbedKey)
[1311]182
183        else:
184            print("Coding Error: 'Star' is not binary")
185
[1308]186    # This exception is not needed, as the function will always be called with a word within the dictionary.
[1307]187    else:
[1331]188        print(key + "is not in the dictionary")
[1307]189        return(' ')
190
[1308]191# This determines the longest line that will be printed in a dictionary given the above method.
192def longestLineLength(dictionary):
[1307]193
[1331]194    maxLineLength = longestKeyLength(dictionary) + longestValueLength(dictionary) + minimumSpace
[1308]195    return(maxLineLength)
[1317]196
[1331]197# Given an array of string, this returns an array of the same strings but in lower cases.
[1317]198def lower(stringArray):
199
200    lowerArray = []
201
202    L = len(stringArray)
203   
204    for i in range(L):
205        lowerArray += [stringArray[i].lower()]
206
207    return lowerArray
208
[1331]209
[1307]210#-------------------------------------------------------------------------------
211# GUI - Tkinter section
212
213class mainInterface:
214
215    def __init__(self, master):
[1308]216       
217        # --------------------------------------------------
218        # Initialise the GUI window.
[1307]219        self.frame = Frame(master)
220        self.frame.grid()
[1308]221        # --------------------------------------------------
[1307]222
[1331]223
[1311]224        # --------------------------------------------------
225        # Menu
[1308]226        self.menubar = Menu(master)
227
[1311]228        # File Menu
229        self.filemenu = Menu(self.menubar, tearoff = 0)
230        self.filemenu.add_command(label = "Open", command = self.createInputInterface)
[1317]231        self.filemenu.add_command(label = "Save specification", command = self.createSpecifiedOutputInterface)
232        self.filemenu.add_command(label = "Save all specification", command = self.createEffectiveOutputInterface)
[1308]233        self.filemenu.add_separator()
[1311]234        self.filemenu.add_command(label = "Exit", command = self.frame.quit)
235        self.menubar.add_cascade(label = "File", menu = self.filemenu)
[1308]236
237
[1311]238        # Options Menu
239        self.editmenu = Menu(self.menubar, tearoff = 0)
240        self.editmenu.add_command(label = "Cut", command = self.notImplementedYet)
241        self.editmenu.add_command(label = "Copy", command = self.notImplementedYet)
242        self.editmenu.add_command(label = "Paste", command = self.notImplementedYet)
243        self.menubar.add_cascade(label = "Options", menu = self.editmenu)
[1308]244
[1311]245        # Help Menu
246        self.helpmenu = Menu(self.menubar, tearoff = 0)
247        self.helpmenu.add_command(label = "About", command = self.notImplementedYet)
248        self.menubar.add_cascade(label = "Help", menu = self.helpmenu)
249        # --------------------------------------------------
[1331]250   
[1308]251       
[1309]252        # Row 1-10 reserved for future extensions
[1307]253       
[1331]254       
[1308]255        # --------------------------------------------------
256        # Parameter name and value entry
[1307]257        self.parameterLabel = Label(self.frame, text = "Parameter: ", foreground = "Blue")
258        self.parameterLabel.grid(row = 11, column = 1, columnspan = 2)
259
[1308]260        self.valueLabel = Label(self.frame, text = "Value: ", foreground = "Forest Green")
[1307]261        self.valueLabel.grid(row = 11, column = 3, columnspan = 2)
262
263        self.parameterEntry = Entry(self.frame, width = 25)
[1312]264        self.parameterEntry.bind("<Return>", self.checkParameterEvent)
[1307]265        self.parameterEntry.grid(row = 12, column = 1, padx = 10, columnspan = 2)
266
267        self.valueEntry = Entry(self.frame, width = 25)
[1312]268        self.valueEntry.bind("<Return>", self.updateParameterEvent)
[1307]269        self.valueEntry.grid(row = 12, column = 3, padx = 10, columnspan = 2)
[1308]270        # --------------------------------------------------
[1307]271
[1331]272
[1308]273        # --------------------------------------------------
274        # Check, update, and describe parameter buttons
[1312]275        self.checkButton = Button(self.frame, text = "Check", command = self.checkParameterWithDescription)
[1307]276        self.checkButton.grid(row = 13, column = 1, padx = 10, pady = 10)
[1317]277       
[1307]278        self.updateButton = Button(self.frame, text = "Update", command = self.updateParameter)
279        self.updateButton.grid(row = 13, column = 2, padx = 10, pady = 10)
280
[1317]281        self.revertDefaultButton = Button(self.frame, text = "Revert to default", command = self.revertDefault)
282        self.revertDefaultButton.grid(row = 13, column = 4, padx = 10, pady = 10, columnspan = 2)
[1308]283        # --------------------------------------------------
[1307]284
[1331]285
[1308]286        # Row 14-15 reserved for displaying the information queried from above
287
[1331]288
[1308]289        # --------------------------------------------------
290        # List parameter files buttons (default and specified)
[1312]291        self.listDefault = Button(self.frame, text = "List Default Parameters", command = lambda: self.listParameters("Default"))
[1308]292        self.listDefault.grid(row = 16, column = 1, padx = 10, pady = 10, columnspan = 2)
[1307]293
[1312]294        self.listSpecified = Button(self.frame, text = "List User Specified Parameters", command = lambda: self.listParameters("Specified"))
[1308]295        self.listSpecified.grid(row = 17, column = 1, padx = 10, pady = 10, columnspan = 2)
[1311]296
[1312]297        self.listEffective = Button(self.frame, text = "List Effective Parameters", command = lambda: self.listParameters("Effective"))
[1311]298        self.listEffective.grid(row = 18, column = 1, padx = 10, pady = 10, columnspan = 2)
[1308]299        # --------------------------------------------------
[1307]300
[1311]301
302        # --------------------------------------------------
303        # Radio Buttons
304
[1316]305        # Obtain (parameter: class) dictionary, and a list of the names of classes in order
[1325]306        (self.classOfParameter, self.classNames, self.description, self.paramType, self.paramFormat) = readParamByClass(defaultParamFilePath)
[1311]307
[1316]308        # Initialise the variable containing the name of the class to be displayed
309        self.whichClass = StringVar()
[1311]310
[1316]311        # Index the buttons
312        i = 0
[1311]313
314
[1316]315        # Create each radio button in order
316        for name in self.classNames:
317           
318            b = Radiobutton(self.frame, text = name, variable = self.whichClass, value = name, indicatoron = 0, command = lambda: self.listParameters("Class"), width = 30)
319
320            # Place them at appropriate positions
[1311]321            r = 19 + int(i/2)
322
323            if (i % 2) == 0:
324                c = 1
325            else:
326                c = 3
[1316]327
328            # Make them stick to the left
[1331]329            b.grid(row = r, column = c, columnspan = 2)
[1311]330            i = i + 1
[1308]331        # --------------------------------------------------
[1307]332
[1331]333
[1308]334        # --------------------------------------------------
335        # Variable definitions throughout the window
336       
[1307]337        # Firstly, determine the default list of parameters
[1316]338        (self.defaultParameters, self.orderedParameters) = readParamFile(defaultParamFilePath)
[1307]339
340        # Then, determine the user specified list of parameters
[1308]341        self.userSpecifiedParameters = {}
[1316]342        self.orderedSpecifiedParameters = []
[1307]343
[1311]344        # This is the effective parameters
[1313]345        self.effectiveParameters = dict(self.defaultParameters)
[1311]346
[1308]347        # Define the string variables in the parameter name and value entry section
[1307]348        self.resultTitle = StringVar()
349        self.result = StringVar()
350        self.notesTitle = StringVar()
351        self.notes = StringVar()
[1312]352        self.color = StringVar()
[1307]353
[1311]354        # Define the string variables for entering file directories, names, and paths   
[1307]355        self.userSpecifiedFileDirectory = StringVar()
356        self.userSpecifiedFileName = StringVar()
357        self.userSpecifiedFilePath = StringVar()
[1308]358        # --------------------------------------------------
[1331]359
360
361        # Give a default name for the user specified file name
362        self.userSpecifiedFileName = 'duchampHIPASS.txt'
363
364        self.frame.bind("<Enter>", self.precaution)
365
366
[1312]367       
[1331]368    # Define a notice box (one button only)
369    def createNoticeBox1(self, title, message, command, buttonText = "Dismiss"):
370       
371        self.noticeBox = Toplevel(width = 200, borderwidth = 20)
372        self.noticeBox.title(title)
373       
374        self.noticeMsg = Message(self.noticeBox, text = message)
375        self.noticeMsg.grid(row = 1, column = 1, columnspan = 2)
376       
377        self.noticeBoxButton = Button(self.noticeBox, text = buttonText, command = command)
378        self.noticeBoxButton.grid(row = 2, column = 1, pady = 5, columnspan = 2)
379       
[1307]380
[1331]381    # Define a notice box (two buttons)
382    def createNoticeBox2(self, title, message, command1, command2, buttonText1 = "Yes", buttonText2 = "No"):
[1312]383
[1331]384        self.noticeBox = Toplevel(width = 200, borderwidth = 20)
385        self.noticeBox.title(title)
[1307]386       
[1331]387        self.noticeMsg = Message(self.noticeBox, text = message)
388        self.noticeMsg.grid(row = 1, column = 1, columnspan = 2)
389       
390        self.noticeBoxButton1 = Button(self.noticeBox, text = buttonText1, command = command1)
391        self.noticeBoxButton1.grid(row = 2, column = 1, pady = 5)
392       
393        self.noticeBoxButton2 = Button(self.noticeBox, text = buttonText2, command = command2)
394        self.noticeBoxButton2.grid(row = 2, column = 2, pady = 5)
395       
[1311]396
[1331]397    # This destroys notice box
398    def dismissNotice(self):
399
400        self.noticeBox.destroy()
[1317]401       
[1331]402       
403    # This checks if file is missing or corrupted or now
404    def precaution(self, event):
405
406        d = [len(self.defaultParameters), len(self.description), len(self.paramType), len(self.paramFormat)]
407        s = ["Default Parameter", "Description", "Type", "Format"]
408       
409        # Firstly, if the default parameter file was not loaded, notify the user
410        if min(d) == 0:
411
412            which = s[d.index(min(d))]
413
414            self.createNoticeBox1(title = "Notice", message = "Input data file missing or corrupted. " + which + " data missing. Close the window and restart.", command = self.frame.quit, buttonText = "Okay")
415            self.frame.bind("<Button-1>", self.quitNow)
416            self.frame.bind("<Button-3>", self.quitNow)
417
418        # Also, check if all of the loaded dictionaries have the same number of elements
419        if max(d) != min(d):
420
421            if d.count(min(d)) == 3:
422                print(1)
423                which = s[d.index(max(d))]
424            elif d.count(max(d)) == 3:
425                print(2)
426                which = s[d.index(min(d))]
427            else:
428                which = "Multiple"
429
430            self.createNoticeBox1(title = "Notice", message = "Input data file corrupted. " + which + " data corrupted. Close the window and restart.", command = self.frame.quit, buttonText = "Okay")
431            self.frame.bind("<Button-1>", self.quitNow)
432            self.frame.bind("<Button-3>", self.quitNow)
[1311]433           
[1331]434
435    # This quits the frame by events
436    def quitNow(self, event):
437
438        self.frame.quit()
[1311]439       
[1331]440
441    # A notice for functions being not implemented yet
442    def notImplementedYet(self):
443
444        self.createNoticeBox1(title = "Be Patient!", message = "Wait! This button is still under developement!", command = self.dismissNotice, buttonText = "Alright!")
445       
446
447    # Some commands want both checking and describing parameters to be done
448    def checkParameterWithDescription(self):
449       
450        self.checkParameter()
451        self.showDescription()
452
453
454    # Allow checking and describing parameters to be driven by events
455    def checkParameterEvent(self, event):
456
457        self.checkParameterWithDescription()
458
459
460    # Allow updating parameters to be driven by events
461    def updateParameterEvent(self, event):
462
463        self.updateParameter()
464
465       
[1308]466    # This checks the value of the parameter entered in the parameter entry box
[1307]467    def checkParameter(self):
468
[1308]469        # Determine which parameter the user wants to check
[1317]470        par = self.parameterEntry.get()
[1307]471
[1308]472        # Find which list it is in and determine its value
473        # Note that specified list takes priority
[1318]474        if par.lower() in lower(self.orderedSpecifiedParameters):
475            index = lower(self.orderedSpecifiedParameters).index(par.lower())
476            parameter2check = self.orderedSpecifiedParameters[index]
[1307]477            value = self.userSpecifiedParameters[parameter2check]
[1317]478            paramInfo = par + ': ' + value
[1312]479            self.color.set("Forest Green")
[1308]480            defaultYN = "Specified Value"
[1318]481        elif par.lower() in lower(self.orderedParameters):
482            index = lower(self.orderedParameters).index(par.lower())
483            parameter2check = self.orderedParameters[index]
[1307]484            value = self.defaultParameters[parameter2check]
485            paramInfo = parameter2check + ': ' + value
[1312]486            self.color.set("Forest Green")
[1308]487            defaultYN = "Default Value"
[1307]488        else:
489            value = "N/A"
[1317]490            paramInfo = '"' + par + '" does not exist'
[1312]491            self.color.set("Red")
[1307]492            defaultYN = "N/A"
493
[1317]494
[1308]495        # Store the results from the above
[1307]496        self.resultTitle.set("Parameter Information")
497        self.result.set(paramInfo)
[1308]498        self.notesTitle.set("Default/Specified")
[1307]499        self.notes.set(defaultYN)
500
[1312]501        self.valueEntry.delete(0, END)
502        self.valueEntry.insert(0, value)
503
504        # --------------------------------------------------
505        # Windows that correspond to the variables above
[1307]506        self.resultLabel = Label(self.frame, textvariable = self.resultTitle)
[1308]507        self.resultLabel.grid(row = 14, column = 1, pady = 0, columnspan = 2)
[1307]508
[1312]509        self.resultValue = Label(self.frame, textvariable = self.result, foreground = self.color.get())
[1308]510        self.resultValue.grid(row = 15, column = 1, pady = 10, columnspan = 2)
[1307]511
512        self.notesLabel = Label(self.frame, textvariable = self.notesTitle)
[1308]513        self.notesLabel.grid(row = 14, column = 3, pady = 0, columnspan = 2)
[1307]514
515        self.notesValue = Label(self.frame, textvariable = self.notes)
[1308]516        self.notesValue.grid(row = 15, column = 3, pady = 10, columnspan = 2)
[1312]517        # --------------------------------------------------
518       
519       
[1308]520    # This updates the value of the parameter entered in the parameter entry box
[1307]521    def updateParameter(self):
522
[1308]523        # Determine which parameter the user wants to update
[1317]524        par = self.parameterEntry.get()
[1307]525
[1308]526        # Update the value and determine if the parameter is being changed or added into the list
[1318]527        if par.lower() in lower(self.orderedSpecifiedParameters):
[1307]528            value2write = self.valueEntry.get()
[1318]529            index = lower(self.orderedSpecifiedParameters).index(par.lower())
530            parameter2update = self.orderedSpecifiedParameters[index]
[1307]531            self.userSpecifiedParameters[parameter2update] = value2write
532            paramInfo = parameter2update + ': ' + value2write
[1308]533            newParamYN = "Updated specified parameter list"
[1312]534            self.color.set("Blue")
[1318]535        elif par.lower() in lower(self.orderedParameters):
536            index = lower(self.orderedParameters).index(par.lower())
537            parameter2update = self.orderedParameters[index]
[1307]538            value2write = self.valueEntry.get()
539            self.userSpecifiedParameters[parameter2update] = value2write
540            paramInfo = parameter2update + ': ' + value2write
[1308]541            newParamYN = "Added in specified parameter list"
[1312]542            self.color.set("Blue")
[1316]543
544            self.orderedSpecifiedParameters = self.orderedSpecifiedParameters + [parameter2update]
[1307]545        else:
[1317]546            paramInfo = '"' + par + '" does not exist'
[1307]547            newParamYN = "-"
[1312]548            self.color.set("Red")
[1307]549
[1308]550        # Store the results from above
[1307]551        self.resultTitle.set("Parameter Information")
552        self.result.set(paramInfo)
553        self.notesTitle.set("Notes")
554        self.notes.set(newParamYN)
555
[1312]556        # --------------------------------------------------
557        # Windows that correspond to the variables above
[1307]558        self.resultLabel = Label(self.frame, textvariable = self.resultTitle)
[1308]559        self.resultLabel.grid(row = 14, column = 1, pady = 0, columnspan = 2)
[1307]560
[1312]561        self.resultValue = Label(self.frame, textvariable = self.result, foreground = self.color.get())
[1308]562        self.resultValue.grid(row = 15, column = 1, pady = 10, columnspan = 2)
[1307]563
564        self.notesLabel = Label(self.frame, textvariable = self.notesTitle)
[1308]565        self.notesLabel.grid(row = 14, column = 3, pady = 0, columnspan = 2)
[1307]566
567        self.notesValue = Label(self.frame, textvariable = self.notes)
[1308]568        self.notesValue.grid(row = 15, column = 3, pady = 10, columnspan = 2)
[1312]569        # --------------------------------------------------
[1307]570
[1311]571        # Update the effective parameters
[1312]572        self.effectiveParameters.update(self.defaultParameters) # In case default wasn't loaded before
[1311]573        self.effectiveParameters.update(self.userSpecifiedParameters)
574
[1331]575        # List specified parameters
[1312]576        self.listParameters("Specified")
577       
578
[1308]579    # This displays a description of the parameter entered in the parameter entry box
[1307]580    def showDescription(self):
581
[1308]582        # Determine which parameter to describe
[1317]583        par = self.parameterEntry.get()
[1307]584
[1308]585        # Determine if the parameter is within the description file
[1318]586        if par.lower() in lower(self.orderedParameters):
587            index = lower(self.orderedParameters).index(par.lower())
588            parameter2describe = self.orderedParameters[index]           
[1307]589            description = self.description[parameter2describe]
590        else:
[1317]591            parameter2describe = par
[1307]592            description = "No description available"
593
[1308]594        # Create the description
[1312]595        self.descriptionList = Listbox(self.frame, width = longestLineLength(self.defaultParameters) + 5, font = ('courier', (10)))
[1311]596        self.descriptionList.grid(row = 30, column = 1, columnspan = 4)
[1307]597
[1317]598        try:
599            self.descriptionList.insert(END, "Description of '" + parameter2describe + "'")
600            self.descriptionList.insert(END, "Class: " + self.classOfParameter[parameter2describe])
[1325]601            self.descriptionList.insert(END, "Type: " + self.paramType[parameter2describe])
602            self.descriptionList.insert(END, "Format: " + self.paramFormat[parameter2describe])
[1317]603            self.descriptionList.insert(END, " ")
[1325]604           
[1317]605        except KeyError:
606            self.descriptionList.delete(0, END)
607            self.descriptionList.insert(END, "Please Enter a parameter")
608            return
[1312]609       
[1307]610
611        descriptionWords = description.split()
612
613        largestWordLength = 0
614
615        for word in descriptionWords:
616            if len(word) > largestWordLength:
617                largestWordLength = len(word)
618        b = 0
619
620        for i in range(len(descriptionWords)):
621                           
622            phrase = ' '.join(descriptionWords[b:i])
623                           
[1316]624            if len(phrase) > longestLineLength(self.defaultParameters) - largestWordLength:
[1307]625                line = phrase
626
627                self.descriptionList.insert(END, line) 
628                b = i
629
[1312]630        self.descriptionList.insert(END, ' '.join(descriptionWords[b:]))
[1331]631
632    # This allows the user to revert a parameter to its default value   
[1317]633    def revertDefault(self):
[1307]634
[1317]635        # Determine which parameter the user wants to revert
636        parameter2revert = self.parameterEntry.get()
637
638        finalValue = self.defaultParameters.get(parameter2revert, "N/A")
639        paramInfo = parameter2revert + ': ' + finalValue
640
641        # Revert the value and determine if the parameter was already default
642        if parameter2revert in self.userSpecifiedParameters:
643
644            del(self.userSpecifiedParameters[parameter2revert])
645            self.orderedSpecifiedParameters.remove(parameter2revert)
646            alreadyDefaultYN = "Reverted back to default"
647            self.color.set("Sky Blue")
648        elif parameter2revert in self.defaultParameters:
649
650            alreadyDefaultYN = "Already default"
651            self.color.set("Sky Blue")
652        else:
653            paramInfo = '"' + parameter2revert + '" does not exist'
654            alreadyDefaultYN = "-"
655            self.color.set("Red")
656
657        # Store the results from above
658        self.resultTitle.set("Parameter Information")
659        self.result.set(paramInfo)
660        self.notesTitle.set("Notes")
661        self.notes.set(alreadyDefaultYN)
662
663        self.valueEntry.delete(0, END)
664        self.valueEntry.insert(0, finalValue)
665
666        # --------------------------------------------------
667        # Windows that correspond to the variables above
668        self.resultLabel = Label(self.frame, textvariable = self.resultTitle)
669        self.resultLabel.grid(row = 14, column = 1, pady = 0, columnspan = 2)
670
671        self.resultValue = Label(self.frame, textvariable = self.result, foreground = self.color.get())
672        self.resultValue.grid(row = 15, column = 1, pady = 10, columnspan = 2)
673
674        self.notesLabel = Label(self.frame, textvariable = self.notesTitle)
675        self.notesLabel.grid(row = 14, column = 3, pady = 0, columnspan = 2)
676
677        self.notesValue = Label(self.frame, textvariable = self.notes)
678        self.notesValue.grid(row = 15, column = 3, pady = 10, columnspan = 2)
679        # --------------------------------------------------
680
681        # Update the effective parameters
682        self.effectiveParameters.update(self.defaultParameters) # In case default wasn't loaded before
683        self.effectiveParameters.update(self.userSpecifiedParameters)
684       
[1331]685        # List the specified parameter values
686        self.listParameters("Specified")
[1317]687
[1331]688       
689    # This displays a list of parameters sets (depending on user command)
[1312]690    def listParameters(self, parameterSet):
[1307]691
[1316]692        paramOrder = self.orderedParameters
693       
[1312]694        if parameterSet == "Default":
695            self.parameters = self.defaultParameters
696        elif parameterSet == "Specified":
697            self.parameters = self.userSpecifiedParameters
[1316]698            paramOrder = self.orderedSpecifiedParameters
[1312]699        elif parameterSet == "Effective":
700            self.parameters = self.effectiveParameters
[1313]701        elif parameterSet == "Class":
[1318]702            self.effectiveParameters.update(self.defaultParameters) # In case default wasn't loaded before
703            self.effectiveParameters.update(self.userSpecifiedParameters)
[1313]704            self.parameters = self.effectiveParameters
[1316]705            className = self.whichClass.get()
[1307]706        else:
[1312]707            print("Coding Error: Used wrong parameter set.")
[1313]708            return
[1307]709
[1309]710        # If the list is empty, it means we haven't loaded it yet
[1312]711        if self.parameters == {}:
[1307]712           
[1331]713            self.createNoticeBox1(title = "Notice", message = "No " + parameterSet + " Parameter File Loaded", command = self.dismissNotice, buttonText = "Okay")
[1307]714
[1309]715        # Otherwise we can print it using the specified format
[1312]716        else:
[1307]717
[1316]718
[1312]719            self.paramListScrollBar = Scrollbar(self.frame)
[1316]720            self.paramListScrollBar.grid(row = 30, column = 5, ipady = 65)
[1312]721           
722            self.parameterList = Listbox(self.frame, yscrollcommand = self.paramListScrollBar.set, width = longestLineLength(self.defaultParameters) + 5, font = ('courier', (10)))
723            self.parameterList.grid(row = 30, column = 1, columnspan = 4)
[1307]724
[1313]725            if parameterSet == "Class":
726                self.parameterList.insert(END, className)
727            else:
728                self.parameterList.insert(END, parameterSet + " set of Parameters")
729               
[1312]730            self.parameterList.insert(END, " ")
[1307]731
[1312]732            star = 0
[1311]733
[1316]734            for par in paramOrder:
[1311]735
[1312]736                if parameterSet == "Effective":
737                    if par in self.userSpecifiedParameters:
738                        star = 1
739                    else:
740                        star = 0
[1313]741                       
742                if parameterSet == "Class":
[1311]743
[1313]744                    if self.classOfParameter[par] == className:
[1311]745
[1313]746                        if par in self.userSpecifiedParameters:
747                            star = 1
748                        else:
749                            star = 0
750
751                        tabbedPar = tabbedDictionaryLine(self.parameters, par, star)
752                        self.parameterList.insert(END, tabbedPar + self.parameters[par])
753                   
754                else:
755                    tabbedPar = tabbedDictionaryLine(self.parameters, par, star)
756                    self.parameterList.insert(END, tabbedPar + self.parameters[par])
757
758                   
759
[1312]760            self.paramListScrollBar.config(command = self.parameterList.yview)
[1311]761
[1312]762            self.list2read = self.parameterList
763            self.parameterList.bind("<ButtonRelease-1>", self.checkItem)
764            self.parameterList.bind("<ButtonRelease-3>", self.checkItemDescript)
765            self.parameterList.bind("<Return>", self.checkItemDescript)
[1311]766
767
[1331]768    # When description is not needed
[1312]769    def checkItem(self, event):
[1311]770
[1312]771        self.doCheckItem(0)
772
[1331]773
774    # When description is needed
[1312]775    def checkItemDescript(self, event):
776
777        self.doCheckItem(1)
778
[1331]779    # When buttons are pressed on the lists, the items will be checked or described.
[1312]780    def doCheckItem(self, describeFlag):
781
782        try:
783            if int(self.list2read.curselection()[0]) != 0:
784
785                line = self.list2read.get(self.list2read.curselection())
786                linebits = line.split()
787                parameter = linebits[0]
788
789                self.parameterEntry.delete(0, END)
790                self.parameterEntry.insert(0, parameter)
791
792                if describeFlag == 0:
793                    self.checkParameter()
794                elif describeFlag == 1:
795                    self.checkParameterWithDescription()
796                else:
797                    print("Coding Error: Describe flag not binary")
798                   
799        except IndexError:
800            pass
[1311]801       
[1317]802
[1331]803    # This handles the input interface (loading a file)
[1310]804    def createInputInterface(self):
[1307]805
[1316]806        self.file_opt = options = {}
807        options['defaultextension'] = '.txt'
808        options['filetypes'] = [('text files', '.txt'), ('all files', '.*')]
809        options['initialdir'] = os.getcwd
810        options['initialfile'] = 'duchampHIPASS.txt'
811        options['parent'] = self.frame
812        options['title'] = 'Load Parameter File'
[1307]813
[1316]814        self.userSpecifiedFilePath = tkFileDialog.askopenfilename(**self.file_opt)
815
[1317]816        self.userSpecifiedFileName = self.userSpecifiedFilePath.split('/')[-1]
[1331]817        self.loadInputFile()
[1314]818       
[1307]819
[1331]820    # This handles the output interface for specified parameters (saving a file)
[1317]821    def createSpecifiedOutputInterface(self):
822       
823        self.file_opt = options = {}
824        options['defaultextension'] = '.txt'
825        options['filetypes'] = [('text files', '.txt'), ('all files', '.*')]
826        options['initialdir'] = os.getcwd
827        options['initialfile'] = self.userSpecifiedFileName.split('.')[0] + 'Edited.' + self.userSpecifiedFileName.split('.')[1]
828        options['parent'] = self.frame
829        options['title'] = 'Save User Specified Parameters'
[1307]830
[1317]831        self.newFilePath = tkFileDialog.asksaveasfilename(**self.file_opt)
832
833        writeParamFile(self.newFilePath, self.userSpecifiedParameters, self.orderedSpecifiedParameters, 1)
[1331]834
835
836    # This handles the output interface for effective parameters (saving a file)
[1317]837    def createEffectiveOutputInterface(self):
838       
[1316]839        self.file_opt = options = {}
840        options['defaultextension'] = '.txt'
841        options['filetypes'] = [('text files', '.txt'), ('all files', '.*')]
842        options['initialdir'] = os.getcwd
[1317]843        options['initialfile'] = self.userSpecifiedFileName.split('.')[0] + 'AllEdited.' + self.userSpecifiedFileName.split('.')[1]
[1316]844        options['parent'] = self.frame
[1317]845        options['title'] = 'Save List of Effective Parameters'
[1314]846
[1316]847        self.newFilePath = tkFileDialog.asksaveasfilename(**self.file_opt)
848
[1317]849        writeParamFile(self.newFilePath, self.effectiveParameters, self.orderedParameters, 1)
[1307]850       
851
[1331]852    # This decides whether to load or update the input files for specified parameters
853    def loadInputFile(self):
[1314]854
[1316]855        try:
856            fin = open(self.userSpecifiedFilePath, 'r')
857            fin.close()
858           
859            # If the list is empty, that means we are loading the file for the first time
860            if self.userSpecifiedParameters == {}:
[1314]861
[1331]862                self.createNoticeBox1(title = "Notice", message = "Input Files Loaded", command = self.load, buttonText = "Okay")
[1314]863
[1316]864            # If the list is non-empty, this means we risk rewritting any edits we have made
865            else:
[1314]866
[1317]867                msg = "Are you sure? Any edits you've made in the previous session will be lost and replaced with the contents of the new files."
[1331]868                self.createNoticeBox2(title = "Notice", message = msg, command1 = self.load, command2 = self.dismissNotice, buttonText1 = "Yes", buttonText2 = "No")
[1316]869        except IOError:
[1317]870            pass
871        except TypeError:
872            pass
873            print("TypeError")
[1331]874           
[1314]875       
[1331]876    # This loads or updates the input files for specified parameters
877    def load(self):
[1314]878
879        # The notice box can go away
880        self.noticeBox.destroy()
881
882        # Then we can determine the list of specified and effective parameters
[1316]883        (self.userSpecifiedParameters, self.orderedSpecifiedParameters) = readParamFile(self.userSpecifiedFilePath)
[1311]884        self.effectiveParameters.update(self.userSpecifiedParameters)
[1314]885       
[1317]886        messages = []
887       
888        # If the file is empty, let the user know.
[1314]889        if self.userSpecifiedParameters == {}:
[1308]890
[1317]891            messages += ["The file you loaded is empty. Parameter file discarded."]
[1314]892           
[1331]893        # Otherwise, determine if some parameters are not part of the default set of parameters.
[1314]894        else:
895            self.listParameters("Specified")
[1307]896
[1317]897            for par in self.orderedSpecifiedParameters:
898
899                if par not in self.defaultParameters:
900
901                    messages += ["Some parameters loaded are not part of the default set of parameters."]
902                    break
903
904        fin = open(self.userSpecifiedFilePath)
905
[1331]906        # Also, determine if the format was not correct.
[1317]907        for line in fin:
908
909            if line[0] != '#' and len(line) > 1 and len(line.split()) > 1:
910
911                if len(line.split()) != 2:
912
913                    messages += ["File loaded has the wrong format. Loaded inputs may be incorrect."]
914                    break
915
916        fin.close()
917
[1331]918        # Let the user know of the above detections
[1317]919        for msg in messages:
920
[1331]921            self.createNoticeBox1(title = "Notice", message = msg, command = self.dismissNotice, buttonText = "Dismiss")
[1317]922               
923
[1314]924       
[1307]925
926
[1317]927   
[1314]928
929
930
931
932
933
934
[1307]935def main():
[1331]936   
[1307]937    root = Tk()
938    root.wm_title("Duchamp Parameters")
939
940    interface = mainInterface(root)
[1308]941    root.config(menu = interface.menubar)
[1307]942    root.mainloop()
943
[1317]944    try:
945        root.destroy()
946    except TclError:
947        pass
948
[1307]949if __name__ == '__main__':
950    main()
Note: See TracBrowser for help on using the repository browser.