from enigma import *
import time
+import sys
# some helper classes first:
class HTMLComponent:
return res
class GUISkin:
- data = { }
+ def __init__(self):
+ self.data = { }
+
def createGUIScreen(self, parent):
for (name, val) in self.items():
self.data[name] = { }
val.GUIcreate(self.data[name], parent, None)
+
+ def deleteGUIScreen(self):
+ for (name, val) in self.items():
+ w = self.data[name]["instance"]
+ val.GUIdelete(self.data[name])
+ del self.data[name]
+
+ # note: you'll probably run into this assert. if this happens, don't panic!
+ # yes, it's evil. I told you that programming in python is just fun, and
+ # suddently, you have to care about things you don't even know.
+ #
+ # but calm down, the solution is easy, at least on paper:
+ #
+ # Each Component, which is a GUIComponent, owns references to each
+ # instantiated eWidget (namely in screen.data[name]["instance"], in case
+ # you care.)
+ # on deleteGUIscreen, all eWidget *must* (!) be deleted (otherwise,
+ # well, problems appear. I don't want to go into details too much,
+ # but this would be a memory leak anyway.)
+ # The assert beyond checks for that. It asserts that the corresponding
+ # eWidget is about to be removed (i.e., that the refcount becomes 0 after
+ # running deleteGUIscreen).
+ # (You might wonder why the refcount is checked for 2 and not for 1 or 0 -
+ # one reference is still hold by the local variable 'w', another one is
+ # hold be the function argument to sys.getrefcount itself. So only if it's
+ # 2 at this point, the object will be destroyed after leaving deleteGUIscreen.)
+ #
+ # Now, how to fix this problem? You're holding a reference somewhere. (References
+ # can only be hold from Python, as eWidget itself isn't related to the c++
+ # way of having refcounted objects. So it must be in python.)
+ #
+ # It could be possible that you're calling deleteGUIscreen trough a call of
+ # a PSignal. For example, you could try to call session.close() in response
+ # to a Button::click. This will fail. (It wouldn't work anyway, as you would
+ # remove a dialog while running it. It never worked - enigma1 just set a
+ # per-mainloop variable on eWidget::close() to leave the exec()...)
+ # That's why Session supports delayed closes. Just call Session.close() and
+ # it will work.
+ #
+ # Another reason is that you just stored the data["instance"] somewhere. or
+ # added it into a notifier list and didn't removed it.
+ #
+ # If you can't help yourself, just ask me. I'll be glad to help you out.
+ # Sorry for not keeping this code foolproof. I really wanted to archive
+ # that, but here I failed miserably. All I could do was to add this assert.
+ assert sys.getrefcount(w) == 2, "too many refs hold to " + str(w)
+
+ def close(self):
+ self.deleteGUIScreen()
+ del self.data
+
+# note: components can be used in multiple screens, so we have kind of
+# two contexts: first the per-component one (self), then the per-screen (i.e.:
+# per eWidget one), called "priv". In "priv", for example, the instance
+# of the eWidget is stored.
+
+# GUI components have a "notifier list" of associated eWidgets to one component
+# (as said - one component instance can be used at multiple screens)
class GUIComponent:
""" GUI component """
self.notifier.append(i)
if self.notifierAdded:
self.notifierAdded(i)
+
+ # GUIdelete must delete *all* references to the current component!
+ def GUIdelete(self, priv):
+ g = priv["instance"]
+ self.notifier.remove(g)
+ self.GUIdeleteInstance(g)
+ del priv["instance"]
+
+ def GUIdeleteInstance(self, priv):
+ pass
class VariableText:
"""VariableText can be used for components which have a variable text, based on any widget with setText call"""
self.setText(text)
self.onClick = [ ]
- def clicked(self):
+ def push(self):
for x in self.onClick:
x()
return 0
-
- def GUIcreate(self, priv, parent, skindata):
- GUIComponent.GUIcreate(self, priv,parent, skindata)
- priv["instance"].selected.get().append(self.clicked)
- def click(self):
- for x in self.onClick:
- x()
-
# html:
def produceHTML(self):
return "<input type=\"submit\" text=\"" + self.getText() + "\">\n"
# GUI:
def GUIcreateInstance(self, priv, parent, skindata):
g = eButton(parent)
-# g.clicked = [ self.click ]
+ g.selected.get().append(self.push)
return g
+
+ def GUIdeleteInstance(self, g):
+ g.selected.get().remove(self.push)
class Header(HTMLComponent, GUIComponent, VariableText):
g = eSlider(parent)
g.setRange(0, 100)
return g
-
-SUBDIRS = base dvb dvb_si gdi network service driver nav content gui python
+SUBDIRS = base dvb dvb_si gdi network service driver nav gui python
if (m_parent)
m_vis = wVisShow;
-
+
if (m_parent)
{
m_parent->m_childs.push_back(this);
void eWidget::hide()
{
+ /* TODO: when hiding an upper level widget, widgets get hidden but keep the */
+ /* wVisShow flag (because when the widget is shown again, the widgets must */
+ /* become visible again. */
+ if (!(m_vis & wVisShow))
+ return;
m_vis &= ~wVisShow;
+
+ /* this is a workaround to the above problem. when we are in the delete phase,
+ don't hide childs. */
+ if (!(m_parent || m_desktop))
+ return;
/* TODO: optimize here to only recalc what's required. possibly merge with show. */
eWidget *root = this;
eWidget::~eWidget()
{
+ hide();
+
if (m_parent)
m_parent->m_childs.remove(this);
+ m_parent = 0;
+
/* destroy all childs */
ePtrList<eWidget>::iterator i(m_childs.begin());
while (i != m_childs.end())
void eWidgetDesktop::recalcClipRegions()
{
- gRegion screen = gRegion(eRect(ePoint(0, 0), m_screen_size));
+ m_background_region = gRegion(eRect(ePoint(0, 0), m_screen_size));
for (ePtrList<eWidget>::iterator i(m_root.begin()); i != m_root.end(); ++i)
- calcWidgetClipRegion(*i, screen);
-// dumpRegion(screen);
+ calcWidgetClipRegion(*i, m_background_region);
}
void eWidgetDesktop::invalidate(const gRegion ®ion)
m_dirty_region |= region;
}
+void eWidgetDesktop::setBackgroundColor(gColor col)
+{
+ m_background_color = col;
+
+ /* if there's something visible from the background, redraw it with the new color. */
+ if (m_dc && m_background_region.valid() && !m_background_region.empty())
+ {
+ /* todo: split out "setBackgroundColor / clear"... maybe? */
+ gPainter painter(m_dc);
+ painter.resetClip(m_background_region);
+ painter.setBackgroundColor(m_background_color);
+ painter.clear();
+ }
+}
+
void eWidgetDesktop::paint()
{
gPainter painter(m_dc);
/* walk all root windows. */
for (ePtrList<eWidget>::iterator i(m_root.begin()); i != m_root.end(); ++i)
i->doPaint(painter, m_dirty_region);
+ m_dirty_region &= m_background_region;
+
+ painter.resetClip(m_dirty_region);
+ painter.setBackgroundColor(m_background_color);
+ painter.clear();
+
m_dirty_region = gRegion();
}
public: // weil debug
eSize m_screen_size;
gRegion m_dirty_region;
+ gRegion m_background_region;
ePtr<gDC> m_dc;
+ gColor m_background_color;
public:
eWidgetDesktop(eSize screen);
~eWidgetDesktop();
void paint();
void setDC(gDC *dc);
+ void setBackgroundColor(gColor col);
+
void setRedrawTask(eMainloop &ml);
private:
ePtrList<eWidget> m_root;
enigma2_LDADD_WHOLE = \
$(top_builddir)/lib/base/libenigma_base.a \
- $(top_builddir)/lib/content/libenigma_content.a \
$(top_builddir)/lib/driver/libenigma_driver.a \
$(top_builddir)/lib/dvb/libenigma_dvb.a \
$(top_builddir)/lib/dvb_si/libenigma_dvb_si.a \
eWidgetDesktop dsk(eSize(720, 576));
wdsk = &dsk;
+ dsk.setBackgroundColor(gColor(0));
dsk.setDC(my_dc);
#endif
# test our screens
components["$001"] = screens["testDialog"]()
-components["$002"] = screens["clockDisplay"](components["clock"])
print "*** classes:"
dump(screens)
def create(self, comp):
comp.createGUIScreen(self.parent)
-def runScreenTest():
- desktop = getDesktop()
-
- wnd = eWindow(desktop)
- mainwnd = wnd
- wnd.setTitle("Screen from python!")
- wnd.move(ePoint(300, 100))
- wnd.resize(eSize(300, 300))
+class Session:
+ def __init__(self):
+ self.desktop = None
+ self.delayTimer = eTimer()
+ self.delayTimer.timeout.get().append(self.processDelay)
+
+ def processDelay(self):
+ components[self.screenname].close()
+ if self.currentWindow != None:
+ self.currentWindow.hide()
+
+ del components[self.screenname]
+ del self.currentWindow
+
+
+ def open(self, screenname, screen):
+ components[screenname] = screen
+ self.screenname = screenname
+ screen.session = self
+
+ if self.desktop != None:
+ self.currentWindow = wnd = eWindow(self.desktop)
+ wnd.setTitle("Screen from python!")
+ wnd.move(ePoint(300, 100))
+ wnd.resize(eSize(300, 300))
+
+ gui = GUIOutputDevice()
+ gui.parent = wnd
+ gui.create(components["$002"])
+
+ applyGUIskin(components["$002"], None, "clockDialog")
+
+ wnd.show()
+ else:
+ self.currentWindow = None
+
+ def close(self):
+ self.delayTimer.start(0, 1)
- gui = GUIOutputDevice()
- gui.parent = wnd
- gui.create(components["$002"])
+def runScreenTest():
+ session = Session()
+ session.desktop = getDesktop()
+
+# components["$002"] = screens["clockDisplay"](components["clock"])
- applyGUIskin(components["$002"], None, "clockDialog")
+ session.open("$002", screens["clockDisplay"](components["clock"]))
- wnd.show()
-# components["$002"].data["okbutton"]["instance"].push()
+ def blub():
+# x = components["$002"]
+ components["$002"].data["okbutton"]["instance"].push()
+# dump(components)
+# print "session, close screen " + str(sys.getrefcount(x))
+# session.close()
+
+ tmr = eTimer()
+ tmr.timeout.get().append(blub)
+ tmr.start(4000, 1)
+
runMainloop()
return 0
from components import *
+import sys
# some screens
def doGlobal(screen):
class Screen(dict, HTMLSkin, GUISkin):
""" bla """
+ def close(self):
+ GUISkin.close(self)
+
# a test dialog
class testDialog(Screen):
def testDialogClick(self):
class clockDisplay(Screen):
def okbutton(self):
print "clockDisplay close"
+
+ self.session.close()
def __init__(self, clock):
- HTMLSkin.__init__(self, ("title", "theClock", "okbutton"))
+ GUISkin.__init__(self)
self["theClock"] = clock
b = Button("bye")
b.onClick = [ self.okbutton ]