""" Settings is a collection of utilities to display, read & write the settings and position widgets. """ from __future__ import absolute_import #Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. import __init__ from fabmetheus_utilities import archive from fabmetheus_utilities import euclidean from fabmetheus_utilities import gcodec import cStringIO import math import os import shutil import sys import traceback import webbrowser try: import Tkinter except: print('You do not have Tkinter, which is needed for the graphical interface, you will only be able to use the command line.') print('Information on how to download Tkinter is at:\nwww.tcl.tk/software/tcltk/') __author__ = 'Enrique Perez (perez_enrique@yahoo.com)' __date__ = "$Date: 2008/23/04 $" __license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' globalRepositoryDialogListTable = {} globalProfileSaveListenerListTable = {} globalCloseListTables = [globalRepositoryDialogListTable, globalProfileSaveListenerListTable] globalSettingReplacements = { 'Perimeter Width over Thickness (ratio):' : 'Edge Width over Height (ratio):', 'Layer Thickness (mm):' : 'Layer Height (mm):', 'Location Arrival X (mm):' : 'Arrival X (mm):', 'Location Arrival Y (mm):' : 'Arrival Y (mm):', 'Location Arrival Z (mm):' : 'Arrival Z (mm):', 'Location Departure X (mm):' : 'Departure X (mm):', 'Location Departure Y (mm):' : 'Departure Y (mm):', 'Location Departure Z (mm):' : 'Departure Z (mm):', 'Location Wipe X (mm):' : 'Wipe X (mm):', 'Location Wipe Y (mm):' : 'Wipe Y (mm):', 'Location Wipe Z (mm):' : 'Wipe Z (mm):' } globalSpreadsheetSeparator = '\t' globalTemporaryOverrides = {} def addAcceleratorCommand( acceleratorBinding, commandFunction, master, menu, text ): "Add accelerator command." acceleratorText = acceleratorBinding[1 : -1] lastIndexOfMinus = acceleratorText.rfind('-') if lastIndexOfMinus > - 1: acceleratorText = acceleratorText[ : lastIndexOfMinus + 1 ] + acceleratorText[ lastIndexOfMinus + 1 : ].capitalize() acceleratorText = acceleratorText.replace('KeyPress-', '') acceleratorText = acceleratorText.replace('-', '+') acceleratorText = acceleratorText.replace('Control', 'Ctrl') acceleratorBinding = acceleratorBinding.replace('KeyPress', '') menu.add_command( accelerator = acceleratorText, label = text, underline = 0, command = commandFunction ) master.bind( acceleratorBinding, commandFunction ) def addEmptyRow( gridPosition ): "Add an empty row." gridPosition.increment() Tkinter.Label( gridPosition.master ).grid( row = gridPosition.row, column = gridPosition.column ) def addListsToRepository(fileNameHelp, repository): 'Add the value to the lists.' addListsToRepositoryByFunction(fileNameHelp, None, repository) def addListsToRepositoryByFunction(fileNameHelp, getProfileDirectory, repository): 'Add the value to the lists.' repository.displayEntities = [] repository.executeTitle = None repository.fileNameHelp = fileNameHelp repository.fileNameInput = None repository.lowerName = fileNameHelp.split('.')[-2] repository.baseName = repository.lowerName + '.csv' repository.baseNameSynonym = None repository.baseNameSynonymDictionary = None repository.capitalizedName = getEachWordCapitalized( repository.lowerName ) repository.getProfileDirectory = getProfileDirectory repository.openLocalHelpPage = HelpPage().getOpenFromDocumentationSubName( repository.fileNameHelp ) repository.openWikiManualHelpPage = None repository.preferences = [] repository.repositoryDialog = None repository.saveListenerTable = {} repository.title = repository.capitalizedName + ' Settings' repository.menuEntities = [] repository.saveCloseTitle = 'Save and Close' repository.windowPosition = WindowPosition().getFromValue( repository, '0+0') for setting in repository.preferences: setting.repository = repository def addMenuEntitiesToMenu( menu, menuEntities ): "Add the menu entities to the menu." for menuEntity in menuEntities: menuEntity.addToMenu( menu ) def addMenuEntitiesToMenuFrameable( menu, menuEntities ): "Add the menu entities to the menu." for menuEntity in menuEntities: menuEntity.addToMenuFrameable( menu ) def addPluginsParentToMenu( directoryPath, menu, parentPath, pluginFileNames ): "Add plugins and the parent to the menu." ToolDialog().addPluginToMenu( menu, parentPath[ : parentPath.rfind('.') ] ) menu.add_separator() addPluginsToMenu( directoryPath, menu, pluginFileNames ) def addPluginsToMenu( directoryPath, menu, pluginFileNames ): "Add plugins to the menu." for pluginFileName in pluginFileNames: ToolDialog().addPluginToMenu( menu, os.path.join( directoryPath, pluginFileName ) ) def cancelRepository(repository): "Read the repository then set all the entities to the read repository values." getReadRepository(repository) for setting in repository.displayEntities: if setting in repository.preferences: setting.setStateToValue() def deleteDirectory( directory, subfolderName ): "Delete the directory if it exists." subDirectory = os.path.join( directory, subfolderName ) if os.path.isdir( subDirectory ): shutil.rmtree( subDirectory ) def deleteMenuItems( menu ): "Delete the menu items." try: lastMenuIndex = menu.index( Tkinter.END ) if lastMenuIndex != None: menu.delete( 0, lastMenuIndex ) except: print('this should never happen, the lastMenuIndex in deleteMenuItems in settings could not be determined.') def getAlongWayHexadecimalColor( beginBrightness, colorWidth, difference, endColorTuple, wayLength ): "Get a color along the way from begin brightness to the end color." alongWay = 1.0 if wayLength != 0.0: alongWay = 0.4 + 0.6 * min( 1.0, abs( float( difference ) / float( wayLength ) ) ) hexadecimalColor = '#' oneMinusAlongWay = 1.0 - alongWay for primaryIndex in xrange(3): hexadecimalColor += getAlongWayHexadecimalPrimary( beginBrightness, oneMinusAlongWay, colorWidth, endColorTuple[ primaryIndex ], alongWay ) return hexadecimalColor def getAlongWayHexadecimalPrimary( beginBrightness, beginRatio, colorWidth, endBrightness, endRatio ): "Get a primary color along the way from grey to the end color." brightness = beginRatio * float( beginBrightness ) + endRatio * float( endBrightness ) return getWidthHex( int( round( brightness ) ), colorWidth ) def getAlterationFile(fileName): "Get the file from the fileName or the lowercase fileName in the alterations directories." settingsAlterationsDirectory = archive.getSettingsPath('alterations') archive.makeDirectory(settingsAlterationsDirectory) fileInSettingsAlterationsDirectory = getFileInGivenDirectory(settingsAlterationsDirectory, fileName) if fileInSettingsAlterationsDirectory != '': return fileInSettingsAlterationsDirectory alterationsDirectory = archive.getSkeinforgePath('alterations') return getFileInGivenDirectory(alterationsDirectory, fileName) def getAlterationFileLine(fileName): "Get the alteration file line from the fileName." lines = getAlterationLines(fileName) if len(lines) == 0: return [] return getAlterationFileLineBlindly(fileName) def getAlterationFileLineBlindly(fileName): "Get the alteration file line from the fileName." return '() %s ()' % fileName def getAlterationFileLines(fileName): 'Get the alteration file line and the text lines from the fileName in the alterations directories.' lines = getAlterationLines(fileName) if len(lines) == 0: return [] return [getAlterationFileLineBlindly(fileName)] + lines def getAlterationLines(fileName): "Get the text lines from the fileName in the alterations directories." return archive.getTextLines(getAlterationFile(fileName)) def getDisplayedDialogFromConstructor(repository): "Display the repository dialog." try: getReadRepository(repository) return RepositoryDialog( repository, Tkinter.Tk() ) except: print('this should never happen, getDisplayedDialogFromConstructor in settings could not open') print(repository) traceback.print_exc(file=sys.stdout) return None def getDisplayedDialogFromPath(path): "Display the repository dialog." pluginModule = archive.getModuleWithPath(path) if pluginModule == None: return None return getDisplayedDialogFromConstructor( pluginModule.getNewRepository() ) def getDisplayToolButtonsRepository( directoryPath, importantFileNames, names, repository ): "Get the display tool buttons." displayToolButtons = [] for name in names: displayToolButton = DisplayToolButton().getFromPath( name in importantFileNames, name, os.path.join( directoryPath, name ), repository ) displayToolButtons.append( displayToolButton ) return displayToolButtons def getEachWordCapitalized( name ): "Get the capitalized name." withSpaces = name.lower().replace('_', ' ') words = withSpaces.split(' ') capitalizedStrings = [] for word in words: capitalizedStrings.append( word.capitalize() ) return ' '.join( capitalizedStrings ) def getFileInGivenDirectory( directory, fileName ): "Get the file from the fileName or the lowercase fileName in the given directory." directoryListing = os.listdir(directory) lowerFileName = fileName.lower() for directoryFile in directoryListing: if directoryFile.lower() == lowerFileName: return getFileTextGivenDirectoryFileName( directory, directoryFile ) return '' def getFileTextGivenDirectoryFileName( directory, fileName ): "Get the entire text of a file with the given file name in the given directory." absoluteFilePath = os.path.join( directory, fileName ) return archive.getFileText( absoluteFilePath ) def getFolders(directory): "Get the folder list in a directory." archive.makeDirectory(directory) directoryListing = [] try: directoryListing = os.listdir(directory) except OSError: print('Skeinforge can not list the directory:') print(directory) print('so give it read/write permission for that directory.') folders = [] for fileName in directoryListing: if os.path.isdir( os.path.join( directory, fileName ) ): folders.append(fileName) return folders def getGlobalRepositoryDialogValues(): "Get the global repository dialog values." global globalRepositoryDialogListTable return euclidean.getListTableElements(globalRepositoryDialogListTable) def getPathInFabmetheusFromFileNameHelp( fileNameHelp ): "Get the directory path from file name help." fabmetheusPath = archive.getFabmetheusPath() splitFileNameHelps = fileNameHelp.split('.') splitFileNameDirectoryNames = splitFileNameHelps[ : - 1 ] for splitFileNameDirectoryName in splitFileNameDirectoryNames: fabmetheusPath = os.path.join( fabmetheusPath, splitFileNameDirectoryName ) return fabmetheusPath def getProfileBaseName(repository): "Get the profile base file name." return getProfileName(repository.baseName, repository) def getProfilesDirectoryInAboveDirectory(subName=''): "Get the profiles directory path in the above directory." aboveProfilesDirectory = archive.getSkeinforgePath('profiles') if subName == '': return aboveProfilesDirectory return os.path.join( aboveProfilesDirectory, subName ) def getProfileName(name, repository): "Get the name, joined with the profile directory if there is one." if repository.getProfileDirectory == None: return name return os.path.join(repository.getProfileDirectory(), name) def getRadioPluginsAddPluginFrame( directoryPath, importantFileNames, names, repository ): "Get the radio plugins and add the plugin frame." repository.pluginFrame = PluginFrame() radioPlugins = [] for name in names: radioPlugin = RadioPlugin().getFromRadio( name in importantFileNames, repository.pluginFrame.latentStringVar, name, repository, name == importantFileNames[0] ) radioPlugin.updateFunction = repository.pluginFrame.update radioPlugins.append( radioPlugin ) defaultRadioButton = getSelectedRadioPlugin( importantFileNames + [ radioPlugins[0].name ], radioPlugins ) repository.pluginFrame.getFromPath( defaultRadioButton, directoryPath, repository ) return radioPlugins def getReadRepository(repository): "Read and return settings from a file." text = archive.getFileText(archive.getProfilesPath(getProfileBaseName(repository)), False) if text == '': if repository.baseNameSynonym != None: text = archive.getFileText(archive.getProfilesPath(getProfileName(repository.baseNameSynonym, repository)), False) if text == '': print('The default %s will be written in the .skeinforge_pypy folder in the home directory.' % repository.title.lower() ) text = archive.getFileText(getProfilesDirectoryInAboveDirectory(getProfileBaseName(repository)), False) if text != '': readSettingsFromText(repository, text) writeSettings(repository) temporaryApplyOverrides(repository) return repository readSettingsFromText(repository, text) temporaryApplyOverrides(repository) return repository def getRepositoryText(repository): "Get the text representation of the repository." repositoryWriter = getRepositoryWriter(repository.title.lower()) for setting in repository.preferences: setting.writeToRepositoryWriter(repositoryWriter) return repositoryWriter.getvalue() def getRepositoryWriter(title): "Get the repository writer for the title." repositoryWriter = cStringIO.StringIO() repositoryWriter.write('Format is tab separated %s.\n' % title) repositoryWriter.write('_Name %sValue\n' % globalSpreadsheetSeparator) return repositoryWriter def getSelectedPluginModuleFromPath(filePath, plugins): "Get the selected plugin module." for plugin in plugins: if plugin.value: return gcodec.getModuleFromPath(plugin.name, filePath) return None def getSelectedPluginName( plugins ): "Get the selected plugin name." for plugin in plugins: if plugin.value: return plugin.name return '' def getSelectedRadioPlugin( names, radioPlugins ): "Get the selected radio button if it exists, None otherwise." for radioPlugin in radioPlugins: if radioPlugin.value: return radioPlugin for name in names: for radioPlugin in radioPlugins: if radioPlugin.name == name: radioPlugin.value = True return radioPlugin print('this should never happen, no getSelectedRadioPlugin in settings') print(names) return radioPlugin[0] def getShortestUniqueSettingName(settingName, settings): "Get the shortest unique name in the settings." for length in xrange(3, len(settingName)): numberOfEquals = 0 shortName = settingName[: length] for setting in settings: if setting.name[: length] == shortName: numberOfEquals += 1 if numberOfEquals < 2: return shortName.lower() return settingName.lower() def getSubfolderWithBasename( basename, directory ): "Get the subfolder in the directory with the basename." archive.makeDirectory(directory) directoryListing = os.listdir(directory) for fileName in directoryListing: joinedFileName = os.path.join( directory, fileName ) if os.path.isdir(joinedFileName): if basename == fileName: return joinedFileName return None def getTitleFromName( title ): "Get the title of this setting." if title[-1] == ':': title = title[ : - 1 ] spaceBracketIndex = title.find(' (') if spaceBracketIndex > - 1: return title[ : spaceBracketIndex ] return title def getUntilFirstBracket(text): 'Get the text until the first bracket, if any.' dotIndex = text.find('(') if dotIndex < 0: return text return text[: dotIndex] def getWidthHex( number, width ): "Get the first width hexadecimal digits." return ('0000%s' % hex(number)[ 2 : ] )[ - width : ] def liftRepositoryDialogs( repositoryDialogs ): "Lift the repository dialogs." for repositoryDialog in repositoryDialogs: repositoryDialog.root.withdraw() # the withdraw & deiconify trick is here because lift does not work properly on my linux computer repositoryDialog.root.lift() # probably not necessary, here in case the withdraw & deiconify trick does not work on some other computer repositoryDialog.root.deiconify() repositoryDialog.root.lift() # probably not necessary, here in case the withdraw & deiconify trick does not work on some other computer repositoryDialog.root.update_idletasks() def openSVGPage( fileName, svgViewer ): "Open svg page with an svg program." if svgViewer == '': return if svgViewer == 'webbrowser': openWebPage(fileName) return filePath = '"' + os.path.normpath(fileName) + '"' # " to send in file name with spaces shellCommand = svgViewer + ' ' + filePath commandResult = os.system(shellCommand) if commandResult != 0: print('It may be that the system could not find the %s program.' % svgViewer ) print('If so, try installing the %s program or look for another svg viewer, like Netscape which can be found at:' % svgViewer ) print('http://www.netscape.org/') print('') def openWebPage( webPagePath ): "Open a web page in a browser." if webPagePath.find('#') != - 1: # to get around # encode bug redirectionText = '\n\n\n' redirectionText += '\n\n' % webPagePath webPagePath = archive.getDocumentationPath('redirect.html') archive.writeFileText( webPagePath, redirectionText ) webPagePath = '"%s"' % webPagePath # " to get around space in url bug try: # " to get around using gnome-open or internet explorer for webbrowser default webbrowserController = webbrowser.get('firefox') except: webbrowserController = webbrowser.get() webbrowserName = webbrowserController.name if webbrowserName == '': try: os.startfile( webPagePath )#this is available on some python environments, but not all return except: pass print('Skeinforge was not able to open the file in a web browser. To see the documentation, open the following file in a web browser:') print(webPagePath) return else: os.system(webbrowserName + ' ' + webPagePath)#used this instead of webbrowser.open() to workaround webbrowser open() bug def printProgress(layerIndex, procedureName): "Print layerIndex followed by a carriage return." printProgressByString('%s layer count %s...' % (procedureName.capitalize(), layerIndex + 1)) def printProgressByNumber(layerIndex, numberOfLayers, procedureName): "Print layerIndex and numberOfLayers followed by a carriage return." printProgressByString('%s layer count %s of %s...' % (procedureName.capitalize(), layerIndex + 1, numberOfLayers)) def printProgressByString(progressString): "Print progress string." sys.stdout.write(progressString) sys.stdout.write(chr(27) + '\r') sys.stdout.flush() def quitWindow(root): "Quit a window." try: root.destroy() except: pass def quitWindows( event=None ): "Quit all windows." global globalRepositoryDialogListTable globalRepositoryDialogValues = euclidean.getListTableElements( globalRepositoryDialogListTable ) for globalRepositoryDialogValue in globalRepositoryDialogValues: quitWindow(globalRepositoryDialogValue.root) def readSettingsFromText(repository, text): "Read settings from a text." text = text.replace(('\nName %sValue\n' % globalSpreadsheetSeparator), ('\n_Name %sValue\n' % globalSpreadsheetSeparator)) lines = archive.getTextLines(text) shortDictionary = {} for setting in repository.preferences: shortDictionary[getShortestUniqueSettingName(setting.name, repository.preferences)] = setting if repository.baseNameSynonymDictionary != None: synonymDictionaryCopy = repository.baseNameSynonymDictionary.copy() for line in lines: splitLine = line.split(globalSpreadsheetSeparator) if len(splitLine) > 1: if splitLine[0] in synonymDictionaryCopy: del synonymDictionaryCopy[splitLine[0]] for synonymDictionaryCopyKey in synonymDictionaryCopy.keys(): text = archive.getFileText(archive.getProfilesPath(getProfileName(synonymDictionaryCopy[synonymDictionaryCopyKey], repository)), False) synonymLines = archive.getTextLines(text) for synonymLine in synonymLines: splitLine = synonymLine.split(globalSpreadsheetSeparator) if len(splitLine) > 1: if splitLine[0] == synonymDictionaryCopyKey: lines.append(synonymLine) for lineIndex in xrange(len(lines)): setRepositoryToLine(lineIndex, lines, shortDictionary) def saveAll(): "Save all the dialogs." for globalRepositoryDialogValue in getGlobalRepositoryDialogValues(): globalRepositoryDialogValue.save() def saveRepository(repository): "Set the entities to the dialog then write them." for setting in repository.preferences: setting.setToDisplay() writeSettingsPrintMessage(repository) for saveListener in repository.saveListenerTable.values(): saveListener() def setButtonFontWeightString( button, isBold ): "Set button font weight given isBold." try: weightString = 'normal' if isBold: weightString = 'bold' splitFont = button['font'].split() button['font'] = ( splitFont[0], splitFont[1], weightString ) except: pass def setEntryText(entry, value): "Set the entry text." if entry == None: return entry.delete(0, Tkinter.END) entry.insert(0, str(value)) def setIntegerValueToString( integerSetting, valueString ): "Set the integer to the string." dotIndex = valueString.find('.') if dotIndex > - 1: valueString = valueString[: dotIndex] try: integerSetting.value = int( valueString ) return except: print('Warning, can not read integer ' + integerSetting.name + ' ' + valueString ) print('Will try reading as a boolean, which might be a mistake.') integerSetting.value = 0 if valueString.lower() == 'true': integerSetting.value = 1 def setRepositoryToLine(lineIndex, lines, shortDictionary): "Set setting dictionary to a setting line.globalSettingReplacements" line = lines[lineIndex] splitLine = line.split(globalSpreadsheetSeparator) if len(splitLine) < 2: return fileSettingName = splitLine[0] if fileSettingName in globalSettingReplacements: fileSettingName = globalSettingReplacements[fileSettingName] shortDictionaryKeys = shortDictionary.keys() shortDictionaryKeys.sort(key=len, reverse=True) # so that a short word like fill is not overidden by a longer word like fillet for shortDictionaryKey in shortDictionaryKeys: if fileSettingName[: len(shortDictionaryKey)].lower() == shortDictionaryKey: shortDictionary[shortDictionaryKey].setValueToSplitLine(lineIndex, lines, splitLine) return def setSpinColor( setting ): "Set the spin box color to the value, yellow if it is lower than the default and blue if it is higher." if setting.entry == None: return if setting.backgroundColor == None: setting.backgroundColor = setting.entry['background'] if setting.backgroundColor[0] != '#': setting.backgroundColor = '#ffffff' setting.colorWidth = len( setting.backgroundColor ) / 3 setting.grey = int( setting.backgroundColor[ 1 : 1 + setting.colorWidth ], 16 ) setting.white = int('f' * setting.colorWidth, 16 ) if abs( setting.value - setting.defaultValue ) <= 0.75 * setting.increment: setting.entry['background'] = setting.backgroundColor return difference = setting.value - setting.defaultValue if difference > 0.0: wayLength = setting.to - setting.defaultValue setting.entry['background'] = getAlongWayHexadecimalColor( setting.grey, setting.colorWidth, difference, ( 0, setting.white, setting.white ), wayLength ) return wayLength = setting.from_ - setting.defaultValue setting.entry['background'] = getAlongWayHexadecimalColor( setting.grey, setting.colorWidth, difference, ( setting.white, setting.white, 0 ), wayLength ) def startMainLoopFromConstructor(repository): "Display the repository dialog and start the main loop." try: import Tkinter except: return displayedDialogFromConstructor = getDisplayedDialogFromConstructor(repository) if displayedDialogFromConstructor == None: print('Warning, displayedDialogFromConstructor in settings is none, so the window will not be displayed.') else: displayedDialogFromConstructor.root.mainloop() def startMainLoopFromWindow(window): 'Display the tableau window and start the main loop.' if window == None: return if window.root == None: print('Warning, window.root in startMainLoopFromWindow in settings is none, so the window will not be displayed.') return window.root.mainloop() def temporaryAddPreferenceOverride(module, name, value): global globalTemporaryOverrides if not module in globalTemporaryOverrides: globalTemporaryOverrides[module] = {} globalTemporaryOverrides[module][name] = value print('OVERRIDE %s %s %s' % (module,name,value)) print(globalTemporaryOverrides[module]) def temporaryApplyOverrides(repository): 'Apply any overrides that have been set at the command line.' # The override dictionary is a mapping of repository names to # key-value mappings. global globalTemporaryOverrides if repository.baseName in globalTemporaryOverrides: settingTable = {} for setting in repository.preferences: settingTable[ setting.name ] = setting for (name, value) in overrides[repository.baseName].items(): if name in settingTable: settingTable[name].setValueToString(value) else: print('Override not applied for: %s, %s' % (name,value)) def writeSettings(repository): "Write the settings to a file." profilesDirectoryPath = archive.getProfilesPath(getProfileBaseName(repository)) archive.makeDirectory(os.path.dirname(profilesDirectoryPath)) archive.writeFileText(profilesDirectoryPath, getRepositoryText(repository)) for setting in repository.preferences: setting.updateSaveListeners() def writeSettingsPrintMessage(repository): "Set the settings to the dialog then write them." writeSettings(repository) print(repository.title.lower().capitalize() + ' have been saved.') def writeValueListToRepositoryWriter( repositoryWriter, setting ): "Write tab separated name and list to the repository writer." repositoryWriter.write( setting.name ) for item in setting.value: if item != '[]': repositoryWriter.write(globalSpreadsheetSeparator) repositoryWriter.write( item ) repositoryWriter.write('\n') class StringSetting: "A class to display, read & write a string." def __init__(self): "Set the update function to none." self.entry = None self.updateFunction = None def __repr__(self): "Get the string representation of this StringSetting." return str(self.__dict__) def addToDialog( self, gridPosition ): "Add this to the dialog." gridPosition.increment() self.label = Tkinter.Label( gridPosition.master, text = self.name ) self.label.grid( row = gridPosition.row, column = 0, columnspan = 3, sticky = Tkinter.W ) self.createEntry( gridPosition.master ) self.setStateToValue() self.entry.grid( row = gridPosition.row, column = 3, columnspan = 2, sticky = Tkinter.W ) self.bindEntry() LabelHelp( self.repository.fileNameHelp, gridPosition.master, self.name, self.label ) def addToMenu( self, repositoryMenu ): "Do nothing because this should only be added to a frameable repository menu." pass def addToMenuFrameable( self, repositoryMenu ): "Add this to the frameable repository menu." titleFromName = getTitleFromName( self.name ) helpWindowMenu = Tkinter.Menu( repositoryMenu, tearoff = 0 ) repositoryMenu.add_cascade( label = titleFromName, menu = helpWindowMenu, underline = 0 ) if self.name in self.repository.frameList.value: helpWindowMenu.add_command( label = 'Remove from Window', command = self.removeFromWindow ) else: helpWindowMenu.add_command( label = 'Add to Window', command = self.addToWindow ) helpWindowMenu.add_separator() helpWindowMenu.add_command( label = 'Help', command = HelpPage().getOpenFromDocumentationSubName( self.repository.fileNameHelp + '#' + titleFromName ) ) def addToWindow(self): "Add this to the repository frame list." self.repository.frameList.addToList( self.name ) def bindEntry(self): "Bind the entry to the update function." if self.updateFunction != None: self.entry.bind('', self.updateFunction ) def createEntry( self, root ): "Create the entry." self.entry = Tkinter.Entry( root ) def getFromValue( self, name, repository, value ): "Initialize." return self.getFromValueOnlyAddToRepository( name, repository, value ) def getFromValueOnly( self, name, repository, value ): "Initialize." self.defaultValue = value self.name = name self.repository = repository self.value = value return self def getFromValueOnlyAddToRepository( self, name, repository, value ): "Initialize." repository.displayEntities.append(self) repository.menuEntities.append(self) repository.preferences.append(self) return self.getFromValueOnly( name, repository, value ) def removeFromWindow(self): "Remove this from the repository frame list." self.repository.frameList.removeFromList( self.name ) def setStateToValue(self): "Set the entry to the value." setEntryText(self.entry, self.value) def setToDisplay(self): "Set the string to the entry field." try: valueString = self.entry.get() self.setValueToString( valueString ) except: pass def setUpdateFunction( self, updateFunction ): "Set the update function." self.updateFunction = updateFunction def setValueToSplitLine( self, lineIndex, lines, splitLine ): "Set the value to the second word of a split line." self.setValueToString(splitLine[1]) def setValueToString( self, valueString ): "Set the value to the value string." self.value = valueString def updateSaveListeners(self): "Update save listeners if any." pass def writeToRepositoryWriter( self, repositoryWriter ): "Write tab separated name and value to the repository writer." repositoryWriter.write('%s%s%s\n' % ( self.name, globalSpreadsheetSeparator, self.value ) ) class BooleanSetting( StringSetting ): "A class to display, read & write a boolean." def addToDialog( self, gridPosition ): "Add this to the dialog." gridPosition.increment() self.checkbutton = Tkinter.Checkbutton( gridPosition.master, command = self.toggleCheckbutton, text = self.name ) #toggleCheckbutton is being used instead of a Tkinter IntVar because there is a weird bug where it doesn't work properly if this setting is not on the first window. self.checkbutton.grid( row = gridPosition.row, columnspan = 5, sticky = Tkinter.W ) self.setStateToValue() LabelHelp( self.repository.fileNameHelp, gridPosition.master, self.name, self.checkbutton ) def addToMenu( self, repositoryMenu ): "Add this to the repository menu." self.activateToggleMenuCheckbutton = False #activateToggleMenuCheckbutton is being used instead of setting command after because add_checkbutton does not return a checkbutton. repositoryMenu.add_checkbutton( label = getTitleFromName( self.name ), command = self.toggleMenuCheckbutton ) if self.value: repositoryMenu.invoke( repositoryMenu.index( Tkinter.END ) ) self.activateToggleMenuCheckbutton = True def addToMenuFrameable( self, repositoryMenu ): "Add this to the frameable repository menu." titleFromName = getTitleFromName( self.name ) helpWindowMenu = Tkinter.Menu( repositoryMenu, tearoff = 0 ) repositoryMenu.add_cascade( label = titleFromName, menu = helpWindowMenu, underline = 0 ) self.addToMenu( helpWindowMenu ) helpWindowMenu.add_separator() helpWindowMenu.add_command( label = 'Help', command = HelpPage().getOpenFromDocumentationSubName( self.repository.fileNameHelp + '#' + titleFromName ) ) def setStateToValue(self): "Set the checkbutton to the boolean." try: if self.value: self.checkbutton.select() else: self.checkbutton.deselect() except: pass def setToDisplay(self): "Do nothing because toggleCheckbutton is handling the value." pass def setValueToString( self, valueString ): "Set the boolean to the string." self.value = ( valueString.lower() == 'true') def toggleCheckbutton(self): "Workaround for Tkinter bug, toggle the value." self.value = not self.value self.setStateToValue() if self.updateFunction != None: self.updateFunction() def toggleMenuCheckbutton(self): "Workaround for Tkinter bug, toggle the value." if self.activateToggleMenuCheckbutton: self.value = not self.value if self.updateFunction != None: self.updateFunction() class CloseListener: "A class to listen to link a window to the global repository dialog list table." def __init__( self, window, closeFunction = None ): "Add the window to the global repository dialog list table." self.closeFunction = closeFunction self.window = window self.shouldWasClosedBeBound = True global globalRepositoryDialogListTable euclidean.addElementToListDictionaryIfNotThere( window, window, globalRepositoryDialogListTable ) def listenToWidget( self, widget ): "Listen to the destroy message of the widget." if self.shouldWasClosedBeBound: self.shouldWasClosedBeBound = False widget.bind('', self.wasClosed ) def wasClosed(self, event): "The dialog was closed." global globalCloseListTables for globalCloseListTable in globalCloseListTables: if self.window in globalCloseListTable: del globalCloseListTable[ self.window ] if self.closeFunction != None: self.closeFunction() class DisplayToolButton: "A class to display the tool dialog button, in a two column wide table." def addToDialog( self, gridPosition ): "Add this to the dialog." self.displayButton = Tkinter.Button( gridPosition.master, activebackground = 'black', activeforeground = 'white', text = getEachWordCapitalized( self.name ), command = self.displayDialog ) setButtonFontWeightString( self.displayButton, self.important ) gridPosition.incrementGivenNumberOfColumns(2) self.displayButton.grid( row = gridPosition.row, column = gridPosition.column, columnspan = 2 ) def displayDialog(self): "Display function." ToolDialog().getFromPath( self.path ).display() def getFromPath( self, important, name, path, repository ): "Initialize." self.important = important self.name = name self.path = path self.repository = repository repository.displayEntities.append(self) return self class FileHelpMenuBar: def __init__( self, root ): "Create a menu bar with a file and help menu." self.underlineLetters = [] self.menuBar = Tkinter.Menu( root ) self.root = root root.config( menu = self.menuBar ) self.fileMenu = Tkinter.Menu( self.menuBar, tearoff = 0 ) self.menuBar.add_cascade( label = "File", menu = self.fileMenu, underline = 0 ) self.underlineLetters.append('f') def addMenuToMenuBar( self, labelText, menu ): "Add a menu to the menu bar." lowerLabelText = labelText.lower() for underlineLetterIndex in xrange( len( lowerLabelText ) ): underlineLetter = lowerLabelText[ underlineLetterIndex ] if underlineLetter not in self.underlineLetters: self.underlineLetters.append( underlineLetter ) self.menuBar.add_cascade( label = labelText, menu = menu, underline = underlineLetterIndex ) return self.menuBar.add_cascade( label = labelText, menu = menu ) def addPluginToMenuBar( self, modulePath, repository, window ): "Add a menu to the menu bar from a tool." pluginModule = archive.getModuleWithPath( modulePath ) if pluginModule == None: print('this should never happen, pluginModule in addMenuToMenuBar in settings is None.') return None repositoryMenu = Tkinter.Menu( self.menuBar, tearoff = 0 ) labelText = getEachWordCapitalized( os.path.basename( modulePath ) ) self.addMenuToMenuBar( labelText, repositoryMenu ) pluginModule.addToMenu( self.root, repositoryMenu, repository, window ) def completeMenu(self, closeFunction, repository, saveFunction, window): "Complete the menu." self.closeFunction = closeFunction self.saveFunction = saveFunction addAcceleratorCommand('', saveFunction, self.root, self.fileMenu, 'Save') self.fileMenu.add_command(label = "Save and Close", command = self.saveClose) addAcceleratorCommand('', closeFunction, self.root, self.fileMenu, 'Close') self.fileMenu.add_separator() addAcceleratorCommand('', quitWindows, self.root, self.fileMenu, 'Quit') skeinforgePluginsPath = archive.getSkeinforgePath('skeinforge_plugins') pluginFileNames = archive.getPluginFileNamesFromDirectoryPath(skeinforgePluginsPath) #for pluginFileName in pluginFileNames: # self.addPluginToMenuBar(os.path.join(skeinforgePluginsPath, pluginFileName), repository, window) def saveClose(self): "Call the save function then the close function." self.saveFunction() self.closeFunction() class FileNameInput( StringSetting ): "A class to display, read & write a fileName." def addToDialog( self, gridPosition ): "Add this to the dialog." self.gridPosition = gridPosition gridPosition.executables.append(self) def execute(self): "Open the file picker." self.wasCancelled = False parent = self.gridPosition.master try: import tkFileDialog summarized = archive.getSummarizedFileName(self.value) initialDirectory = os.path.dirname( summarized ) if len( initialDirectory ) > 0: initialDirectory += os.sep else: initialDirectory = "." fileName = tkFileDialog.askopenfilename( filetypes = self.getFileNameFirstTypes(), initialdir = initialDirectory, initialfile = os.path.basename( summarized ), parent = parent, title = self.name ) self.setCancelledValue(fileName) return except: print('Could not get the old directory in settings, so the file picker will be opened in the default directory.') try: fileName = tkFileDialog.askopenfilename( filetypes = self.getFileNameFirstTypes(), initialdir = '.', initialfile = '', parent = parent, title = self.name ) self.setCancelledValue(fileName) except: print('Error in execute in FileName in settings, ' + self.name ) def getFileNameFirstTypes(self): "Get the file types with the file type of the fileName moved to the front of the list." allFiles = [ ('All', '*.*') ] try: basename = os.path.basename(self.value) splitFile = basename.split('.') allReadables = [] if len( self.fileTypes ) > 1: for fileType in self.fileTypes: allReadable = ( ('All Readable', fileType[1] ) ) allReadables.append( allReadable ) if len( splitFile ) < 1: return allReadables + allFiles + self.fileTypes baseExtension = splitFile[-1] for fileType in self.fileTypes: fileExtension = fileType[1].split('.')[-1] if fileExtension == baseExtension: fileNameFirstTypes = self.fileTypes[:] fileNameFirstTypes.remove( fileType ) return [ fileType ] + allReadables + allFiles + fileNameFirstTypes return allReadables + allFiles + self.fileTypes except: return allFiles def getFromFileName( self, fileTypes, name, repository, value ): "Initialize." self.getFromValueOnly( name, repository, value ) self.fileTypes = fileTypes self.wasCancelled = False repository.displayEntities.append(self) repository.preferences.append(self) return self def setCancelledValue( self, fileName ): "Set the value to the file name and wasCancelled true if a file was not picked." if ( str(fileName) == '()' or str(fileName) == ''): self.wasCancelled = True else: self.value = fileName def setToDisplay(self): "Do nothing because the file dialog is handling the value." pass class FloatSetting( StringSetting ): "A class to display, read & write a float." def setValueToString( self, valueString ): "Set the float to the string." try: self.value = float( valueString ) except: print('Oops, can not read float' + self.name + ' ' + valueString ) class FloatSpin( FloatSetting ): "A class to display, read & write an float in a spin box." def addToMenuFrameable( self, repositoryMenu ): "Add this to the frameable repository menu." titleFromName = getTitleFromName( self.name ) helpWindowMenu = Tkinter.Menu( repositoryMenu, tearoff = 0 ) repositoryMenu.add_cascade( label = titleFromName, menu = helpWindowMenu, underline = 0 ) if self.name in self.repository.frameList.value: helpWindowMenu.add_command( label = 'Remove from Window', command = self.removeFromWindow ) else: helpWindowMenu.add_command( label = 'Add to Window', command = self.addToWindow ) helpWindowMenu.add_separator() changeString = ' by %s' % self.increment helpWindowMenu.add_command( label = 'Increase' + changeString, command = self.increase ) helpWindowMenu.add_command( label = 'Decrease' + changeString, command = self.decrease ) helpWindowMenu.add_separator() helpWindowMenu.add_command( label = 'Help', command = HelpPage().getOpenFromDocumentationSubName( self.repository.fileNameHelp + '#' + titleFromName ) ) def bindEntry(self): "Bind the entry to the update function." self.entry.bind('', self.entryUpdated ) self.setColor() def createEntry( self, root ): "Create the entry." self.entry = Tkinter.Spinbox( root, command = self.setColorToDisplay, from_ = self.from_, increment = self.increment, to = self.to ) def decrease(self): "Decrease the value then set the state and color to the value." self.value -= self.increment self.setStateUpdateColor() def entryUpdated(self, event=None): "Create the entry." self.setColorToDisplay() if self.updateFunction != None: self.updateFunction(event) def getFromValue(self, from_, name, repository, to, value): "Initialize." self.backgroundColor = None self.from_ = from_ self.minimumWidth = min(value - from_, to - value) rank = euclidean.getRank(0.05 * (to - from_)) self.increment = euclidean.getIncrementFromRank(rank) self.to = to return self.getFromValueOnlyAddToRepository(name, repository, value) def increase(self): "Increase the value then set the state and color to the value." self.value += self.increment self.setStateUpdateColor() def setColor(self, event=None): "Set the color to the value, yellow if it is lower than the default and blue if it is higher." setSpinColor(self) def setColorToDisplay(self, event=None): "Set the color to the value, yellow if it is lower than the default and blue if it is higher." self.setToDisplay() self.setColor() def setStateToValue(self): "Set the entry to the value." setEntryText( self.entry, self.value ) self.setColor() def setStateUpdateColor(self): "Set the state to the value, call the update function, then set the color." self.setStateToValue() if self.updateFunction != None: self.updateFunction() class FloatSpinNotOnMenu( FloatSpin ): "A class to display, read & write an float in a spin box, which is not to be added to a menu." def getFromValueOnlyAddToRepository( self, name, repository, value ): "Initialize." repository.displayEntities.append(self) repository.preferences.append(self) return self.getFromValueOnly( name, repository, value ) class FloatSpinUpdate( FloatSpin ): "A class to display, read, update & write an float in a spin box." def createEntry( self, root ): "Create the entry." self.entry = Tkinter.Spinbox( root, command = self.entryUpdated, from_ = self.from_, increment = self.increment, to = self.to ) class FrameList: "A class to list the frames." def addToList(self, word): "Add the word to the sorted list." self.value.append(word) self.value.sort() self.repository.window.redisplayWindowUpdate() def getFromValue( self, name, repository, value ): "Initialize." repository.preferences.append(self) self.name = name self.repository = repository self.value = value return self def removeFromList(self, word): "Remove the word from the sorted list." self.value.remove(word) self.value.sort() self.repository.window.redisplayWindowUpdate() def setToDisplay(self): "Do nothing because frame list does not have a display." pass def setValueToSplitLine( self, lineIndex, lines, splitLine ): "Set the value to the second and later words of a split line." self.value = splitLine[1 :] def updateSaveListeners(self): "Update save listeners if any." pass def writeToRepositoryWriter( self, repositoryWriter ): "Write tab separated name and list to the repository writer." writeValueListToRepositoryWriter( repositoryWriter, self ) class GridHorizontal: "A class to place elements horizontally on a grid." def __init__( self, column, row ): "Initialize the column and row." self.column = column self.columnStart = column self.row = row def getCopy(self): "Get a copy." copy = GridHorizontal( self.column, self.row ) copy.columnStart = self.columnStart return copy def increment(self): "Increment the position horizontally." self.column += 1 class GridVertical: "A class to place elements vertically on a grid." def __init__( self, column, row ): "Initialize the column and row." self.column = column self.columnOffset = column self.columnStart = column self.row = row self.rowStart = row def execute(self): "The execute button was clicked." for executable in self.executables: executable.execute() saveAll() self.repository.execute() def getCopy(self): "Get a copy." copy = GridVertical( self.column, self.row ) copy.columnOffset = self.columnOffset copy.columnStart = self.columnStart copy.rowStart = self.rowStart return copy def increment(self): "Increment the position vertically." self.column = self.columnStart self.columnOffset = self.columnStart self.row += 1 def incrementGivenNumberOfColumns( self, numberOfColumns ): "Increment the position vertically and offset it horizontally by the given number of columns." self.column = self.columnOffset if self.columnOffset == self.columnStart: self.columnOffset = self.columnStart + 1 self.row += 1 return if self.columnOffset < self.columnStart + numberOfColumns - 1: self.columnOffset += 1 return self.columnOffset = self.columnStart def setExecutablesRepository( self, repository ): "Set the executables to an empty list and set the repository." self.executables = [] self.repository = repository class HelpPage: "A class to open a help page." def __init__(self): "Initialize column." self.column = 3 def addToDialog( self, gridPosition ): "Add this to the dialog." capitalizedName = getEachWordCapitalized( self.name ) self.displayButton = Tkinter.Button( gridPosition.master, activebackground = 'black', activeforeground = 'white', command = self.openPage, text = capitalizedName ) if len( capitalizedName ) < 12: self.displayButton['width'] = 10 self.displayButton.grid( row = gridPosition.row, column = self.column, columnspan = 2 ) def addToMenu( self, repositoryMenu ): "Add this to the repository menu." repositoryMenu.add_command( label = getTitleFromName( self.name ), command = self.openPage ) def addToMenuFrameable( self, repositoryMenu ): "Add this to the frameable repository menu." self.addToMenu( repositoryMenu ) def getFromNameAfterHTTP( self, afterHTTP, name, repository ): "Initialize." self.setToNameRepository( name, repository ) self.hypertextAddress = 'http://' + afterHTTP return self def getFromNameAfterWWW( self, afterWWW, name, repository ): "Initialize." self.setToNameRepository( name, repository ) self.hypertextAddress = 'http://www.' + afterWWW return self def getFromNameSubName( self, name, repository, subName=''): "Initialize." self.setToNameRepository( name, repository ) self.hypertextAddress = archive.getDocumentationPath( subName ) return self def getOpenFromAbsolute( self, hypertextAddress ): "Get the open help page function from the hypertext address." self.hypertextAddress = hypertextAddress return self.openPage def getOpenFromAfterHTTP( self, afterHTTP ): "Get the open help page function from the part of the address after the HTTP." self.hypertextAddress = 'http://' + afterHTTP return self.openPage def getOpenFromAfterWWW( self, afterWWW ): "Get the open help page function from the afterWWW of the address after the www." self.hypertextAddress = 'http://www.' + afterWWW return self.openPage def getOpenFromDocumentationSubName( self, subName=''): "Get the open help page function from the afterWWW of the address after the www." self.hypertextAddress = archive.getDocumentationPath( subName ) return self.openPage def openPage(self, event=None): "Open the browser to the hypertext address." openWebPage( self.hypertextAddress ) def setToNameRepository( self, name, repository ): "Set to the name and repository." self.name = name self.repository = repository repository.displayEntities.append(self) repository.menuEntities.append(self) class HelpPageRepository: "A class to open a repository help page." def __init__( self, repository ): "Add this to the dialog." self.repository = repository def openPage(self, event=None): "Open the browser to the repository help page." if self.repository.openWikiManualHelpPage == None: self.repository.openLocalHelpPage() return from skeinforge_application.skeinforge_utilities import skeinforge_help helpRepository = getReadRepository( skeinforge_help.HelpRepository() ) if helpRepository.wikiManualPrimary.value: self.repository.openWikiManualHelpPage() return self.repository.openLocalHelpPage() class IntSetting( FloatSetting ): "A class to display, read & write an int." def setValueToString( self, valueString ): "Set the integer to the string." setIntegerValueToString( self, valueString ) class IntSpin(FloatSpin): "A class to display, read & write an int in a spin box." def getFromValue(self, from_, name, repository, to, value): "Initialize." self.backgroundColor = None self.from_ = from_ rank = euclidean.getRank(0.05 * (to - from_)) self.increment = max(1, int(euclidean.getIncrementFromRank(rank))) self.minimumWidth = min(value - from_, to - value) self.to = to return self.getFromValueOnlyAddToRepository(name, repository, value) def getSingleIncrementFromValue( self, from_, name, repository, to, value ): "Initialize." self.backgroundColor = None self.from_ = from_ self.increment = 1 self.minimumWidth = min(value - from_, to - value) self.to = to return self.getFromValueOnlyAddToRepository( name, repository, value ) def setValueToString( self, valueString ): "Set the integer to the string." setIntegerValueToString( self, valueString ) class IntSpinNotOnMenu( IntSpin ): "A class to display, read & write an integer in a spin box, which is not to be added to a menu." def getFromValueOnlyAddToRepository( self, name, repository, value ): "Initialize." repository.displayEntities.append(self) repository.preferences.append(self) return self.getFromValueOnly( name, repository, value ) class IntSpinUpdate( IntSpin ): "A class to display, read, update & write an int in a spin box." def createEntry( self, root ): "Create the entry." self.entry = Tkinter.Spinbox( root, command = self.entryUpdated, from_ = self.from_, increment = self.increment, to = self.to ) class LabelDisplay: "A class to add a label." def addToDialog( self, gridPosition ): "Add this to the dialog." gridPosition.increment() self.label = Tkinter.Label( gridPosition.master, text = self.name ) self.label.grid( row = gridPosition.row, column = 0, columnspan = self.columnspan, sticky = Tkinter.W ) LabelHelp( self.repository.fileNameHelp, gridPosition.master, self.name, self.label ) def getFromName( self, name, repository ): "Initialize." self.columnspan = 3 self.name = name self.repository = repository repository.displayEntities.append(self) return self class LabelHelp: "A class to add help to a widget." def __init__( self, fileNameHelp, master, name, widget ): "Add menu to the widget." if len( name ) < 1: return self.popupMenu = Tkinter.Menu( master, tearoff = 0 ) titleFromName = getTitleFromName( name.replace('- ', '').replace(' -', '') ) self.popupMenu.add_command( label = 'Help', command = HelpPage().getOpenFromDocumentationSubName( fileNameHelp + '#' + titleFromName ) ) widget.bind('', self.unpostPopupMenu ) widget.bind('', self.unpostPopupMenu ) widget.bind('', self.displayPopupMenu ) def displayPopupMenu(self, event=None): 'Display the popup menu when the button is right clicked.' try: self.popupMenu.tk_popup( event.x_root + 30, event.y_root, 0 ) finally: self.popupMenu.grab_release() def unpostPopupMenu(self, event=None): 'Unpost the popup menu.' self.popupMenu.unpost() class LabelSeparator: "A class to add a label and menu separator." def addToDialog( self, gridPosition ): "Add this to the dialog." gridPosition.increment() self.label = Tkinter.Label( gridPosition.master, text='') self.label.grid( row = gridPosition.row, column = 0, columnspan = 3, sticky = Tkinter.W ) def addToMenu( self, repositoryMenu ): "Add this to the repository menu." repositoryMenu.add_separator() def addToMenuFrameable( self, repositoryMenu ): "Add this to the frameable repository menu." self.addToMenu( repositoryMenu ) def getFromRepository( self, repository ): "Initialize." self.name = '' self.repository = repository repository.displayEntities.append(self) repository.menuEntities.append(self) return self class LatentStringVar: "A class to provide a StringVar when needed." def __init__(self): "Set the string var." self.stringVar = None def getString(self): "Get the string." return self.getVar().get() def getVar(self): "Get the string var." if self.stringVar == None: self.stringVar = Tkinter.StringVar() return self.stringVar def setString(self, word): "Set the string." self.getVar().set(word) class LayerCount: 'A class to handle the layerIndex.' def __init__(self): 'Initialize.' self.layerIndex = -1 def __repr__(self): 'Get the string representation of this LayerCount.' return str(self.layerIndex) def printProgressIncrement(self, procedureName): 'Print progress then increment layerIndex.' self.layerIndex += 1 printProgress(self.layerIndex, procedureName) class MenuButtonDisplay: "A class to add a menu button." def addRadiosToDialog( self, gridPosition ): "Add the menu radios to the dialog." for menuRadio in self.menuRadios: menuRadio.addToDialog( gridPosition ) def addToMenu( self, repositoryMenu ): "Add this to the repository menu." if len( self.menuRadios ) < 1: print('The MenuButtonDisplay in settings should have menu items.') print(self.name) return self.menu = Tkinter.Menu( repositoryMenu, tearoff = 0 ) repositoryMenu.add_cascade( label = getTitleFromName( self.name ), menu = self.menu ) self.setRadioVarToName( self.menuRadios[0].name ) def addToMenuFrameable( self, repositoryMenu ): "Add this to the frameable repository menu." titleFromName = getTitleFromName( self.name ) self.addToMenu( repositoryMenu ) self.menu.add_command( label = 'Help', command = HelpPage().getOpenFromDocumentationSubName( self.repository.fileNameHelp + '#' + titleFromName ) ) self.menu.add_separator() def getFromName( self, name, repository ): "Initialize." self.columnspan = 2 self.menuRadios = [] self.name = name self.radioVar = None self.repository = repository repository.menuEntities.append(self) return self def removeMenus(self): "Remove all menus." deleteMenuItems( self.menu ) self.menuRadios = [] def setRadioVarToName(self, name): "Get the menu button." self.optionList = [name] self.radioVar = Tkinter.StringVar() self.radioVar.set( self.optionList[0] ) def setToNameAddToDialog( self, name, gridPosition ): "Get the menu button." if self.radioVar != None: return gridPosition.increment() self.setRadioVarToName( name ) self.label = Tkinter.Label( gridPosition.master, text = self.name ) self.label.grid( row = gridPosition.row, column = 0, columnspan = 3, sticky = Tkinter.W ) self.menuButton = Tkinter.OptionMenu( gridPosition.master, self.radioVar, self.optionList ) self.menuButton.grid( row = gridPosition.row, column = 3, columnspan = self.columnspan, sticky = Tkinter.W ) self.menuButton.menu = Tkinter.Menu( self.menuButton, tearoff = 0 ) self.menu = self.menuButton.menu self.menuButton['menu'] = self.menu LabelHelp( self.repository.fileNameHelp, gridPosition.master, self.name, self.label ) class MenuRadio( BooleanSetting ): "A class to display, read & write a boolean with associated menu radio button." def addToDialog( self, gridPosition ): "Add this to the dialog." self.menuButtonDisplay.setToNameAddToDialog( self.name, gridPosition ) self.addToSubmenu() def addToMenu( self, repositoryMenu ): "Add this to the submenu set by MenuButtonDisplay, the repository menu is ignored" self.addToSubmenu() def addToMenuFrameable( self, repositoryMenu ): "Add this to the frameable repository menu." self.addToMenu( repositoryMenu ) def addToSubmenu(self): "Add this to the submenu." self.activate = False menu = self.menuButtonDisplay.menu menu.add_radiobutton( label = self.name, command = self.clickRadio, value = self.name, variable = self.menuButtonDisplay.radioVar ) self.menuLength = menu.index( Tkinter.END ) if self.value: self.menuButtonDisplay.radioVar.set( self.name ) self.invoke() self.activate = True def clickRadio(self): "Workaround for Tkinter bug, invoke and set the value when clicked." if not self.activate: return self.menuButtonDisplay.radioVar.set( self.name ) if self.updateFunction != None: self.updateFunction() def getFromMenuButtonDisplay( self, menuButtonDisplay, name, repository, value ): "Initialize." self.getFromValueOnlyAddToRepository( name, repository, value ) self.menuButtonDisplay = menuButtonDisplay self.menuButtonDisplay.menuRadios.append(self) return self def invoke(self): "Workaround for Tkinter bug, invoke to set the value when changed." self.menuButtonDisplay.menu.invoke( self.menuLength ) def setStateToValue(self): "Set the checkbutton to the boolean." try: if self.value: self.invoke() except: pass def setToDisplay(self): "Set the boolean to the checkbutton." if self.menuButtonDisplay.radioVar != None: self.value = ( self.menuButtonDisplay.radioVar.get() == self.name ) class PluginFrame: "A class to display the plugins in a frame." def __init__(self): "Initialize." self.gridTable = {} self.latentStringVar = LatentStringVar() self.oldLatentString = '' def addToDialog( self, gridPosition ): "Add this to the dialog." gridPosition.increment() self.gridPosition = gridPosition.getCopy() self.gridPosition.master = gridPosition.master self.createFrame( gridPosition ) def createFrame( self, gridPosition ): "Create the frame." gridVertical = GridVertical( 0, 0 ) gridVertical.master = Tkinter.LabelFrame( gridPosition.master, borderwidth = 3, relief = 'raised') gridVertical.master.grid( row = gridPosition.row, column = gridPosition.column, columnspan = 12, sticky = Tkinter.E + Tkinter.W + Tkinter.N + Tkinter.S ) gridPosition.master.grid_rowconfigure( gridPosition.row, weight = 1 ) gridPosition.master.grid_columnconfigure( gridPosition.column + 11, weight = 1 ) if self.latentStringVar.getString() == '': self.defaultRadioButton.setSelect() self.gridTable[ self.latentStringVar.getString() ] = gridVertical path = os.path.join( self.directoryPath, self.latentStringVar.getString() ) pluginModule = archive.getModuleWithPath(path) if pluginModule == None: print('this should never happen, pluginModule in addToDialog in PluginFrame in settings is None') print(path) return gridVertical.repository = getReadRepository( pluginModule.getNewRepository() ) gridVertical.frameGridVertical = GridVertical( 0, 0 ) gridVertical.frameGridVertical.setExecutablesRepository( gridVertical.repository ) executeTitle = gridVertical.repository.executeTitle #if executeTitle != None: # executeButton = Tkinter.Button( gridVertical.master, activebackground = 'black', activeforeground = 'blue', text = executeTitle, command = gridVertical.frameGridVertical.execute ) # executeButton.grid( row = gridVertical.row, column = gridVertical.column, sticky = Tkinter.W ) # gridVertical.column += 1 self.helpButton = Tkinter.Button( gridVertical.master, activebackground = 'black', activeforeground = 'white', text = "?", command = HelpPageRepository( gridVertical.repository ).openPage ) self.helpButton.grid( row = gridVertical.row, column = gridVertical.column, sticky = Tkinter.W ) addEmptyRow( gridVertical ) gridVertical.increment() from fabmetheus_utilities.hidden_scrollbar import HiddenScrollbar gridVertical.xScrollbar = HiddenScrollbar( gridVertical.master, orient = Tkinter.HORIZONTAL ) gridVertical.xScrollbar.grid( row = gridVertical.row + 1, column = gridVertical.column, columnspan = 11, sticky = Tkinter.E + Tkinter.W ) gridVertical.yScrollbar = HiddenScrollbar( gridVertical.master ) gridVertical.yScrollbar.grid( row = gridVertical.row, column = gridVertical.column + 12, sticky = Tkinter.N + Tkinter.S ) canvasHeight = min( 1000, gridPosition.master.winfo_screenheight() - 540 ) - 6 - int( gridVertical.xScrollbar['width'] ) canvasWidth = min( 650, gridPosition.master.winfo_screenwidth() - 100 ) - 6 - int( gridVertical.yScrollbar['width'] ) gridVertical.canvas = Tkinter.Canvas( gridVertical.master, height = canvasHeight, highlightthickness = 0, width = canvasWidth ) gridVertical.frameGridVertical.master = Tkinter.Frame( gridVertical.canvas ) for setting in gridVertical.repository.displayEntities: setting.addToDialog( gridVertical.frameGridVertical ) addEmptyRow( gridVertical.frameGridVertical ) gridVertical.frameGridVertical.master.update_idletasks() gridVertical.xScrollbar.config( command = gridVertical.canvas.xview ) gridVertical.canvas['xscrollcommand'] = gridVertical.xScrollbar.set gridVertical.yScrollbar.config( command = gridVertical.canvas.yview ) gridVertical.canvas['yscrollcommand'] = gridVertical.yScrollbar.set gridVertical.canvas.create_window( 0, 0, anchor = Tkinter.NW, window = gridVertical.frameGridVertical.master ) gridVertical.canvas['scrollregion'] = gridVertical.frameGridVertical.master.grid_bbox() gridVertical.canvas.grid( row = gridVertical.row, column = gridVertical.column, columnspan = 12, sticky = Tkinter.E + Tkinter.W + Tkinter.N + Tkinter.S ) gridVertical.master.grid_rowconfigure( gridVertical.row, weight = 1 ) gridVertical.master.grid_columnconfigure( gridVertical.column + 11, weight = 1 ) gridVertical.frameGridVertical.master.lift() self.oldLatentString = self.latentStringVar.getString() def focusSetMaster( self, gridPosition ): "Set the focus to the plugin master." gridPosition.frameGridVertical.master.focus_set() def getFromPath( self, defaultRadioButton, directoryPath, repository ): "Initialize." self.defaultRadioButton = defaultRadioButton self.directoryPath = directoryPath self.name = 'PluginFrame' self.repository = repository repository.displayEntities.append(self) repository.preferences.append(self) return self def setStateToValue(self): "Set the state of all the plugins to the value." for gridTableValue in self.gridTable.values(): cancelRepository( gridTableValue.repository ) def setToDisplay(self): "Set the plugins to the display." pass def update(self): "Update the frame." if len(self.gridTable) < 1: return if self.oldLatentString == self.latentStringVar.getString(): return self.oldLatentString = self.latentStringVar.getString() self.repository.preferences.remove(self) for setting in self.repository.preferences: setting.setToDisplay() writeSettingsPrintMessage(self.repository) self.repository.preferences.append(self) if self.latentStringVar.getString() in self.gridTable: gridPosition = self.gridTable[self.latentStringVar.getString()] gridPosition.master.lift() self.focusSetMaster(gridPosition) return self.createFrame(self.gridPosition) def updateSaveListeners(self): "Update save listeners if any." gridTableKeys = self.gridTable.keys() gridTableKeys.sort() for gridTableKey in gridTableKeys: saveRepository( self.gridTable[ gridTableKey ].repository ) def writeToRepositoryWriter( self, repositoryWriter ): "Write tab separated name and value to the repository writer." pass class PluginGroupFrame( PluginFrame ): "A class to display the plugin groups in a frame." def createFrame( self, gridPosition ): "Create the frame." gridVertical = GridVertical( 0, 0 ) gridVertical.master = Tkinter.LabelFrame( gridPosition.master, borderwidth = 3, relief = 'raised') gridVertical.master.grid( row = gridPosition.row, column = gridPosition.column, columnspan = 11, sticky = Tkinter.E + Tkinter.W + Tkinter.N + Tkinter.S ) gridPosition.master.grid_rowconfigure( gridPosition.row, weight = 1 ) gridPosition.master.grid_columnconfigure( gridPosition.column + 10, weight = 1 ) if self.latentStringVar.getString() == '': self.defaultRadioButton.setSelect() self.gridTable[ self.latentStringVar.getString() ] = gridVertical path = os.path.join( self.directoryPath, self.latentStringVar.getString() ) pluginModule = archive.getModuleWithPath(path) if pluginModule == None: print('this should never happen, pluginModule in addToDialog in PluginFrame in settings is None') print(path) return gridVertical.repository = getReadRepository( pluginModule.getNewRepository() ) gridVertical.setExecutablesRepository( gridVertical.repository ) executeTitle = gridVertical.repository.executeTitle #if executeTitle != None: # executeButton = Tkinter.Button( gridVertical.master, activebackground = 'black', activeforeground = 'blue', text = executeTitle, command = gridVertical.execute ) # executeButton.grid( row = gridVertical.row, column = gridVertical.column, sticky = Tkinter.W ) # gridVertical.column += 1 #self.helpButton = Tkinter.Button( gridVertical.master, activebackground = 'black', activeforeground = 'white', text = "?", command = HelpPageRepository( gridVertical.repository ).openPage ) #self.helpButton.grid( row = gridVertical.row, column = gridVertical.column, sticky = Tkinter.W ) addEmptyRow( gridVertical ) gridVertical.increment() for setting in gridVertical.repository.displayEntities: setting.addToDialog( gridVertical ) gridVertical.master.update_idletasks() gridVertical.master.lift() self.oldLatentString = self.latentStringVar.getString() def focusSetMaster( self, gridPosition ): "Set the focus to the plugin master." gridPosition.master.focus_set() class Radio( BooleanSetting ): "A class to display, read & write a boolean with associated radio button." def addToDialog( self, gridPosition ): "Add this to the dialog." gridPosition.increment() self.createRadioButton( gridPosition ) self.radiobutton.grid( row = gridPosition.row, column = 0, columnspan = 3, sticky = Tkinter.W ) self.setStateToValue() def clickRadio(self): "Workaround for Tkinter bug, set the value." self.latentStringVar.setString( self.radiobutton['value'] ) if self.updateFunction != None: self.updateFunction() def createRadioButton( self, gridPosition ): "Create the radio button." self.radiobutton = Tkinter.Radiobutton( gridPosition.master, command = self.clickRadio, text = self.name, value = self.name, variable = self.latentStringVar.getVar() ) LabelHelp( self.repository.fileNameHelp, gridPosition.master, self.name, self.radiobutton ) def getFromRadio( self, latentStringVar, name, repository, value ): "Initialize." self.getFromValueOnly( name, repository, value ) self.latentStringVar = latentStringVar repository.displayEntities.append(self) repository.preferences.append(self) #when addToMenu is added to this entity, the line below should be uncommented # repository.menuEntities.append(self) return self def setSelect(self): "Set the int var and select the radio button." oldLatentStringValue = self.latentStringVar.getString() self.latentStringVar.setString( self.radiobutton['value'] ) self.radiobutton.select() if oldLatentStringValue == '': return False return oldLatentStringValue != self.latentStringVar.getString() def setStateToValue(self): "Set the checkbutton to the boolean." if self.value: if self.setSelect(): if self.updateFunction != None: self.updateFunction() def setToDisplay(self): "Set the boolean to the checkbutton." self.value = ( self.latentStringVar.getString() == self.radiobutton['value'] ) class RadioCapitalized( Radio ): "A class to display, read & write a boolean with associated radio button." def createRadioButton( self, gridPosition ): "Create the radio button." capitalizedName = getEachWordCapitalized( self.name ) self.radiobutton = Tkinter.Radiobutton( gridPosition.master, command = self.clickRadio, text = capitalizedName, value = self.name, variable = self.latentStringVar.getVar() ) class RadioCapitalizedButton( Radio ): "A class to display, read & write a boolean with associated radio button." def createRadioButton( self, gridPosition ): "Create the radio button." capitalizedName = getEachWordCapitalized( self.name ) self.radiobutton = Tkinter.Radiobutton( gridPosition.master, command = self.clickRadio, text = capitalizedName, value = self.name, variable = self.latentStringVar.getVar() ) self.displayButton = Tkinter.Button( gridPosition.master, activebackground = 'black', activeforeground = 'white', text = capitalizedName, command = self.displayDialog ) self.displayButton.grid( row = gridPosition.row, column = 3, columnspan = 2 ) def displayDialog(self): "Display function." ToolDialog().getFromPath( self.path ).display() self.setSelect() def getFromPath( self, latentStringVar, name, path, repository, value ): "Initialize." self.getFromRadio( latentStringVar, name, repository, value ) self.path = path return self class RadioPlugin( RadioCapitalized ): "A class to display, read & write a boolean with associated radio button." def addToDialog( self, gridPosition ): "Add this to the dialog." self.createRadioButton( gridPosition ) self.radiobutton['activeforeground'] = 'magenta' self.radiobutton['selectcolor'] = 'white' self.radiobutton['borderwidth'] = 3 self.radiobutton['indicatoron'] = 0 setButtonFontWeightString( self.radiobutton, self.important ) self.incrementGridPosition( gridPosition ) self.setStateToValue() def getFromRadio( self, important, latentStringVar, name, repository, value ): "Initialize." self.important = important return RadioCapitalized.getFromRadio( self, latentStringVar, name, repository, value ) def incrementGridPosition( self, gridPosition ): "Increment the grid position." gridPosition.incrementGivenNumberOfColumns( 10 ) self.radiobutton.grid( row = gridPosition.row, column = gridPosition.column, sticky = Tkinter.W ) class TextSetting( StringSetting ): "A class to display, read & write a text." def __init__(self): "Set the update function to none." self.tokenConversions = [ TokenConversion(), TokenConversion('carriageReturn', '\r'), TokenConversion('doubleQuote', '"'), TokenConversion('newline', '\n'), TokenConversion('semicolon', ';'), TokenConversion('singleQuote', "'" ), TokenConversion('tab', '\t') ] self.updateFunction = None def addToDialog( self, gridPosition ): "Add this to the dialog." gridPosition.increment() self.label = Tkinter.Label( gridPosition.master, text = self.name ) self.label.grid( row = gridPosition.row, column = 0, columnspan = 3, sticky = Tkinter.W ) gridPosition.increment() self.entry = Tkinter.Text( gridPosition.master ) self.setStateToValue() self.entry.grid( row = gridPosition.row, column = 0, columnspan = 5, sticky = Tkinter.W ) LabelHelp( self.repository.fileNameHelp, gridPosition.master, self.name, self.label ) def getFromValue( self, name, repository, value ): "Initialize." self.getFromValueOnly( name, repository, value ) repository.displayEntities.append(self) repository.preferences.append(self) return self def setStateToValue(self): "Set the entry to the value." try: self.entry.delete( 1.0, Tkinter.END ) self.entry.insert( Tkinter.INSERT, self.value ) except: pass def setToDisplay(self): "Set the string to the entry field." valueString = self.entry.get( 1.0, Tkinter.END ) self.setValueToString( valueString ) def setValueToSplitLine( self, lineIndex, lines, splitLine ): "Set the value to the second word of a split line." replacedValue = splitLine[1] for tokenConversion in reversed( self.tokenConversions ): replacedValue = tokenConversion.getTokenizedString( replacedValue ) self.setValueToString( replacedValue ) def writeToRepositoryWriter( self, repositoryWriter ): "Write tab separated name and value to the repository writer." replacedValue = self.value for tokenConversion in self.tokenConversions: replacedValue = tokenConversion.getNamedString( replacedValue ) repositoryWriter.write('%s%s%s\n' % ( self.name, globalSpreadsheetSeparator, replacedValue ) ) class TokenConversion: "A class to convert tokens in a string." def __init__( self, name = 'replaceToken', token = '___replaced___'): "Set the name and token." self.replacedName = '___replaced___' + name self.token = token def getNamedString( self, text ): "Get a string with the tokens changed to names." return text.replace( self.token, self.replacedName ) def getTokenizedString( self, text ): "Get a string with the names changed to tokens." return text.replace( self.replacedName, self.token ) class ToolDialog: "A class to display the tool repository dialog." def addPluginToMenu( self, menu, path ): "Add the display command to the menu." name = os.path.basename(path) self.path = path menu.add_command( label = getEachWordCapitalized( name ) + '...', command = self.display ) def display(self): "Display the tool repository dialog." global globalRepositoryDialogListTable for repositoryDialog in globalRepositoryDialogListTable: if getPathInFabmetheusFromFileNameHelp( repositoryDialog.repository.fileNameHelp ) == self.path: liftRepositoryDialogs( globalRepositoryDialogListTable[ repositoryDialog ] ) return self.repositoryDialog = getDisplayedDialogFromPath( self.path ) def getFromPath( self, path ): "Initialize and return display function." self.path = path return self class WindowPosition( StringSetting ): "A class to display, read & write a window position." def addToDialog( self, gridPosition ): "Set the root to later get the geometry." self.root = gridPosition.master self.setToDisplay() def getFromValue( self, repository, value ): "Initialize." self.getFromValueOnly('WindowPosition', repository, value ) repository.displayEntities.append(self) repository.preferences.append(self) return self def setToDisplay(self): "Set the string to the window position." try: geometryString = self.root.geometry() except: return if geometryString == '1x1+0+0': return firstPlusIndexPlusOne = geometryString.find('+') + 1 self.value = geometryString[ firstPlusIndexPlusOne : ] def setWindowPosition(self): "Set the window position." movedGeometryString = '%sx%s+%s' % ( self.root.winfo_reqwidth(), self.root.winfo_reqheight(), self.value ) self.root.geometry( movedGeometryString ) class RepositoryDialog: def __init__( self, repository, root ): "Add entities to the dialog." self.isFirst = ( len( globalRepositoryDialogListTable.keys() ) == 0 ) self.closeListener = CloseListener(self) self.repository = repository self.gridPosition = GridVertical( 0, - 1 ) self.gridPosition.setExecutablesRepository(repository) self.gridPosition.master = root self.root = root self.openDialogListeners = [] repository.repositoryDialog = self root.withdraw() title = repository.title if repository.fileNameInput != None: title = os.path.basename( repository.fileNameInput.value ) + ' - ' + title root.title( title ) fileHelpMenuBar = FileHelpMenuBar( root ) fileHelpMenuBar.completeMenu( self.close, repository, self.save, self ) for setting in repository.displayEntities: setting.addToDialog( self.gridPosition ) if self.gridPosition.row < 20: addEmptyRow( self.gridPosition ) self.addButtons( repository, root ) root.update_idletasks() self.setWindowPositionDeiconify() root.deiconify() for openDialogListener in self.openDialogListeners: openDialogListener.openDialog() def __repr__(self): "Get the string representation of this RepositoryDialog." return self.repository.title def addButtons( self, repository, root ): "Add buttons to the dialog." columnIndex = 0 self.gridPosition.increment() saveCommand = self.save saveText = 'Save' if self.isFirst: saveCommand = saveAll saveText = 'Save All' if repository.executeTitle != None: executeButton = Tkinter.Button( root, activebackground = 'black', activeforeground = 'blue', text = repository.executeTitle, command = self.gridPosition.execute ) executeButton.grid( row = self.gridPosition.row, column = columnIndex, columnspan = 2, sticky = Tkinter.W ) columnIndex += 2 self.helpButton = Tkinter.Button( root, activebackground = 'black', activeforeground = 'white', text = "?", command = HelpPageRepository(self.repository).openPage ) self.helpButton.grid( row = self.gridPosition.row, column = columnIndex, sticky = Tkinter.W ) self.closeListener.listenToWidget( self.helpButton ) columnIndex += 6 cancelButton = Tkinter.Button( root, activebackground = 'black', activeforeground = 'orange', command = self.cancel, fg = 'orange', text = 'Cancel') cancelButton.grid( row = self.gridPosition.row, column = columnIndex ) columnIndex += 1 self.saveButton = Tkinter.Button( root, activebackground = 'black', activeforeground = 'darkgreen', command = saveCommand, fg = 'darkgreen', text = saveText ) self.saveButton.grid( row = self.gridPosition.row, column = columnIndex ) def cancel(self, event=None): "Set all entities to their saved state." cancelRepository(self.repository) def close(self, event=None): "The dialog was closed." try: self.root.destroy() except: pass def save(self, event=None): "Set the entities to the dialog then write them." saveRepository(self.repository) def setWindowPositionDeiconify(self): "Set the window position if that setting exists." for setting in self.repository.preferences: if setting.name == 'WindowPosition': setting.setWindowPosition() return