chiark / gitweb /
Added skin setting to advanced settings
[cura.git] / Cura / cura_sf / fabmetheus_utilities / settings.py
1 """
2 Settings is a collection of utilities to display, read & write the settings and position widgets.
3
4 """
5
6 from __future__ import absolute_import
7 #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.
8 import __init__
9
10 import os, sys
11 import types, math
12
13 from util import profile
14 from fabmetheus_utilities import archive
15
16 def DEFSET(setting):
17         return setting.value
18
19 def storedSetting(name):
20         return lambda setting: profile.getProfileSetting(name)
21 def storedPreference(name):
22         return lambda setting: profile.getPreference(name)
23
24 def ifSettingAboveZero(name):
25         return lambda setting: float(profile.getProfileSetting(name)) > 0
26
27 def ifSettingIs(name, value):
28         return lambda setting: profile.getProfileSetting(name) == value
29
30 def raftLayerCount(setting):
31         if profile.getProfileSetting('enable_raft') == "True":
32                 return '1'
33         return '0'
34
35 def storedPercentSetting(name):
36         return lambda setting: float(profile.getProfileSetting(name)) / 100
37
38 def calculateEdgeWidth(setting):
39         return profile.calculateEdgeWidth()
40
41 def calculateShells(setting):
42         return profile.calculateLineCount() - 1
43
44 def calculateShellsBase(setting):
45         edgeWidth = profile.calculateEdgeWidth()
46         extraWall = float(profile.getProfileSetting('extra_base_wall_thickness'))
47         
48         return profile.calculateLineCount() - 1 + int(extraWall / edgeWidth + 0.0001)
49
50 def calculateSolidLayerCount(setting):
51         return profile.calculateSolidLayerCount()
52
53 def firstLayerSpeedRatio(setting):
54         bottomSpeed = float(profile.getProfileSetting('bottom_layer_speed'))
55         speed = float(profile.getProfileSetting('print_speed'))
56         return bottomSpeed/speed
57
58 def calcSupportDistanceRatio(setting):
59         edgeWidth = calculateEdgeWidth(setting)
60         distance = float(profile.getProfileSetting('support_distance'))
61         return distance / edgeWidth
62
63 def calculateMultiplyDistance(setting):
64         edgeWidth = calculateEdgeWidth(setting)
65         return 10.0 / edgeWidth
66
67 def calcBottomLayerFlowRateRatio(setting):
68         bottomThickness = float(profile.getProfileSetting('bottom_thickness'))
69         layerThickness = float(profile.getProfileSetting('layer_height'))
70         if bottomThickness < layerThickness:
71                 return 1.0
72         return bottomThickness / layerThickness
73
74 def calcExtraBottomThickness(setting):
75         bottomThickness = float(profile.getProfileSetting('bottom_thickness'))
76         layerThickness = float(profile.getProfileSetting('layer_height'))
77         if bottomThickness < layerThickness:
78                 return 0.0
79         return bottomThickness - layerThickness
80
81 def calcLayerSkip(setting):
82         bottomThickness = float(profile.getProfileSetting('bottom_thickness'))
83         layerThickness = float(profile.getProfileSetting('layer_height'))
84         if bottomThickness < layerThickness:
85                 return 0
86         return int(math.ceil((bottomThickness - layerThickness) / layerThickness + 0.0001) - 1)
87
88 def getProfileInformation():
89         return {
90                 'carve': {
91                         'Add_Layer_Template_to_SVG': DEFSET,
92                         'Edge_Width_mm': calculateEdgeWidth,
93                         'Extra_Decimal_Places_float': DEFSET,
94                         'Import_Coarseness_ratio': DEFSET,
95                         'Layer_Height_mm': storedSetting("layer_height"),
96                         'Layers_From_index': calcLayerSkip,
97                         'Layers_To_index': DEFSET,
98                         'Correct_Mesh': DEFSET,
99                         'Unproven_Mesh': DEFSET,
100                         'SVG_Viewer': DEFSET,
101                         'FlipX': storedSetting("flip_x"),
102                         'FlipY': storedSetting("flip_y"),
103                         'FlipZ': storedSetting("flip_z"),
104                         'SwapXZ': storedSetting("swap_xz"),
105                         'SwapYZ': storedSetting("swap_yz"),
106                         'Scale': storedSetting("model_scale"),
107                         'Rotate': storedSetting("model_rotate_base"),
108                 },'scale': {
109                         'Activate_Scale': "False",
110                         'XY_Plane_Scale_ratio': DEFSET,
111                         'Z_Axis_Scale_ratio': DEFSET,
112                         'SVG_Viewer': DEFSET,
113                 },'bottom': {
114                         'Activate_Bottom': DEFSET,
115                         'Additional_Height_over_Layer_Thickness_ratio': DEFSET,
116                         'Altitude_mm': calcExtraBottomThickness,
117                         'SVG_Viewer': DEFSET,
118                 },'preface': {
119                         'Meta': DEFSET,
120                         'Set_Positioning_to_Absolute': DEFSET,
121                         'Set_Units_to_Millimeters': DEFSET,
122                         'Start_at_Home': DEFSET,
123                         'Turn_Extruder_Off_at_Shut_Down': DEFSET,
124                         'Turn_Extruder_Off_at_Start_Up': DEFSET,
125                 },'widen': {
126                         'Activate_Widen': DEFSET,
127                         'Widen_Width_over_Edge_Width_ratio': DEFSET,
128                 },'inset': {
129                         'Add_Custom_Code_for_Temperature_Reading': "False",
130                         'Infill_in_Direction_of_Bridge': "True",
131                         'Infill_Width': storedSetting("nozzle_size"),
132                         'Loop_Order_Choice': DEFSET,
133                         'Overlap_Removal_Width_over_Perimeter_Width_ratio': DEFSET,
134                         'Turn_Extruder_Heater_Off_at_Shut_Down': DEFSET,
135                         'Volume_Fraction_ratio': DEFSET,
136                 },'fill': {
137                         'Activate_Fill': "True",
138                         'Solid_Surface_Top': storedSetting("solid_top"),
139                         'Override_First_Layer_Sequence': storedSetting("force_first_layer_sequence"),
140                         'Diaphragm_Period_layers': DEFSET,
141                         'Diaphragm_Thickness_layers': DEFSET,
142                         'Extra_Shells_on_Alternating_Solid_Layer_layers': calculateShells,
143                         'Extra_Shells_on_Base_layers': calculateShellsBase,
144                         'Extra_Shells_on_Sparse_Layer_layers': calculateShells,
145                         'Grid_Circle_Separation_over_Perimeter_Width_ratio': DEFSET,
146                         'Grid_Extra_Overlap_ratio': DEFSET,
147                         'Grid_Junction_Separation_Band_Height_layers': DEFSET,
148                         'Grid_Junction_Separation_over_Octogon_Radius_At_End_ratio': DEFSET,
149                         'Grid_Junction_Separation_over_Octogon_Radius_At_Middle_ratio': DEFSET,
150                         'Infill_Begin_Rotation_degrees': DEFSET,
151                         'Infill_Begin_Rotation_Repeat_layers': DEFSET,
152                         'Infill_Odd_Layer_Extra_Rotation_degrees': DEFSET,
153                         'Grid_Circular': ifSettingIs('infill_type', 'Grid Circular'),
154                         'Grid_Hexagonal': ifSettingIs('infill_type', 'Grid Hexagonal'),
155                         'Grid_Rectangular': ifSettingIs('infill_type', 'Grid Rectangular'),
156                         'Line': ifSettingIs('infill_type', 'Line'),
157                         'Infill_Perimeter_Overlap_ratio': storedPercentSetting('fill_overlap'),
158                         'Infill_Solidity_ratio': storedPercentSetting('fill_density'),
159                         'Infill_Width': storedSetting("nozzle_size"),
160                         'Sharpest_Angle_degrees': DEFSET,
161                         'Solid_Surface_Thickness_layers': calculateSolidLayerCount,
162                         'Start_From_Choice': DEFSET,
163                         'Surrounding_Angle_degrees': DEFSET,
164                         'Thread_Sequence_Choice': storedSetting('sequence'),
165                 },'multiply': {
166                         'Activate_Multiply': "True",
167                         'Center_X_mm': storedSetting("machine_center_x"),
168                         'Center_Y_mm': storedSetting("machine_center_y"),
169                         'Number_of_Columns_integer': storedSetting('model_multiply_x'),
170                         'Number_of_Rows_integer': storedSetting('model_multiply_y'),
171                         'Reverse_Sequence_every_Odd_Layer': DEFSET,
172                         'Separation_over_Perimeter_Width_ratio': calculateMultiplyDistance,
173                 },'speed': {
174                         'Activate_Speed': "True",
175                         'Add_Flow_Rate': "True",
176                         'Bridge_Feed_Rate_Multiplier_ratio': storedPercentSetting('bridge_speed'),
177                         'Bridge_Flow_Rate_Multiplier_ratio': storedPercentSetting('bridge_material_amount'),
178                         'Duty_Cyle_at_Beginning_portion': DEFSET,
179                         'Duty_Cyle_at_Ending_portion': DEFSET,
180                         'Feed_Rate_mm/s': storedSetting("print_speed"),
181                         'Flow_Rate_Setting_float': storedSetting("print_speed"),
182                         'Object_First_Layer_Feed_Rate_Infill_Multiplier_ratio': firstLayerSpeedRatio,
183                         'Object_First_Layer_Feed_Rate_Perimeter_Multiplier_ratio': firstLayerSpeedRatio,
184                         'Object_First_Layer_Feed_Rate_Travel_Multiplier_ratio': firstLayerSpeedRatio,
185                         'Object_First_Layer_Flow_Rate_Infill_Multiplier_ratio': firstLayerSpeedRatio,
186                         'Object_First_Layer_Flow_Rate_Perimeter_Multiplier_ratio': firstLayerSpeedRatio,
187                         'Object_First_Layers_Amount_Of_Layers_For_Speed_Change': DEFSET,
188                         'Orbital_Feed_Rate_over_Operating_Feed_Rate_ratio': DEFSET,
189                         'Maximum_Z_Feed_Rate_mm/s': DEFSET,
190                         'Perimeter_Feed_Rate_Multiplier_ratio': DEFSET,
191                         'Perimeter_Flow_Rate_Multiplier_ratio': DEFSET,
192                         'Travel_Feed_Rate_mm/s': storedSetting("travel_speed"),
193                         'Bottom_layer_flow_rate_ratio': calcBottomLayerFlowRateRatio,
194                 },'temperature': {
195                         'Activate_Temperature': DEFSET,#ifSettingAboveZero('print_temperature'),
196                         'Cooling_Rate_Celcius/second': DEFSET,
197                         'Heating_Rate_Celcius/second': DEFSET,
198                         'Base_Temperature_Celcius': DEFSET,#storedSetting("print_temperature"),
199                         'Interface_Temperature_Celcius': DEFSET,#storedSetting("print_temperature"),
200                         'Object_First_Layer_Infill_Temperature_Celcius': DEFSET,#storedSetting("print_temperature"),
201                         'Object_First_Layer_Perimeter_Temperature_Celcius': DEFSET,#storedSetting("print_temperature"),
202                         'Object_Next_Layers_Temperature_Celcius': DEFSET,#storedSetting("print_temperature"),
203                         'Support_Layers_Temperature_Celcius': DEFSET,#storedSetting("print_temperature"),
204                         'Supported_Layers_Temperature_Celcius': DEFSET,#storedSetting("print_temperature"),
205                 },'raft': {
206                         'Activate_Raft': "True",
207                         'Add_Raft,_Elevate_Nozzle,_Orbit': DEFSET,
208                         'Base_Feed_Rate_Multiplier_ratio': DEFSET,
209                         'Base_Flow_Rate_Multiplier_ratio': storedPercentSetting('raft_base_material_amount'),
210                         'Base_Infill_Density_ratio': DEFSET,
211                         'Base_Layer_Thickness_over_Layer_Thickness': DEFSET,
212                         'Base_Layers_integer': raftLayerCount,
213                         'Base_Nozzle_Lift_over_Base_Layer_Thickness_ratio': DEFSET,
214                         'Initial_Circling': DEFSET,
215                         'Infill_Overhang_over_Extrusion_Width_ratio': DEFSET,
216                         'Interface_Feed_Rate_Multiplier_ratio': DEFSET,
217                         'Interface_Flow_Rate_Multiplier_ratio': storedPercentSetting('raft_interface_material_amount'),
218                         'Interface_Infill_Density_ratio': DEFSET,
219                         'Interface_Layer_Thickness_over_Layer_Thickness': DEFSET,
220                         'Interface_Layers_integer': raftLayerCount,
221                         'Interface_Nozzle_Lift_over_Interface_Layer_Thickness_ratio': DEFSET,
222                         'Name_of_Support_End_File': DEFSET,
223                         'Name_of_Support_Start_File': DEFSET,
224                         'Operating_Nozzle_Lift_over_Layer_Thickness_ratio': DEFSET,
225                         'Raft_Additional_Margin_over_Length_%': DEFSET,
226                         'Raft_Margin_mm': storedSetting('raft_margin'),
227                         'Support_Cross_Hatch': 'False',
228                         'Support_Flow_Rate_over_Operating_Flow_Rate_ratio': storedPercentSetting('support_rate'),
229                         'Support_Gap_over_Perimeter_Extrusion_Width_ratio': calcSupportDistanceRatio,
230                         'Support_Material_Choice_': storedSetting('support'),
231                         'Support_Minimum_Angle_degrees': DEFSET,
232                 },'skirt': {
233                         'Skirt_line_count': storedSetting("skirt_line_count"),
234                         'Convex': "True",
235                         'Gap_Width_mm': storedSetting("skirt_gap"),
236                         'Layers_To_index': "1",
237                 },'joris': {
238                         'Activate_Joris': storedSetting("joris"),
239                         'Layers_From_index': calculateSolidLayerCount,
240                 },'chamber': {
241                         'Activate_Chamber': "False",
242                         'Bed_Temperature_Celcius': DEFSET,
243                         'Bed_Temperature_Begin_Change_Height_mm': DEFSET,
244                         'Bed_Temperature_End_Change_Height_mm': DEFSET,
245                         'Bed_Temperature_End_Celcius': DEFSET,
246                         'Chamber_Temperature_Celcius': DEFSET,
247                         'Holding_Force_bar': DEFSET,
248                 },'tower': {
249                         'Activate_Tower': "False",
250                         'Extruder_Possible_Collision_Cone_Angle_degrees': DEFSET,
251                         'Maximum_Tower_Height_layers': DEFSET,
252                         'Tower_Start_Layer_integer': DEFSET,
253                 },'jitter': {
254                         'Activate_Jitter': "False",
255                         'Jitter_Over_Perimeter_Width_ratio': DEFSET,
256                 },'clip': {
257                         'Activate_Clip': "False",
258                         'Clip_Over_Perimeter_Width_ratio': DEFSET,
259                         'Maximum_Connection_Distance_Over_Perimeter_Width_ratio': DEFSET,
260                 },'smooth': {
261                         'Activate_Smooth': "False",
262                         'Layers_From_index': DEFSET,
263                         'Maximum_Shortening_over_Width_float': DEFSET,
264                 },'stretch': {
265                         'Activate_Stretch': "False",
266                         'Cross_Limit_Distance_Over_Perimeter_Width_ratio': DEFSET,
267                         'Loop_Stretch_Over_Perimeter_Width_ratio': DEFSET,
268                         'Path_Stretch_Over_Perimeter_Width_ratio': DEFSET,
269                         'Perimeter_Inside_Stretch_Over_Perimeter_Width_ratio': DEFSET,
270                         'Perimeter_Outside_Stretch_Over_Perimeter_Width_ratio': DEFSET,
271                         'Stretch_From_Distance_Over_Perimeter_Width_ratio': DEFSET,
272                 },'skin': {
273                         'Activate_Skin': storedSetting("enable_skin"),
274                         'Horizontal_Infill_Divisions_integer': "1",
275                         'Horizontal_Perimeter_Divisions_integer': "1",
276                         'Vertical_Divisions_integer': "2",
277                         'Hop_When_Extruding_Infill': "False",
278                         'Layers_From_index': "1",
279                 },'comb': {
280                         'Activate_Comb': "True",
281                         'Running_Jump_Space_mm': DEFSET,
282                 },'cool': {
283                         'Activate_Cool': "True",
284                         'Bridge_Cool_Celcius': DEFSET,
285                         'Cool_Type': DEFSET,
286                         'Maximum_Cool_Celcius': DEFSET,
287                         'Minimum_Layer_Time_seconds': storedSetting("cool_min_layer_time"),
288                         'Minimum_Orbital_Radius_millimeters': DEFSET,
289                         'Name_of_Cool_End_File': DEFSET,
290                         'Name_of_Cool_Start_File': DEFSET,
291                         'Orbital_Outset_millimeters': DEFSET,
292                         'Turn_Fan_On_at_Beginning': storedSetting("fan_enabled"),
293                         'Turn_Fan_Off_at_Ending': "False",
294                         'Minimum_feed_rate_mm/s': storedSetting("cool_min_feedrate"),
295                         'Fan_on_at_layer': storedSetting('fan_layer'),
296                         'Fan_speed_%': storedSetting('fan_speed'),
297                 },'hop': {
298                         'Activate_Hop': "False",
299                         'Hop_Over_Layer_Thickness_ratio': DEFSET,
300                         'Minimum_Hop_Angle_degrees': DEFSET,
301                 },'wipe': {
302                         'Activate_Wipe': "False",
303                         'Arrival_X_mm': DEFSET,
304                         'Arrival_Y_mm': DEFSET,
305                         'Arrival_Z_mm': DEFSET,
306                         'Departure_X_mm': DEFSET,
307                         'Departure_Y_mm': DEFSET,
308                         'Departure_Z_mm': DEFSET,
309                         'Wipe_X_mm': DEFSET,
310                         'Wipe_Y_mm': DEFSET,
311                         'Wipe_Z_mm': DEFSET,
312                         'Wipe_Period_layers': DEFSET,
313                 },'oozebane': {
314                         'Activate_Oozebane': "False",
315                         'After_Startup_Distance_millimeters': DEFSET,
316                         'Early_Shutdown_Distance_millimeters': DEFSET,
317                         'Early_Startup_Distance_Constant_millimeters': DEFSET,
318                         'Early_Startup_Maximum_Distance_millimeters': DEFSET,
319                         'First_Early_Startup_Distance_millimeters': DEFSET,
320                         'Minimum_Distance_for_Early_Startup_millimeters': DEFSET,
321                         'Minimum_Distance_for_Early_Shutdown_millimeters': DEFSET,
322                         'Slowdown_Startup_Steps_positive_integer': DEFSET,
323                 },'dwindle': {
324                         'Activate_Dwindle': "False",
325                         'End_Rate_Multiplier_ratio': DEFSET,
326                         'Pent_Up_Volume_cubic_millimeters': DEFSET,
327                         'Slowdown_Steps_positive_integer': DEFSET,
328                         'Slowdown_Volume_cubic_millimeters': DEFSET,
329                 },'splodge': {
330                         'Activate_Splodge': "False",
331                         'Initial_Lift_over_Extra_Thickness_ratio': DEFSET,
332                         'Initial_Splodge_Feed_Rate_mm/s': DEFSET,
333                         'Operating_Splodge_Feed_Rate_mm/s': DEFSET,
334                         'Operating_Splodge_Quantity_Length_millimeters': DEFSET,
335                         'Initial_Splodge_Quantity_Length_millimeters': DEFSET,
336                         'Operating_Lift_over_Extra_Thickness_ratio': DEFSET,
337                 },'home': {
338                         'Activate_Home': "False",
339                         'Name_of_Home_File': DEFSET,
340                 },'lash': {
341                         'Activate_Lash': "False",
342                         'X_Backlash_mm': DEFSET,
343                         'Y_Backlash_mm': DEFSET,
344                 },'fillet': {
345                         'Activate_Fillet': "False",
346                         'Arc_Point': DEFSET,
347                         'Arc_Radius': DEFSET,
348                         'Arc_Segment': DEFSET,
349                         'Bevel': DEFSET,
350                         'Corner_Feed_Rate_Multiplier_ratio': DEFSET,
351                         'Fillet_Radius_over_Perimeter_Width_ratio': DEFSET,
352                         'Reversal_Slowdown_Distance_over_Perimeter_Width_ratio': DEFSET,
353                         'Use_Intermediate_Feed_Rate_in_Corners': DEFSET,
354                 },'limit': {
355                         'Activate_Limit': "False",
356                         'Maximum_Initial_Feed_Rate_mm/s': DEFSET,
357                 },'unpause': {
358                         'Activate_Unpause': "False",
359                         'Delay_milliseconds': DEFSET,
360                         'Maximum_Speed_ratio': DEFSET,
361                 },'dimension': {
362                         'Activate_Dimension': "True",
363                         'Absolute_Extrusion_Distance': "True",
364                         'Relative_Extrusion_Distance': "False",
365                         'Extruder_Retraction_Speed_mm/s': storedSetting('retraction_speed'),
366                         'Filament_Diameter_mm': storedSetting("filament_diameter"),
367                         'Filament_Packing_Density_ratio': storedSetting("filament_density"),
368                         'Maximum_E_Value_before_Reset_float': DEFSET,
369                         'Minimum_Travel_for_Retraction_millimeters': storedSetting("retraction_min_travel"),
370                         'Retract_Within_Island': DEFSET,
371                         'Retraction_Distance_millimeters': storedSetting('retraction_amount'),
372                         'Restart_Extra_Distance_millimeters': storedSetting('retraction_extra'),
373                 },'alteration': {
374                         'Activate_Alteration': "True",
375                         'Name_of_End_File': "end.gcode",
376                         'Name_of_Start_File': "start.gcode",
377                         'Remove_Redundant_Mcode': "True",
378                         'Replace_Variable_with_Setting': DEFSET,
379                 },'export': {
380                         'Activate_Export': "True",
381                         'Add_Descriptive_Extension': DEFSET,
382                         'Add_Export_Suffix': DEFSET,
383                         'Add_Profile_Extension': DEFSET,
384                         'Add_Timestamp_Extension': DEFSET,
385                         'Also_Send_Output_To': DEFSET,
386                         'Analyze_Gcode': DEFSET,
387                         'Comment_Choice': DEFSET,
388                         'Do_Not_Change_Output': DEFSET,
389                         'binary_16_byte': DEFSET,
390                         'gcode_step': DEFSET,
391                         'gcode_time_segment': DEFSET,
392                         'gcode_small': DEFSET,
393                         'File_Extension': DEFSET,
394                         'Name_of_Replace_File': DEFSET,
395                         'Save_Penultimate_Gcode': "False",
396                 }
397         }
398
399 def safeConfigName(name):
400         return name.replace("=", "").replace(":", "").replace(" ", "_").replace("(", "").replace(")", "")
401
402 def getReadRepository(repository):
403         "Read the configuration for this 'repository'"
404         
405         info = getProfileInformation()
406         if not info.has_key(repository.name):
407                 print "Warning: Plugin: " + repository.name + " missing from Cura info"
408                 return repository
409         info = info[repository.name]
410         
411         #print('getReadRepository:', repository.name)
412         for p in repository.preferences:
413                 name = safeConfigName(p.name)
414                 if not info.has_key(name):
415                         print "Setting: " + repository.name + ":" + name + " missing from Cura info"
416                         continue
417                 if isinstance(info[name], types.FunctionType):
418                         p.setValueToString(str(info[name](p)))
419                 else:
420                         p.setValueToString(str(info[name]))
421
422         return repository
423
424 def printProgress(layerIndex, procedureName):
425         print ("Progress[" + procedureName + ":" + str(layerIndex+1) + "]")
426         sys.stdout.flush()
427
428 def printProgressByNumber(layerIndex, numberOfLayers, procedureName):
429         print ("Progress[" + procedureName + ":" + str(layerIndex+1) + ":" + str(numberOfLayers) + "]")
430         sys.stdout.flush()
431
432 def getAlterationFileLines(fileName):
433         'Get the alteration file line and the text lines from the fileName in the alterations directories.'
434         return getAlterationLines(fileName)
435
436 def getAlterationLines(fileName):
437         return archive.getTextLines(getAlterationFile(fileName))
438
439 def getAlterationFile(fileName):
440         return profile.getAlterationFileContents(fileName)
441
442 ####################################
443 ## Configuration settings classes ##
444 ####################################
445
446 class GeneralSetting:
447         "Just a basic setting subclass"
448         def getFromValue( self, name, repository, value ):
449                 #print('GeneralSetting:', name, repository, value )
450                 self.name = name
451                 self.value = value
452                 repository.preferences.append(self)
453                 return self
454
455 class StringSetting(GeneralSetting):
456         "A class to display, read & write a string."
457         def setValueToString(self, value):
458                 self.value = value
459
460 class BooleanSetting( GeneralSetting ):
461         "A class to display, read & write a boolean."
462         def setValueToString(self, value):
463                 self.value = str(value) == "True"
464
465 class LatentStringVar:
466         "This is actually used as 'group' object for Radio buttons. (Did I mention the code is a mess?)"
467         "This class doesn't have a name, and isn't really used for anything. It doesn't even know which repository it belongs to"
468
469 class Radio( BooleanSetting ):
470         "A class to display, read & write a boolean with associated radio button."
471         def getFromRadio( self, latentStringVar, name, repository, value ):
472                 "Initialize."
473                 #print('Radio->getFromRadio:', latentStringVar, name, repository, value )
474                 self.name = name
475                 self.value = value
476                 repository.preferences.append(self)
477                 return self
478
479 class RadioCapitalized( Radio ):
480         "A class to display, read & write a boolean with associated radio button."
481
482 class RadioCapitalizedButton( Radio ):
483         "A class to display, read & write a boolean with associated radio button. With an added configuration dialog button"
484         "Only used for the extra export options, which we are not using, so ignore the path for now"
485         def getFromPath( self, latentStringVar, name, path, repository, value ):
486                 "Initialize."
487                 #print('RadioCapitalizedButton->getFromPath:', latentStringVar, name, path, repository, value )
488                 self.name = name
489                 self.value = value
490                 repository.preferences.append(self)
491                 return self
492                 
493 class FileNameInput(StringSetting ):
494         "A class to display, read & write a fileName."
495         def getFromFileName( self, fileTypes, name, repository, value ):
496                 #print('FileNameInput:getFromFileName:', self, fileTypes, name, repository, value )
497                 self.name = name
498                 self.value = value
499                 return self
500
501 class HelpPage:
502     "A class to open a help page."
503     def getOpenFromAbsolute( self, hypertextAddress ):
504         return self
505
506 class MenuButtonDisplay:
507         "A class to add a combo box selection."
508         def getFromName( self, name, repository ):
509                 #print('MenuButtonDisplay->getFromName:', name, repository )
510                 self.name = name
511                 self.value = "ERROR"
512                 self.radioList = []
513                 repository.preferences.append(self)
514                 return self
515         
516         def addRadio(self, radio, default):
517                 if default:
518                         self.value = radio.name
519                 self.radioList.append(radio)
520         
521         def setValueToString(self, value):
522                 valueFound = False
523                 for radio in self.radioList:
524                         if radio.name == value:
525                                 valueFound = True;
526                 if valueFound:
527                         self.value = value
528                         for radio in self.radioList:
529                                 radio.value = (radio.name == value)
530
531 class MenuRadio( BooleanSetting ):
532         "A class to display, read & write a boolean with associated combo box selection."
533         def getFromMenuButtonDisplay( self, menuButtonDisplay, name, repository, value ):
534                 "Initialize."
535                 #print('MenuRadio->getFromMenuButtonDisplay:', menuButtonDisplay, name, repository, value )
536                 self.name = name
537                 self.value = value
538                 menuButtonDisplay.addRadio(self, value)
539                 return self
540
541 class LabelDisplay:
542         "A class to add a label."
543         def getFromName( self, name, repository ):
544                 "Initialize."
545                 return self
546
547 class FloatSetting(GeneralSetting):
548         "A class to display, read & write a float."
549         def setValueToString(self, value):
550                 self.value = float(value)
551
552 class FloatSpin( FloatSetting ):
553         "A class to display, read & write an float in a spin box."
554         def getFromValue(self, from_, name, repository, to, value):
555                 "Initialize."
556                 self.name = name
557                 self.value = value
558                 if repository != None:
559                         repository.preferences.append(self)
560                 return self
561
562 class LabelSeparator:
563         "A class to add a label and menu separator."
564         def getFromRepository( self, repository ):
565                 "Initialize."
566                 return self
567
568 class IntSpin(FloatSpin):
569         "A class to display, read & write an int in a spin box."
570         def getSingleIncrementFromValue( self, from_, name, repository, to, value ):
571                 "Initialize."
572                 self.name = name
573                 self.value = value
574                 repository.preferences.append(self)
575                 return self
576
577         def setValueToString(self, value):
578                 self.value = int(value)
579
580 ##########################
581 # Helper classes
582 ##########################
583
584 class LayerCount:
585         'A class to handle the layerIndex.'
586         def __init__(self):
587                 'Initialize.'
588                 self.layerIndex = -1
589
590         def __repr__(self):
591                 'Get the string representation of this LayerCount.'
592                 return str(self.layerIndex)
593
594         def printProgressIncrement(self, procedureName):
595                 'Print progress then increment layerIndex.'
596                 self.layerIndex += 1
597                 printProgress(self.layerIndex, procedureName)
598