No problem. It's not something I've looked at much before.
Dialogs created with instantiateDialog() don't necessarily want to have user input. For example, see the use of SubtitleLabel in Screens/InfoBarGenerics.py.
prl wrote: ↑Wed May 27, 2020 11:56
I don't have opkg installed on my Mac. I copy package source files to the test Beyonwiz, have it compile them by running the plugin, then copy the .pyo files back and use a copy of ipkg-build to make the package from those .pyo files. I have it all packaged into a Makefile, so it's not as inconvenient as it sounds.
I can't remember where I got ipkg-build. Perhaps from the opkg-utils plugin on the Beyonwiz. It's just a shell script.
https://www.linuxsat-support.com/thread/4957-how-to-create-ipk-file-on-enigma2-based-receivers/?postID=519252#post519252 wrote:Unfortunately, in most Enigma firmware, the "ar" tool is included in Busybox only as a very simple tool (with missing features). For example, in OpenPLi-7, Busybox is currently in v1.24.1. The lightweight version of "ar" in this Busybox does not support the file combining argument (ar -r as for example) .
Newer Busybox v1.29.2, which is already in OpenATV 6.3 firmware, already supports this argument (ar -r). However, it is still a lightweight "ar" version.
For this purpose it is possible to copy the alternative binary "ar" or the alternative complete newer Busybox (also as a binary, for a specific chipset). The busybox is located in the "/bin" directory and the "ar" tool is located in the "/usr/bin" directory. In both cases, as usually the sym-link link is used in most Enigma (the "ar" tool is called from Busybox and not directly as a binary file). I don't recommend overwriting "/bin/busybox*" files due to possible compatibility issues in some firmwares. Conversely, you can override the "/usr/bin/ar" tool (if you have the right binary file for your chipset!)
https://dream.reichholf.net/pydoc/html/d1/d90/classenigma_1_1iPlayableService__ENUMS.html wrote:evPause = _enigma.iPlayableService_ENUMS_evPause
evPlay = _enigma.iPlayableService_ENUMS_evPlay
evSeek = _enigma.iPlayableService_ENUMS_evSeek
statePlay = _enigma.iPlayableService_ENUMS_statePlay
statePause = _enigma.iPlayableService_ENUMS_statePause
Code: Select all
self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
{
iPlayableService.evUser: self.__timeUpdated,
iPlayableService.evUser+1: self.__statePlay,
iPlayableService.evUser+2: self.__statePause,
Code: Select all
/*
The following states exist:
- stop: data source closed, no playback
- pause: data source active, decoder paused
- play: data source active, decoder consuming
- decoder fast forward: data source linear, decoder drops frames
- trickmode, highspeed reverse: data source fast forwards / reverses, decoder just displays frames as fast as it can
- slow motion: decoder displays frames multiple times
*/
enum {
stateStop,
statePause,
statePlay,
stateDecoderFastForward,
stateTrickmode,
stateSlowMotion
};
Code: Select all
for c in self.onPlayStateChanged:
c(self.seekstate)
Thanks.prl wrote: ↑Fri Jul 31, 2020 15:50Of the events that you're interested in, eDVBServicePlay only sends evStart and evStopped. The only user event it sends is evUser+1 for "timeshift file changed".
An eDVBServicePlay's m_cue sends an eCueSheet.evtSeek on seeks, but that doesn't seem to be propagated through the eDVBServicePlay.
The relevant source files are service/iservice.h, service/servicedvb.{h,cpp} dvb/dvb.{h,cpp} & dvb/idvb.h.
Code: Select all
import os
from Tools import Notifications
from Screens.Screen import Screen
from Screens.MessageBox import MessageBox
from Plugins.Plugin import PluginDescriptor
class Daemon(Screen):
def __init__(self, session):
try:
Screen.__init__(self, session)
var+=1 # raise exception
except Exception as e:
Notifications.AddPopup(text=_("Unable to launch Daemon.\nReason: %s" %e),
type=MessageBox.TYPE_ERROR, timeout=20)
# this appears to work fine, although Screen.__init__() must come first, otherwise
# enigma says "RuntimeError: modal open are allowed only from a screen which is modal!"
class GUI(Screen):
def __init__(self, session):
try:
Screen.__init__(self, session)
var+=1
except Exception as e:
Notifications.AddPopup(text=_("Unable to launch GUI.\nReason: %s" %e),
type=MessageBox.TYPE_ERROR, timeout=20)
# execution reaches here, and there are no errors in the log, but the notification
# doesn't appear on screen and the user is locked out of the screen (every button
# press just shows the "not allowed" button press icon, as if there is some invisible
# screen in front of everything)
# also tried
Notifications.AddNotification(MessageBox, _("Unable to launch GUI.\nReason: %s" %e),
type=MessageBox.TYPE_ERROR, timeout=20)
def startDaemon(session, **kwargs):
session.open(Daemon)
def startGUI(session, **kwargs):
session.open(GUI)
def Plugins(**kwargs):
DescriptorList = []
DescriptorList.append(
PluginDescriptor(
name="MyPlugin",
where = PluginDescriptor.WHERE_SESSIONSTART,
description=_("MyPlugin"),
fnc=startDaemon
)
)
DescriptorList.append(
PluginDescriptor(
name="MyPlugin",
where = [
PluginDescriptor.WHERE_PLUGINMENU,
PluginDescriptor.WHERE_EXTENSIONSMENU
],
description=_("MyPlugin"),
fnc=startGUI
)
)
return DescriptorList
sonicblue wrote: ↑Sun Aug 09, 2020 09:12Is this a safe assumption for all versions of Enigma or will some boxes behave differently and end up locking the user out of their box with an invisible Daemon screen on top of everything with no action map and no way of closing it (assuming that's what's happening with the GUI screen).
Code: Select all
import os
from Tools import Notifications
from Screens.Screen import Screen
from Screens.MessageBox import MessageBox
from Plugins.Plugin import PluginDescriptor
from enigma import eTimer
from Components.ActionMap import ActionMap
from Components.Sources.StaticText import StaticText
class GUI(Screen):
def __init__(self, session):
Screen.__init__(self, session)
self.skinName = ["Setup"]
self["actions"] = ActionMap(["SetupActions", "ColorActions", "MenuActions"],
{
"red": self.close, "cancel": self.close,
"green": self.hide,
}, -2)
self["key_red"] = StaticText(_("self.close()"))
self["key_green"] = StaticText(_("self.hide()"))
def startGUI(session, **kwargs):
session.open(GUI)
def Plugins(**kwargs):
DescriptorList = []
DescriptorList.append(
PluginDescriptor(
name="MyPlugin",
where = [
# PluginDescriptor.WHERE_SESSIONSTART, # doesn't show GUI
PluginDescriptor.WHERE_PLUGINMENU, # shows GUI
PluginDescriptor.WHERE_EXTENSIONSMENU # shows GUI
],
description=_("MyPlugin"),
fnc=startGUI
)
)
return DescriptorList
sonicblue wrote: ↑Mon Aug 10, 2020 07:32Code: Select all
def startDaemon(session, **kwargs): daemon = session.instantiateDialog(Daemon)
sonicblue wrote: ↑Sun Aug 09, 2020 20:06
No. In that case, what should I do? I need it to be a Screen, but I don't want the screen to appear, because if it did, it doesn't have a skin or actionmap, causing the user to be locked out of their box with an invisible screen on top of everything in the case that Enigma did show it. It seems Engima deliberately doesn't display skinless screens if called from WHERE_SESSIONSTART, but I don't like not knowing why my code works. I'll probably just associate an action map with it to be safe, unless you know of a better way? eg. calling self.close() or self.hide() at the end of Daemon's layoutfinished? Or will self.close() somehow invalidate the screen, causing failure of other modules that rely on it being a screen (i.e ServiceEventTracker)?
sonicblue wrote: ↑Mon Aug 10, 2020 10:24sonicblue wrote: ↑Mon Aug 10, 2020 07:32Code: Select all
def startDaemon(session, **kwargs): daemon = session.instantiateDialog(Daemon)
It seems the issue is that daemon is a local variable pointing to the Screen object, so enigma must be losing a reference to it when the function ends. If I make daemon a global, the problem goes away and everything works as expected, except ...
Code: Select all
_myPluginMain = None
...
PluginDescriptor(
name="MyPlugin",
where=PluginDescriptor.WHERE_SESSIONSTART,
description=_("My Great Plugin"),
needsRestart=False,
fnc=myPluginMain
),
...
def myPluginMain(reason, session, **kwargs):
global _myPluginMain
if reason == 0: # Starting
if not _myPluginMain:
_myPluginMain = MyPlugin(session)
elif reason == 1: # Shutting down
if _myPluginMain:
_myPluginMain.stopTimers()
_myPluginMain = None
Code: Select all
import os
import traceback
from datetime import datetime
from time import time, strftime
from Tools import Notifications
from Screens.Screen import Screen
from Screens.MessageBox import MessageBox
from Plugins.Plugin import PluginDescriptor
from enigma import eTimer, iPlayableService, iServiceInformation
from enigma import ePoint
from Components.ServiceEventTracker import ServiceEventTracker
from Components.ActionMap import ActionMap
from Components.Sources.StaticText import StaticText
class Daemon(Screen):
instance = None
def __init__(self, session):
Daemon.instance = self # deleting this causes Daemon.Success()
# to not arrive due to garbage collection
# just a green square to tell if the screen got shown
self.skin = """<screen name="Daemon" position="100,100" size="200,200" flags="wfNoBorder" backgroundColor="#c000ff00" zPosition="11" ></screen>"""
Screen.__init__(self, session)
self.timer = eTimer()
self.timer.callback.append(self.Success)
self.timer.start(3000, True)
print "Daemon.__init__() finished"
def Success(self):
print "Daemon.Success()"
def startDaemon(session, **kwargs):
daemon = session.instantiateDialog(Daemon)
daemon.show()
session.execDialog(daemon) # removing this causes daemon.show() to work
daemon.show()
def Plugins(**kwargs):
DescriptorList = []
DescriptorList.append(
PluginDescriptor(
name="Daemon",
where = PluginDescriptor.WHERE_SESSIONSTART,
description=_("Daemon"),
fnc=startDaemon,
needsRestart=True
)
)
return DescriptorList
sonicblue wrote: ↑Tue Aug 11, 2020 03:38The only unresolved mystery I'm trying to solve is why session.execDialog() causes Screen.show() to fail. Here is a code example.
Code: Select all
session.execDialog(daemon) # removing this causes daemon.show() to work
prl wrote: ↑Tue Aug 11, 2020 10:48That's hardly more difficult than using ServiceEventTracker. You just need to make a similar dict to what you already use for ServiceEventTracker, mapping the events you're interested in to functions/methods to call, and call the indexed event method for the events in the map.
sonicblue wrote: ↑Wed Aug 12, 2020 11:32
Because there's no documentation and that German forum post was saying to do it. I have no idea what any of those exec functions in mytest.py actually do. I don't know what it means when a screen is "execing". It seems we need it to be execing to enable its Action Map though, as per your example.
prl wrote: ↑Tue Aug 11, 2020 10:48Why are you calling session.execDialog(daemon)? If you don't want daemon to be interactive, don't do anything more than daemon.show(). If you do want daemon to be interactive, call daemon.execBegin() after the show, as I did in the working example code I posted earlier in the topic.
Code: Select all
def autostart(session):
AutoVideoMode(session)
class AutoVideoMode(Screen):
def __init__(self, session):
Screen.__init__(self, session)
self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
{
iPlayableService.evStart: self.__evStart,
iPlayableService.evVideoSizeChanged: self.VideoChanged,
iPlayableService.evVideoProgressiveChanged: self.VideoChanged,
iPlayableService.evVideoFramerateChanged: self.VideoChanged,
iPlayableService.evBuffering: self.BufferInfo,
})
Code: Select all
class AutoVideoMode(object):
def __init__(self, session):
self.eventActions = ServiceEventTracker(screen=self, eventmap=
{
iPlayableService.evStart: self.__evStart,
iPlayableService.evVideoSizeChanged: self.VideoChanged,
iPlayableService.evVideoProgressiveChanged: self.VideoChanged,
iPlayableService.evVideoFramerateChanged: self.VideoChanged,
iPlayableService.evBuffering: self.BufferInfo,
})
session.nav.event.append(self.gotEvent)
def gotEvent(evt):
if evt in self.eventActions:
self.eventActions[evt](evt)
class ServiceEventTracker wrote: def __del_event(self):
EventMap = ServiceEventTracker.EventMap.setdefault
for x in self.__eventmap.iteritems():
EventMap(x[0], []).remove((self.__passall, self.__screen, x[1]))
Code: Select all
class ServiceEventTracker:
...
def __init__(self, screen, eventmap):
...
screen.onClose.append(self.__del_event)
def __del_event(self):