chiark / gitweb /
plugins: Support user configuration of default values
[cura.git] / Cura / util / validators.py
1 """
2 Setting validators.
3 These are the validators for various profile settings, each validator can be attached to a setting.
4 The validators can be queried to see if the setting is valid.
5 There are 3 possible outcomes:
6         Valid   - No problems found
7         Warning - The value is valid, but not recommended
8         Error   - The value is not a proper number, out of range, or some other way wrong.
9 """
10 from __future__ import division
11 __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
12
13 import types
14 import math
15
16 SUCCESS = 0
17 WARNING = 1
18 ERROR   = 2
19
20 class validFloat(object):
21         """
22         Checks if the given value in the setting is a valid float. An invalid float is an error condition.
23         And supports a minimum and/or maximum value. The min/max values are error conditions.
24         If the value == min or max then this is also an error.
25         """
26         def __init__(self, setting, minValue = None, maxValue = None):
27                 self.setting = setting
28                 self.setting._validators.append(self)
29                 self.minValue = minValue
30                 self.maxValue = maxValue
31         
32         def validate(self):
33                 try:
34                         f = float(eval(self.setting.getValue().replace(',','.'), {}, {}))
35                         if self.minValue is not None and f < self.minValue:
36                                 return ERROR, 'This setting should not be below ' + str(round(self.minValue, 3))
37                         if self.maxValue is not None and f > self.maxValue:
38                                 return ERROR, 'This setting should not be above ' + str(self.maxValue)
39                         return SUCCESS, ''
40                 except (ValueError, SyntaxError, TypeError, NameError):
41                         return ERROR, '"' + str(self.setting.getValue()) + '" is not a valid number or expression'
42
43 class validInt(object):
44         """
45         Checks if the given value in the setting is a valid integer. An invalid integer is an error condition.
46         And supports a minimum and/or maximum value. The min/max values are error conditions.
47         If the value == min or max then this is also an error.
48         """
49         def __init__(self, setting, minValue = None, maxValue = None):
50                 self.setting = setting
51                 self.setting._validators.append(self)
52                 self.minValue = minValue
53                 self.maxValue = maxValue
54         
55         def validate(self):
56                 try:
57                         f = int(eval(self.setting.getValue(), {}, {}))
58                         if self.minValue is not None and f < self.minValue:
59                                 return ERROR, 'This setting should not be below ' + str(self.minValue)
60                         if self.maxValue is not None and f > self.maxValue:
61                                 return ERROR, 'This setting should not be above ' + str(self.maxValue)
62                         return SUCCESS, ''
63                 except (ValueError, SyntaxError, TypeError, NameError):
64                         return ERROR, '"' + str(self.setting.getValue()) + '" is not a valid whole number or expression'
65
66 class warningAbove(object):
67         """
68         A validator to give off a warning if a value is equal or above a certain value.
69         """
70         def __init__(self, setting, minValueForWarning, warningMessage):
71                 self.setting = setting
72                 self.setting._validators.append(self)
73                 self.minValueForWarning = minValueForWarning
74                 self.warningMessage = warningMessage
75         
76         def validate(self):
77                 try:
78                         f = float(eval(self.setting.getValue().replace(',','.'), {}, {}))
79                         if isinstance(self.minValueForWarning, types.FunctionType):
80                                 if f >= self.minValueForWarning():
81                                         return WARNING, self.warningMessage % (self.minValueForWarning())
82                         else:
83                                 if f >= self.minValueForWarning:
84                                         return WARNING, self.warningMessage
85                         return SUCCESS, ''
86                 except (ValueError, SyntaxError, TypeError):
87                         #We already have an error by the int/float validator in this case.
88                         return SUCCESS, ''
89
90 class warningBelow(object):
91         """
92         A validator to give off a warning if a value is equal or below a certain value.
93         """
94         def __init__(self, setting, minValueForWarning, warningMessage):
95                 self.setting = setting
96                 self.setting._validators.append(self)
97                 self.minValueForWarning = minValueForWarning
98                 self.warningMessage = warningMessage
99
100         def validate(self):
101                 try:
102                         f = float(eval(self.setting.getValue().replace(',','.'), {}, {}))
103                         if isinstance(self.minValueForWarning, types.FunctionType):
104                                 if f <= self.minValueForWarning():
105                                         return WARNING, self.warningMessage % (self.minValueForWarning())
106                         else:
107                                 if f <= self.minValueForWarning:
108                                         return WARNING, self.warningMessage
109                         return SUCCESS, ''
110                 except (ValueError, SyntaxError, TypeError):
111                         #We already have an error by the int/float validator in this case.
112                         return SUCCESS, ''
113
114 class wallThicknessValidator(object):
115         """
116         Special wall-thickness validator. The wall thickness is used to calculate the amount of shells and the thickness of the shells.
117         But, on certain conditions the resulting wall-thickness is not really suitable for printing. The range in which this can happen is small.
118         But better warn for it.
119         """
120         def __init__(self, setting):
121                 self.setting = setting
122                 self.setting._validators.append(self)
123         
124         def validate(self):
125                 from Cura.util import profile
126                 try:
127                         wallThickness = profile.getProfileSettingFloat('wall_thickness')
128                         nozzleSize = profile.getProfileSettingFloat('nozzle_size')
129                         if wallThickness < 0.01:
130                                 return SUCCESS, ''
131                         if wallThickness <= nozzleSize * 0.5:
132                                 return ERROR, 'Trying to print walls thinner then the half of your nozzle size, this will not produce anything usable'
133                         if wallThickness <= nozzleSize * 0.85:
134                                 return WARNING, 'Trying to print walls thinner then the 0.8 * nozzle size. Small chance that this will produce usable results'
135                         if wallThickness < nozzleSize:
136                                 return SUCCESS, ''
137                         if nozzleSize <= 0:
138                                 return ERROR, 'Incorrect nozzle size'
139                         
140                         lineCount = int(wallThickness / nozzleSize)
141                         lineWidth = wallThickness / lineCount
142                         lineWidthAlt = wallThickness / (lineCount + 1)
143                         if lineWidth >= nozzleSize * 1.5 and lineWidthAlt <= nozzleSize * 0.85:
144                                 return WARNING, 'Current selected shell thickness results in a line thickness of ' + str(lineWidthAlt) + 'mm which is not recommended with your nozzle of ' + str(nozzleSize) + 'mm'
145                         if abs((lineCount * nozzleSize) - wallThickness) > 0.01 and abs(((lineCount + 1) * nozzleSize) - wallThickness) > 0.01:
146                                 return WARNING, 'Currently selected shell thickness is not a multiple of the nozzle size. While this prints fine, it does not give optimal results.'
147                         return SUCCESS, ''
148                 except ValueError:
149                         #We already have an error by the int/float validator in this case.
150                         return SUCCESS, ''
151
152 class printSpeedValidator(object):
153         """
154         Validate the printing speed by checking for a certain amount of volume per second.
155         This is based on the fact that you can push 10mm3 per second trough an UM-Origonal nozzle.
156         TODO: Update this code so it works better for different machine times with other feeders.
157         """
158         def __init__(self, setting):
159                 self.setting = setting
160                 self.setting._validators.append(self)
161
162         def validate(self):
163                 from Cura.util import profile
164                 try:
165                         nozzleSize = profile.getProfileSettingFloat('nozzle_size')
166                         layerHeight = profile.getProfileSettingFloat('layer_height')
167                         raw_user_input = self.setting.getValue()
168                         new_print_speed = raw_user_input.replace(',', '.')
169                         try:
170                                 evaluated = eval(new_print_speed, {}, {}) #Converts unicode into numeric
171                         except SyntaxError: #Caused by unicode string being empty
172                                 evaluated = 0.0
173                         printSpeed = float(evaluated)
174                         if printSpeed == 0.0:
175                                 printSpeed = profile.getProfileSettingFloat('print_speed')
176                         
177                         printVolumePerMM = layerHeight * nozzleSize
178                         printVolumePerSecond = printVolumePerMM * printSpeed
179                         #Using 8mm3 per second with a 0.4mm nozzle
180                         maxPrintVolumePerSecond = 8 / (math.pi*(0.2*0.2)) * (math.pi*(nozzleSize/2*nozzleSize/2))
181                         
182                         if printVolumePerSecond > maxPrintVolumePerSecond:
183                                 return WARNING, 'You are trying to print more then %.1fmm^3 of filament per second. This might cause filament slipping. (You are printing at %0.1fmm^3 per second)' % (maxPrintVolumePerSecond, printVolumePerSecond)
184                         
185                         return SUCCESS, 'You are printing at %0.1fmm^3 per second' % (printVolumePerSecond)
186                 except ValueError:
187                         #We already have an error by the int/float validator in this case.
188                         return SUCCESS, ''