- fixed lib/Makefile.am for "components" directory
[enigma2.git] / components.py
1 from enigma import *
2 import time
3 import sys
4
5 # some helper classes first:
6 class HTMLComponent:
7         def produceHTML(self):
8                 return ""
9                 
10 class HTMLSkin:
11         order = ()
12
13         def __init__(self, order):
14                 self.order = order
15
16         def produceHTML(self):
17                 res = "<html>\n"
18                 for name in self.order:
19                         res += self[name].produceHTML()
20                 res += "</html>\n";
21                 return res
22
23 class GUISkin:
24         def __init__(self):
25                 self.data = { }
26         
27         def createGUIScreen(self, parent):
28                 for (name, val) in self.items():
29                         self.data[name] = { }
30                         if isinstance(val, GUIComponent):
31                                 val.GUIcreate(self.data[name], parent, None)
32         
33         def deleteGUIScreen(self):
34                 for (name, val) in self.items():
35                         if isinstance(val, GUIComponent):
36                                 w = self.data[name]["instance"]
37                                 val.GUIdelete(self.data[name])
38                         try:
39                                 val.fix()
40                         except:
41                                 pass
42                         del self.data[name]
43                         
44                         # note: you'll probably run into this assert. if this happens, don't panic!
45                         # yes, it's evil. I told you that programming in python is just fun, and 
46                         # suddently, you have to care about things you don't even know.
47                         #
48                         # but calm down, the solution is easy, at least on paper:
49                         #
50                         # Each Component, which is a GUIComponent, owns references to each
51                         # instantiated eWidget (namely in screen.data[name]["instance"], in case
52                         # you care.)
53                         # on deleteGUIscreen, all eWidget *must* (!) be deleted (otherwise,
54                         # well, problems appear. I don't want to go into details too much,
55                         # but this would be a memory leak anyway.)
56                         # The assert beyond checks for that. It asserts that the corresponding
57                         # eWidget is about to be removed (i.e., that the refcount becomes 0 after
58                         # running deleteGUIscreen).
59                         # (You might wonder why the refcount is checked for 2 and not for 1 or 0 -
60                         # one reference is still hold by the local variable 'w', another one is
61                         # hold be the function argument to sys.getrefcount itself. So only if it's
62                         # 2 at this point, the object will be destroyed after leaving deleteGUIscreen.)
63                         #
64                         # Now, how to fix this problem? You're holding a reference somewhere. (References
65                         # can only be hold from Python, as eWidget itself isn't related to the c++
66                         # way of having refcounted objects. So it must be in python.)
67                         #
68                         # It could be possible that you're calling deleteGUIscreen trough a call of
69                         # a PSignal. For example, you could try to call screen.doClose() in response
70                         # to a Button::click. This will fail. (It wouldn't work anyway, as you would
71                         # remove a dialog while running it. It never worked - enigma1 just set a 
72                         # per-mainloop variable on eWidget::close() to leave the exec()...)
73                         # That's why Session supports delayed closes. Just call Session.close() and
74                         # it will work.
75                         #
76                         # Another reason is that you just stored the data["instance"] somewhere. or
77                         # added it into a notifier list and didn't removed it.
78                         #
79                         # If you can't help yourself, just ask me. I'll be glad to help you out.
80                         # Sorry for not keeping this code foolproof. I really wanted to archive
81                         # that, but here I failed miserably. All I could do was to add this assert.
82                         assert sys.getrefcount(w) == 2, "too many refs hold to " + str(w)
83         
84         def close(self):
85                 self.deleteGUIScreen()
86                 del self.data
87
88 # note: components can be used in multiple screens, so we have kind of
89 # two contexts: first the per-component one (self), then the per-screen (i.e.:
90 # per eWidget one), called "priv". In "priv", for example, the instance
91 # of the eWidget is stored.
92
93
94 # GUI components have a "notifier list" of associated eWidgets to one component
95 # (as said - one component instance can be used at multiple screens)
96 class GUIComponent:
97         """ GUI component """
98
99         def __init__(self):
100                 self.notifier = [ ]
101         
102         def GUIcreate(self, priv, parent, skindata):
103                 i = self.GUIcreateInstance(self, parent, skindata)
104                 priv["instance"] = i
105                 self.notifier.append(i)
106                 try:
107                         self.notifierAdded(i)
108                 except:
109                         pass
110         
111         # GUIdelete must delete *all* references to the current component!
112         def GUIdelete(self, priv):
113                 g = priv["instance"]
114                 self.notifier.remove(g)
115                 self.GUIdeleteInstance(g)
116                 del priv["instance"]
117
118         def GUIdeleteInstance(self, priv):
119                 pass
120
121 class VariableText:
122         """VariableText can be used for components which have a variable text, based on any widget with setText call"""
123         
124         def __init__(self):
125                 self.message = ""
126         
127         def notifierAdded(self, notifier):
128                 notifier.setText(self.message)
129
130         def setText(self, text):
131                 if self.message != text:
132                         self.message = text
133                         for x in self.notifier:
134                                 x.setText(self.message)
135
136         def getText(self):
137                 return self.message
138
139 class VariableValue:
140         """VariableValue can be used for components which have a variable value (like eSlider), based on any widget with setValue call"""
141         
142         def __init__(self):
143                 self.value = 0
144         
145         def notifierAdded(self, notifier):
146                 notifier.setValue(self.value)
147
148         def setValue(self, value):
149                 if self.value != value:
150                         self.value = value
151                         for x in self.notifier:
152                                 x.setValue(self.value)
153
154         def getValue(self):
155                 return self.value
156
157 # now some "real" components:
158
159 class Clock(HTMLComponent, GUIComponent, VariableText):
160         def __init__(self):
161                 VariableText.__init__(self)
162                 GUIComponent.__init__(self)
163                 self.doClock()
164                 
165                 self.clockTimer = eTimer()
166                 self.clockTimer.timeout.get().append(self.doClock)
167                 self.clockTimer.start(1000)
168
169 # "funktionalitaet"     
170         def doClock(self):
171                 self.setText("clock: " + time.asctime())
172
173 # realisierung als GUI
174         def GUIcreateInstance(self, priv, parent, skindata):
175                 g = eLabel(parent)
176                 return g
177
178 # ...und als HTML:
179         def produceHTML(self):
180                 return self.getText()
181
182 class Button(HTMLComponent, GUIComponent, VariableText):
183         def __init__(self, text="", onClick = [ ]):
184                 GUIComponent.__init__(self)
185                 VariableText.__init__(self)
186                 self.setText(text)
187                 self.onClick = onClick
188         
189         def push(self):
190                 for x in self.onClick:
191                         x()
192                 return 0
193         
194         def disable(self):
195                 pass
196         
197         def enable(self):
198                 pass
199
200 # html:
201         def produceHTML(self):
202                 return "<input type=\"submit\" text=\"" + self.getText() + "\">\n"
203
204 # GUI:
205         def GUIcreateInstance(self, priv, parent, skindata):
206                 g = eButton(parent)
207                 g.selected.get().append(self.push)
208                 return g
209         
210         def GUIdeleteInstance(self, g):
211                 g.selected.get().remove(self.push)
212
213 class Label(HTMLComponent, GUIComponent, VariableText):
214         def __init__(self, text=""):
215                 GUIComponent.__init__(self)
216                 VariableText.__init__(self)
217                 self.setText(text)
218         
219 # html: 
220         def produceHTML(self):
221                 return self.getText()
222
223 # GUI:
224         def GUIcreateInstance(self, priv, parent, skindata):
225                 g = eLabel(parent)
226                 return g
227
228 class Header(HTMLComponent, GUIComponent, VariableText):
229
230         def __init__(self, message):
231                 GUIComponent.__init__(self)
232                 VariableText.__init__(self)
233                 self.setText(message)
234         
235         def produceHTML(self):
236                 return "<h2>" + self.getText() + "</h2>\n"
237
238         def GUIcreateInstance(self, priv, parent, skindata):
239                 g = eLabel(parent)
240                 g.setText(self.message)
241                 return g
242
243 class VolumeBar(HTMLComponent, GUIComponent, VariableValue):
244         
245         def __init__(self):
246                 GUIComponent.__init__(self)
247                 VariableValue.__init__(self)
248
249         def GUIcreateInstance(self, priv, parent, skindata):
250                 g = eSlider(parent)
251                 g.setRange(0, 100)
252                 return g
253
254 # a general purpose progress bar
255 class ProgressBar(HTMLComponent, GUIComponent, VariableValue):
256         def __init__(self):
257                 GUIComponent.__init__(self)
258                 VariableValue.__init__(self)
259
260         def GUIcreateInstance(self, priv, parent, skindata):
261                 g = eSlider(parent)
262                 g.setRange(0, 100)
263                 return g
264         
265 class MenuList(HTMLComponent, GUIComponent):
266         def __init__(self, list):
267                 GUIComponent.__init__(self)
268                 self.l = eListboxPythonStringContent()
269                 self.l.setList(list)
270         
271         def getCurrent(self):
272                 return self.l.getCurrentSelection()
273         
274         def GUIcreateInstance(self, priv, parent, skindata):
275                 g = eListbox(parent)
276                 g.setContent(self.l)
277                 return g
278         
279         def GUIdeleteInstance(self, g):
280                 g.setContent(None)
281
282 class ServiceList(HTMLComponent, GUIComponent):
283         def __init__(self):
284                 GUIComponent.__init__(self)
285                 self.l = eListboxServiceContent()
286
287         def GUIcreateInstance(self, priv, parent, skindata):
288                 g = eListbox(parent)
289                 g.setContent(self.l)
290                 return g
291         
292         def GUIdeleteInstance(self, g):
293                 g.setContent(None)
294
295         def setRoot(self, root):
296                 self.l.setRoot(root)
297
298 class ServiceScan:
299         
300         Idle = 1
301         Running = 2
302         Done = 3
303         Error = 4
304                 
305         def scanStatusChanged(self):
306                 if self.state == self.Running:
307                         self.progressbar.setValue(self.scan.getProgress())
308                         if self.scan.isDone():
309                                 self.state = self.Done
310                         else:
311                                 self.text.setText("scan in progress - %d %% done!\n%d services found!" % (self.scan.getProgress(), self.scan.getNumServices()))
312                 
313                 if self.state == self.Done:
314                         self.text.setText("scan done!")
315                 
316                 if self.state == self.Error:
317                         self.text.setText("ERROR - failed to scan!")
318         
319         def __init__(self, progressbar, text):
320                 self.progressbar = progressbar
321                 self.text = text
322                 self.scan = eComponentScan()
323                 if self.scan.start():
324                         self.state = self.Error
325                 else:
326                         self.state = self.Running
327                 self.scan.statusChanged.get().append(self.scanStatusChanged)
328                 self.scanStatusChanged()
329
330         def isDone(self):
331                 return self.state == self.Done
332
333         def fix(self):
334                 self.scan.statusChanged.get().remove(self.scanStatusChanged)
335