chiark / gitweb /
Merge remote-tracking branch 'upstream/master'
[cura.git] / Cura / gui / configBase.py
1 from __future__ import absolute_import
2 from __future__ import division
3
4 import platform
5 import wx, wx.lib.stattext, types
6 from wx.lib.agw import floatspin
7
8 from Cura.util import validators
9 from Cura.util import profile
10
11 class configPanelBase(wx.Panel):
12         "A base class for configuration dialogs. Handles creation of settings, and popups"
13         def __init__(self, parent):
14                 super(configPanelBase, self).__init__(parent)
15                 
16                 self.settingControlList = []
17                 
18                 #Create the popup window
19                 self.popup = wx.PopupWindow(self, flags=wx.BORDER_SIMPLE)
20                 self.popup.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_INFOBK))
21                 self.popup.setting = None
22                 self.popup.text = wx.StaticText(self.popup, -1, '');
23                 self.popup.sizer = wx.BoxSizer()
24                 self.popup.sizer.Add(self.popup.text, flag=wx.EXPAND|wx.ALL, border=1)
25                 self.popup.SetSizer(self.popup.sizer)
26         
27         def CreateConfigTab(self, nb, name):
28                 leftConfigPanel, rightConfigPanel, configPanel = self.CreateConfigPanel(nb)
29                 nb.AddPage(configPanel, name)
30                 return leftConfigPanel, rightConfigPanel
31         
32         def CreateConfigPanel(self, parent):
33                 configPanel = wx.Panel(parent);
34                 leftConfigPanel = wx.Panel(configPanel)
35                 rightConfigPanel = wx.Panel(configPanel)
36
37                 sizer = wx.GridBagSizer(2, 2)
38                 leftConfigPanel.SetSizer(sizer)
39                 sizer = wx.GridBagSizer(2, 2)
40                 rightConfigPanel.SetSizer(sizer)
41
42                 sizer = wx.BoxSizer(wx.HORIZONTAL)
43                 configPanel.SetSizer(sizer)
44                 sizer.Add(leftConfigPanel, border=35, flag=wx.RIGHT)
45                 sizer.Add(rightConfigPanel)
46                 leftConfigPanel.main = self
47                 rightConfigPanel.main = self
48                 return leftConfigPanel, rightConfigPanel, configPanel
49
50         def CreateDynamicConfigTab(self, nb, name):
51                 configPanel = wx.lib.scrolledpanel.ScrolledPanel(nb)    
52                 #configPanel = wx.Panel(nb);
53                 leftConfigPanel = wx.Panel(configPanel)
54                 rightConfigPanel = wx.Panel(configPanel)
55
56                 sizer = wx.GridBagSizer(2, 2)
57                 leftConfigPanel.SetSizer(sizer)
58                 sizer.AddGrowableCol(1)
59
60                 sizer = wx.GridBagSizer(2, 2)
61                 rightConfigPanel.SetSizer(sizer)
62                 sizer.AddGrowableCol(1)
63
64                 sizer = wx.BoxSizer(wx.HORIZONTAL)
65                 sizer.Add(leftConfigPanel, proportion=1, border=35, flag=wx.EXPAND)
66                 sizer.Add(rightConfigPanel, proportion=1, flag=wx.EXPAND)
67                 configPanel.SetSizer(sizer)
68
69                 configPanel.SetAutoLayout(1)
70                 configPanel.SetupScrolling(scroll_x=False, scroll_y=True)
71
72                 leftConfigPanel.main = self
73                 rightConfigPanel.main = self
74
75                 configPanel.leftPanel = leftConfigPanel
76                 configPanel.rightPanel = rightConfigPanel
77
78                 nb.AddPage(configPanel, name)
79
80                 return leftConfigPanel, rightConfigPanel, configPanel
81
82         def OnPopupDisplay(self, setting):
83                 self.popup.setting = setting
84                 self.UpdatePopup(setting)
85                 self.popup.Show(True)
86                 
87         def OnPopupHide(self, e):
88                 self.popup.Show(False)
89         
90         def UpdatePopup(self, setting):
91                 if self.popup.setting == setting:
92                         if setting.validationMsg != '':
93                                 self.popup.text.SetLabel(setting.validationMsg + '\n\n' + setting.helpText)
94                         else:
95                                 self.popup.text.SetLabel(setting.helpText)
96                         self.popup.text.Wrap(350)
97                         self.popup.Fit()
98                         x, y = setting.ctrl.ClientToScreenXY(0, 0)
99                         sx, sy = setting.ctrl.GetSizeTuple()
100                         #if platform.system() == "Windows":
101                         #       for some reason, under windows, the popup is relative to the main window... in some cases. (Wierd ass bug)
102                         #       wx, wy = self.ClientToScreenXY(0, 0)
103                         #       x -= wx
104                         #       y -= wy
105                         self.popup.SetPosition((x, y+sy))
106         
107         def updateProfileToControls(self):
108                 "Update the configuration wx controls to show the new configuration settings"
109                 for setting in self.settingControlList:
110                         if setting.type == 'profile':
111                                 setting.SetValue(profile.getProfileSetting(setting.configName))
112                         else:
113                                 setting.SetValue(profile.getPreference(setting.configName))
114                 self.Update()
115
116         def getLabelColumnWidth(self, panel):
117                 maxWidth = 0
118                 for child in panel.GetChildren():
119                         if isinstance(child, wx.lib.stattext.GenStaticText):
120                                 maxWidth = max(maxWidth, child.GetSize()[0])
121                 return maxWidth
122         
123         def setLabelColumnWidth(self, panel, width):
124                 for child in panel.GetChildren():
125                         if isinstance(child, wx.lib.stattext.GenStaticText):
126                                 size = child.GetSize()
127                                 size[0] = width
128                                 child.SetBestSize(size)
129         
130 class TitleRow():
131         def __init__(self, panel, name):
132                 "Add a title row to the configuration panel"
133                 sizer = panel.GetSizer()
134                 x = sizer.GetRows()
135                 self.title = wx.StaticText(panel, -1, name)
136                 self.title.SetFont(wx.Font(wx.SystemSettings.GetFont(wx.SYS_ANSI_VAR_FONT).GetPointSize(), wx.FONTFAMILY_DEFAULT, wx.NORMAL, wx.FONTWEIGHT_BOLD))
137                 sizer.Add(self.title, (x,0), (1,3), flag=wx.EXPAND|wx.TOP|wx.LEFT, border=10)
138                 sizer.Add(wx.StaticLine(panel), (x+1,0), (1,3), flag=wx.EXPAND|wx.LEFT,border=10)
139                 sizer.SetRows(x + 2)
140
141 class SettingRow():
142         def __init__(self, panel, label, configName, defaultValue = '', helpText = 'Help: TODO', type = 'profile'):
143                 "Add a setting to the configuration panel"
144                 sizer = panel.GetSizer()
145                 x = sizer.GetRows()
146                 y = 0
147                 flag = 0
148                 
149                 self.validators = []
150                 self.validationMsg = ''
151                 self.helpText = helpText
152                 self.configName = configName
153                 self.panel = panel
154                 self.type = type
155
156                 self.label = wx.lib.stattext.GenStaticText(panel, -1, label)
157                 self.label.Bind(wx.EVT_ENTER_WINDOW, self.OnMouseEnter)
158                 self.label.Bind(wx.EVT_LEAVE_WINDOW, self.OnMouseExit)
159
160                 getSettingFunc = profile.getPreference
161                 if self.type == 'profile':
162                         getSettingFunc = profile.getProfileSetting
163                 if isinstance(defaultValue, types.StringTypes):
164                         self.ctrl = wx.TextCtrl(panel, -1, getSettingFunc(configName))
165                         self.ctrl.Bind(wx.EVT_TEXT, self.OnSettingChange)
166                         flag = wx.EXPAND
167                 elif isinstance(defaultValue, types.FloatType):
168                         digits = 0
169                         while 1 / pow(10, digits) > defaultValue:
170                                 digits += 1
171                         self.ctrl = floatspin.FloatSpin(panel, -1, value=float(getSettingFunc(configName)), increment=defaultValue, digits=digits, min_val=0.0)
172                         self.ctrl.Bind(floatspin.EVT_FLOATSPIN, self.OnSettingChange)
173                         flag = wx.EXPAND
174                 elif isinstance(defaultValue, types.BooleanType):
175                         self.ctrl = wx.CheckBox(panel, -1, style=wx.ALIGN_RIGHT)
176                         self.SetValue(getSettingFunc(configName))
177                         self.ctrl.Bind(wx.EVT_CHECKBOX, self.OnSettingChange)
178                 elif isinstance(defaultValue, wx.Colour):
179                         self.ctrl = wx.ColourPickerCtrl(panel, -1)
180                         self.SetValue(getSettingFunc(configName))
181                         self.ctrl.Bind(wx.EVT_COLOURPICKER_CHANGED, self.OnSettingChange)
182                 else:
183                         self.ctrl = wx.ComboBox(panel, -1, getSettingFunc(configName), choices=defaultValue, style=wx.CB_DROPDOWN|wx.CB_READONLY)
184                         self.ctrl.Bind(wx.EVT_COMBOBOX, self.OnSettingChange)
185                         self.ctrl.Bind(wx.EVT_LEFT_DOWN, self.OnMouseExit)
186                         flag = wx.EXPAND
187
188                 # Set the minimum size of control to something other than the humungous default
189                 minSize = self.ctrl.GetMinSize()
190                 
191                 ##if platform.system() == "Darwin":
192                 ##      # Under MacOS, it appears that the minSize is used for the actual size, so give the field a bit more room...
193                 ##      minSize[0] = 150
194                 ##else:
195                 ##      minSize[0] = 50
196                 ##self.ctrl.SetMinSize(minSize)
197                 
198                 sizer.Add(self.label, (x,y), flag=wx.ALIGN_CENTER_VERTICAL|wx.LEFT,border=10)
199                 sizer.Add(self.ctrl, (x,y+1), flag=wx.ALIGN_BOTTOM|flag)
200                 sizer.SetRows(x+1)
201
202                 self.ctrl.Bind(wx.EVT_ENTER_WINDOW, self.OnMouseEnter)
203                 self.ctrl.Bind(wx.EVT_LEAVE_WINDOW, self.OnMouseExit)
204                 if isinstance(self.ctrl, floatspin.FloatSpin):
205                         self.ctrl.GetTextCtrl().Bind(wx.EVT_ENTER_WINDOW, self.OnMouseEnter)
206                         self.ctrl.GetTextCtrl().Bind(wx.EVT_LEAVE_WINDOW, self.OnMouseExit)
207                         self.defaultBGColour = self.ctrl.GetTextCtrl().GetBackgroundColour()
208                 else:
209                         self.defaultBGColour = self.ctrl.GetBackgroundColour()
210                 
211                 panel.main.settingControlList.append(self)
212
213         def OnMouseEnter(self, e):
214                 self.panel.main.OnPopupDisplay(self)
215
216         def OnMouseExit(self, e):
217                 self.panel.main.OnPopupHide(self)
218                 e.Skip()
219
220         def OnSettingChange(self, e):
221                 if self.type == 'profile':
222                         profile.putProfileSetting(self.configName, self.GetValue())
223                 else:
224                         profile.putPreference(self.configName, self.GetValue())
225                 result = validators.SUCCESS
226                 msgs = []
227                 for validator in self.validators:
228                         res, err = validator.validate()
229                         if res == validators.ERROR:
230                                 result = res
231                         elif res == validators.WARNING and result != validators.ERROR:
232                                 result = res
233                         if res != validators.SUCCESS:
234                                 msgs.append(err)
235                 ctrl = self.ctrl
236                 if isinstance(ctrl, floatspin.FloatSpin):
237                         ctrl = ctrl.GetTextCtrl()
238                 if result == validators.ERROR:
239                         ctrl.SetBackgroundColour('Red')
240                 elif result == validators.WARNING:
241                         ctrl.SetBackgroundColour('Yellow')
242                 else:
243                         ctrl.SetBackgroundColour(self.defaultBGColour)
244                 ctrl.Refresh()
245
246                 self.validationMsg = '\n'.join(msgs)
247                 self.panel.main.UpdatePopup(self)
248
249         def GetValue(self):
250                 if isinstance(self.ctrl, wx.ColourPickerCtrl):
251                         return str(self.ctrl.GetColour().GetAsString(wx.C2S_HTML_SYNTAX))
252                 else:
253                         return str(self.ctrl.GetValue())
254
255         def SetValue(self, value):
256                 if isinstance(self.ctrl, wx.CheckBox):
257                         self.ctrl.SetValue(str(value) == "True")
258                 elif isinstance(self.ctrl, wx.ColourPickerCtrl):
259                         self.ctrl.SetColour(value)
260                 elif isinstance(self.ctrl, floatspin.FloatSpin):
261                         try:
262                                 self.ctrl.SetValue(float(value))
263                         except ValueError:
264                                 pass
265                 else:
266                         self.ctrl.SetValue(value)
267
268 #Settings notify works as a validator, but instead of validating anything, it calls another function, which can use the value.
269 # A bit hacky, bit it works.
270 class settingNotify():
271         def __init__(self, setting, func):
272                 self.setting = setting
273                 self.setting.validators.append(self)
274                 self.func = func
275         
276         def validate(self):
277                 self.func()
278                 return validators.SUCCESS, ''
279