- allow close of dialog
authorFelix Domke <tmbinc@elitedvb.net>
Sun, 23 Jan 2005 23:14:14 +0000 (23:14 +0000)
committerFelix Domke <tmbinc@elitedvb.net>
Sun, 23 Jan 2005 23:14:14 +0000 (23:14 +0000)
 - some eWidget fixes
 - background for eWidgetDesktop
 - introduce "session" object

components.py
lib/Makefile.am
lib/gui/ewidget.cpp
lib/gui/ewidgetdesktop.cpp
lib/gui/ewidgetdesktop.h
main/Makefile.am
main/enigma.cpp
mytest.py
screens.py

index 8ae4806..4c26c0a 100644 (file)
@@ -1,5 +1,6 @@
 from enigma import *
 import time
+import sys
 
 # some helper classes first:
 class HTMLComponent:
@@ -20,12 +21,72 @@ class HTMLSkin:
                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 """
 
@@ -38,6 +99,16 @@ class GUIComponent:
                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"""
@@ -107,19 +178,11 @@ class Button(HTMLComponent, GUIComponent, VariableText):
                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"
@@ -127,8 +190,11 @@ class Button(HTMLComponent, GUIComponent, VariableText):
 # 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):
 
@@ -155,4 +221,3 @@ class VolumeBar(HTMLComponent, GUIComponent, VariableValue):
                g = eSlider(parent)
                g.setRange(0, 100)
                return g
-
index f33133b..a09b9bc 100644 (file)
@@ -1 +1 @@
-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
index 3ebac35..7f08936 100644 (file)
@@ -10,7 +10,7 @@ eWidget::eWidget(eWidget *parent): m_parent(parent ? parent->child() : 0)
        
        if (m_parent)
                m_vis = wVisShow;
-       
+               
        if (m_parent)
        {
                m_parent->m_childs.push_back(this);
@@ -109,7 +109,17 @@ void eWidget::show()
 
 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;
@@ -137,9 +147,13 @@ void eWidget::destruct()
 
 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())
index bb6c7c3..1b5cf9b 100644 (file)
@@ -42,11 +42,10 @@ void eWidgetDesktop::calcWidgetClipRegion(eWidget *widget, gRegion &parent_visib
 
 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 &region)
@@ -56,12 +55,33 @@ void eWidgetDesktop::invalidate(const gRegion &region)
        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();
 }
 
index 1354a86..f76baf6 100644 (file)
@@ -13,7 +13,9 @@ class eWidgetDesktop: public Object
 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();
@@ -25,6 +27,8 @@ public:
        void paint();
        void setDC(gDC *dc);
        
+       void setBackgroundColor(gColor col);
+       
        void setRedrawTask(eMainloop &ml);
 private:
        ePtrList<eWidget> m_root;
index 46ce98b..e0e7e79 100644 (file)
@@ -9,7 +9,6 @@ enigma2_SOURCES = \
 
 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 \
index faff4d8..92982e5 100644 (file)
@@ -105,6 +105,7 @@ int main(int argc, char **argv)
        eWidgetDesktop dsk(eSize(720, 576));
        
        wdsk = &dsk;
+       dsk.setBackgroundColor(gColor(0));
        dsk.setDC(my_dc);
 #endif
 
index 0cbeac0..54f0f86 100644 (file)
--- a/mytest.py
+++ b/mytest.py
@@ -29,7 +29,6 @@ screens["global"](components)
 
 # test our screens
 components["$001"] = screens["testDialog"]()
-components["$002"] = screens["clockDisplay"](components["clock"])
 
 print "*** classes:"
 dump(screens)
@@ -55,24 +54,65 @@ class GUIOutputDevice(OutputDevice):
        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
index 7287898..b68f669 100644 (file)
@@ -1,4 +1,5 @@
 from components import *
+import sys
 
 # some screens
 def doGlobal(screen):
@@ -7,6 +8,9 @@ def doGlobal(screen):
 class Screen(dict, HTMLSkin, GUISkin):
        """ bla """
        
+       def close(self):
+               GUISkin.close(self)
+       
 # a test dialog
 class testDialog(Screen):
        def testDialogClick(self):
@@ -24,9 +28,11 @@ class testDialog(Screen):
 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 ]