2046 lines
77 KiB
Python
2046 lines
77 KiB
Python
"""
|
|
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 '(<alterationFile>) %s (</alterationFile>)' % 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 = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">\n<html>\n<head>\n'
|
|
redirectionText += '<meta http-equiv="REFRESH" content="0;url=%s"></head>\n</HTML>\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('<Return>', 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('<Destroy>', 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('<Control-KeyPress-s>', saveFunction, self.root, self.fileMenu, 'Save')
|
|
self.fileMenu.add_command(label = "Save and Close", command = self.saveClose)
|
|
addAcceleratorCommand('<Control-KeyPress-w>', closeFunction, self.root, self.fileMenu, 'Close')
|
|
self.fileMenu.add_separator()
|
|
addAcceleratorCommand('<Control-KeyPress-q>', 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('<Return>', 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('<Button-1>', self.unpostPopupMenu )
|
|
widget.bind('<Button-2>', self.unpostPopupMenu )
|
|
widget.bind('<Button-3>', 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
|