Hello World

Moderators: Gully, peteru

sonicblue
Master
Posts: 247
Joined: Wed Oct 25, 2017 14:30

Re: Hello World

Post by sonicblue » Sun Jun 07, 2020 18:06

prl wrote:
Sun Jun 07, 2020 17:59
Also, if other parts of your plugin (like Plugins.Plugin.PluginDescriptors) want to refer to refer to your config.plugins.myplugin.enabled, it will need to be defined earlier than in the class's __init__(). Fr example as in a module global in your plugin.py, or in the plugin's __init__.py file.
Could I alternatively just put a get method in the class which contains my configlist?

I'm trying to write my plugin in 2 modules: GUI and Daemon. Daemon constantly runs in the background monitoring video mode and doing other stuff based on the config values, GUI is just an interface for changing the config values which then affect what the Daemon does.

prl
Wizard God
Posts: 32705
Joined: Tue Sep 04, 2007 13:49
Location: Canberra; Black Mountain Tower transmitters

Re: Hello World

Post by prl » Sun Jun 07, 2020 18:11

sonicblue wrote:
Sun Jun 07, 2020 16:27
I'm currently learning the nightmare known as the config file :lol:

Is my current understanding correct that:

config.plugins.myplugin.enabled.value = True # setting is applied, but will be lost if the user reboots

True, unless someone else calls configfile.save().
sonicblue wrote:
Sun Jun 07, 2020 16:27
config.plugins.myplugin.enabled.save() # the setting will be written to file and survives a reboot, but only if user reboots gracefully i.e no A/C loss

Yes, though some other config screen might do a save if it's used and saved before a crash.
sonicblue wrote:
Sun Jun 07, 2020 16:27
configfile.save() # forces all system-wide config settings which have had save() called on them to be immediately written to file and will survive any type of reboot

ConfigListScreen provides a convenience method (self.saveAll() if your plugin class inherits from ConfigListScreen) that does a save() on all the config list's variables, and then does a configfile.save()

Note that when you create a config variable, you can give it a default value: e.g. ConfigBoolean(default=True). ConfigBoolean's default default value is False, i.e. ConfigBoolean(default=False).value == ConfigBoolean().value.
Peter
T4 HDMI
U4, T4, T3, T2, V2 test/development machines
Sony BDV-9200W HT system
LG OLED55C9PTA 55" OLED TV

prl
Wizard God
Posts: 32705
Joined: Tue Sep 04, 2007 13:49
Location: Canberra; Black Mountain Tower transmitters

Re: Hello World

Post by prl » Sun Jun 07, 2020 18:22

sonicblue wrote:
Sun Jun 07, 2020 18:06
prl wrote:
Sun Jun 07, 2020 17:59
Also, if other parts of your plugin (like Plugins.Plugin.PluginDescriptors) want to refer to refer to your config.plugins.myplugin.enabled, it will need to be defined earlier than in the class's __init__(). Fr example as in a module global in your plugin.py, or in the plugin's __init__.py file.
Could I alternatively just put a get method in the class which contains my configlist?

You could add one (an __get__() method might be more appropriate), but no matter what methods your class has, you can't access a config variable until someone has created it. I'm not sure even what your get method would be doing. Defining __get__() is moving into Python Dark Arts ;).

Config variables in plugins are usually defined in the body (global level) of plugin.py if that is the only Python file in the plugin, or in __init__.py, if there is more than one. __init__.py is run before any other part of the plugin so putting the config file in the global level in __init__.py ensures that the confog variable is available to all other parts of the plugin.
sonicblue wrote:
Sun Jun 07, 2020 18:06
I'm trying to write my plugin in 2 modules: GUI and Daemon. Daemon constantly runs in the background monitoring video mode and doing other stuff based on the config values, GUI is just an interface for changing the config values which then affect what the Daemon does.

Then you almost certainly need 3 modules, those two and __init__.py.

Note that deep down in the guts of enigma2, things are arranged so that isinstance(Screen, dict) is True. That's why you can do self["config"] on it. dict has a perfectly good implementation of __get__(). :)
Peter
T4 HDMI
U4, T4, T3, T2, V2 test/development machines
Sony BDV-9200W HT system
LG OLED55C9PTA 55" OLED TV

prl
Wizard God
Posts: 32705
Joined: Tue Sep 04, 2007 13:49
Location: Canberra; Black Mountain Tower transmitters

Re: Hello World

Post by prl » Sun Jun 07, 2020 18:38

If you want an example of a fairly simple DIY config screen for a plugin, you could look at Plugin/System/IceTV/pluin.py's IceTVNewUserSetup screen. The IceTV plugin also uses __init__.py .to define its config variables, and is also a daemon, though the daemon isn't in a separate module from the config, and you probably don't want to use twisted.internet classes to implement your daemon.
Peter
T4 HDMI
U4, T4, T3, T2, V2 test/development machines
Sony BDV-9200W HT system
LG OLED55C9PTA 55" OLED TV

sonicblue
Master
Posts: 247
Joined: Wed Oct 25, 2017 14:30

Re: Hello World

Post by sonicblue » Sun Jun 07, 2020 19:31

Thanks, I've been looking at IceTV quite a bit, trying to learn from it. It's slow going when everything's new :)
Another idea I had was to make some members of my daemon class static so they could be accessible from the GUI class.

prl
Wizard God
Posts: 32705
Joined: Tue Sep 04, 2007 13:49
Location: Canberra; Black Mountain Tower transmitters

Re: Hello World

Post by prl » Sun Jun 07, 2020 22:21

sonicblue wrote:
Sun Jun 07, 2020 19:31
Another idea I had was to make some members of my daemon class static so they could be accessible from the GUI class.

The usual thing to do for classes that have singleton instances is to have a class variable that points to the instance, and a static method that returns the instance (or None if there isn't one).

And remember that nothing will display for a Screen unless it has a skin.
Peter
T4 HDMI
U4, T4, T3, T2, V2 test/development machines
Sony BDV-9200W HT system
LG OLED55C9PTA 55" OLED TV

sonicblue
Master
Posts: 247
Joined: Wed Oct 25, 2017 14:30

Re: Hello World

Post by sonicblue » Tue Jun 09, 2020 13:42

In VideoMode.py there's this:

Code: Select all

service = self.session.nav.getCurrentService()
if not service:
	return
But is the above code safe? Without knowing what getCurrentService() is returning, is it possible that it will return nothing, in which case service will be uninitialized, in turn causing the if statement to crash the plugin since that's what I've observed happens when I try to perform an if statement on an uninitialized variable.

Also is there any documentation for eTimer? For example if two timers are calling the same function, and the first timer is half way through the function when the second timer fires, will it:

a) interrupt the function being called by the first timer, then when it finishes, hand back execution to the first timer's function instance
b) as above, but without handing execution back
c) wait for the first timer's function instance to finish, then call the function the second time

prl
Wizard God
Posts: 32705
Joined: Tue Sep 04, 2007 13:49
Location: Canberra; Black Mountain Tower transmitters

Re: Hello World

Post by prl » Tue Jun 09, 2020 14:29

sonicblue wrote:
Tue Jun 09, 2020 13:42
In VideoMode.py there's this:

Code: Select all

service = self.session.nav.getCurrentService()
if not service:
	return
But is the above code safe? Without knowing what getCurrentService() is returning, is it possible that it will return nothing, in which case service will be uninitialized, in turn causing the if statement to crash the plugin since that's what I've observed happens when I try to perform an if statement on an uninitialized variable.

service is guaranteed to be initialised before the if statement. Even Python functions/methods that don't appear to return anything will actually return something: the object instance that represents "nothing": None.

None evaluates to False in contexts where a bool is expected. Other useful conversions are: 0 converts to False, non-zero converts to True, empty tuples, lists, sets and dicts evaluate as False and non-empty ones evaluate as True.

So, for example, if len(myDict): can be simplified to if myDict:
sonicblue wrote:
Tue Jun 09, 2020 13:42
Also is there any documentation for eTimer?
lib/base/base.{h,cpp}
sonicblue wrote:
Tue Jun 09, 2020 13:42
For example if two timers are calling the same function, and the first timer is half way through the function when the second timer fires, will it:

a) interrupt the function being called by the first timer, then when it finishes, hand back execution to the first timer's function instance
b) as above, but without handing execution back
c) wait for the first timer's function instance to finish, then call the function the second time

Timer actions are synchronous with all other event dispatching (remote button presses, keyboard, socket data availability, etc), in enigma2. They are all dispatched from the main dispatch loop in enigma2.eApp

Only one eApp main dispatch loop event is ever active at a time, so c).

Timers that fire at the same time are processed in the order that they were started. Note that myTimer.start(0, True) is allowed. myTimer.start(0, False) or equivalently myTimer.start(0) are probably not things you want to do. The optional second argument to eTimer.start() makes the timer repeated at that interval if False, otherwise it's a one-shot.
Peter
T4 HDMI
U4, T4, T3, T2, V2 test/development machines
Sony BDV-9200W HT system
LG OLED55C9PTA 55" OLED TV

sonicblue
Master
Posts: 247
Joined: Wed Oct 25, 2017 14:30

Re: Hello World

Post by sonicblue » Tue Jun 09, 2020 17:07

Thank you!

sonicblue
Master
Posts: 247
Joined: Wed Oct 25, 2017 14:30

Re: Hello World

Post by sonicblue » Tue Jun 09, 2020 20:23

prl wrote:
Tue Jun 09, 2020 14:29
Even Python functions/methods that don't appear to return anything will actually return something: the object instance that represents "nothing": None.

In that case this appears to be unsafe

Code: Select all

VideoMode.py
video_height = int(info.getInfo(iServiceInformation.sVideoHeight))
video_width = int(info.getInfo(iServiceInformation.sVideoWidth))
video_framerate = int(info.getInfo(iServiceInformation.sFrameRate))
video_progressive = int(info.getInfo(iServiceInformation.sProgressive))

if not ((0 < video_height <= 2160) and (0 < video_width <= 4096)):  

If getInfo fails it might return None which can't be cast to int nor compared with ints.

Code: Select all

VideoMode.py
	def _fromProc(pathname, base=10):
		res = None
		if path.exists(pathname):
			f = open(pathname, "r")
			try:
				val = int(f.read(), base)
				if val >= 0:
					res = val
			except:
				pass
			f.close()
		return res

	video_height = _fromProc("/proc/stb/vmpeg/0/yres", 16)
	video_width = _fromProc("/proc/stb/vmpeg/0/xres", 16)
	video_framerate = _fromProc("/proc/stb/vmpeg/0/framerate")
	video_progressive = _fromProc("/proc/stb/vmpeg/0/progressive")

if not ((0 < video_height <= 2160) and (0 < video_width <= 4096)):  
	print "[VideoMode] Can not determine video characteristics from service or /proc - do nothing"
	return

If it fails to read from proc, None will be returned and again cannot be compared with ints.

I must say I am a bit surprised at the inflexibility of Python in this regard. I'm trying to do a simple thing like compare a var with a config value, but since I don't know if that config value exists because it's only present for certain chipsets, I must convert it to a separate var using try/except, and even excepting on NameError wasnt sufficient because config.value is a class member which produces some other exception.

User avatar
peteru
Uber Wizard
Posts: 9737
Joined: Tue Jun 12, 2007 23:06
Location: Sydney, Australia
Contact:

Re: Hello World

Post by peteru » Tue Jun 09, 2020 23:24

int iServiceInformation::getInfo(int w); will always return an int, so the code (besides being in poor style) is safe with respect to not having to handle a None value.

The result returned from _fromProc() should have the default value initialised to 0 rather than None.

"Beauty lies in the hands of the beer holder."
Blog.

prl
Wizard God
Posts: 32705
Joined: Tue Sep 04, 2007 13:49
Location: Canberra; Black Mountain Tower transmitters

Re: Hello World

Post by prl » Wed Jun 10, 2020 11:12

sonicblue wrote:
Tue Jun 09, 2020 20:23
I must say I am a bit surprised at the inflexibility of Python in this regard. I'm trying to do a simple thing like compare a var with a config value, but since I don't know if that config value exists because it's only present for certain chipsets, I must convert it to a separate var using try/except, and even excepting on NameError wasnt sufficient because config.value is a class member which produces some other exception.

Exception handling is not an unusual thing in Python at all. A lot of actions (like, say, opening a file) will throw an exception on failure, rather than, as in C & C++, just returning an error return.

In some ways it enforces good programming discipline: if you don't handle the exception, your code will die.

Sometimes catching the exception is simpler than testing, but if you want to test whether a config variable exists, you can always do something like hasattr(config.usage, "someConfVar"). That will also work for testing whether a subsection exists, like hasattr(config.plugins, "icetv").

Referencing a non-existent config variable or subsection should give you an AttributeError exception.
Peter
T4 HDMI
U4, T4, T3, T2, V2 test/development machines
Sony BDV-9200W HT system
LG OLED55C9PTA 55" OLED TV

sonicblue
Master
Posts: 247
Joined: Wed Oct 25, 2017 14:30

Re: Hello World

Post by sonicblue » Wed Jun 10, 2020 12:27

prl wrote:
Wed Jun 10, 2020 11:12
you can always do something like hasattr(config.usage, "someConfVar"). That will also work for testing whether a subsection exists, like hasattr(config.plugins, "icetv").

Thanks, that's much nicer. All the examples I found on Google involved about 4 lines of try/except which I found a bit bloated for such a basic evaluation of a config value. To be totally safe though it seems I'd also have to use eg. isinstance(var, int) otherwise I'm just assuming what that config value holds, which could change in a future software update. In the end it's only one extra statement which is not too bad :)

Regarding the aforementioned unsafe code, I noticed that a failure to get the stream resolution from /proc/stb/vmpeg/0/ does actually happen every now and then when zapping multiple times in quick succession, since between zaps there is no video stream for about 500ms during which the autores timer can fire. Luckily all the proc values are set to 0 behind the scenes so they still evaluate to false/int.

prl
Wizard God
Posts: 32705
Joined: Tue Sep 04, 2007 13:49
Location: Canberra; Black Mountain Tower transmitters

Re: Hello World

Post by prl » Wed Jun 10, 2020 14:32

sonicblue wrote:
Wed Jun 10, 2020 12:27
To be totally safe though it seems I'd also have to use eg. isinstance(var, int) otherwise I'm just assuming what that config value holds, which could change in a future software update. In the end it's only one extra statement which is not too bad :)
Pretty much all the code assumes that the type returned by each config variable class won't be changed. Personally I wouldn't bother with the test.
Peter
T4 HDMI
U4, T4, T3, T2, V2 test/development machines
Sony BDV-9200W HT system
LG OLED55C9PTA 55" OLED TV

sonicblue
Master
Posts: 247
Joined: Wed Oct 25, 2017 14:30

Re: Hello World

Post by sonicblue » Sun Jul 05, 2020 18:02

I'm having trouble understanding the interaction between config values and the GUI.

It seems that calling setValue() on a config value will update both the config value and the corresponding GUI element associated with the config value.

However I've noticed that doesn't seem to always be the case. For example if the user applies a display mode in [AV Settings] and then selects "no" in response to "is this mode ok?", the config values for (port, mode, rate) are set to their last good values using setValue(), but the GUI only gets updated for the first two and not the last one (rate). This causes the GUI and config values to become desynchronised, and if the user was to press OK at this point, they would be OK'ing different values to what is shown on screen.

I note that none of the mentioned config values has an addNotifier, nor is createSetup() called (the function which refreshes the GUI with the current config values when the user presses keyleft or keyright).

Steps to reproduce:

1. In [AV Settings] set (HDMI, 1080p, multi) and choose "yes" in response to "is this mode ok"
2. In [AV Settings] choose (YPbPr, 1080i, 50hz) and choose "no" in response to "is this mode ok"
3. Notice that in [AV Settings] the port and mode have been reverted back to HDMI 1080p, but rate is still left at 50hz.
4. Notice that adding debug lines shows the config value for rate is "multi" (or you can just close and re-open [AV Settings] to refresh its true value)

The code when choosing "no" is simply:
https://bitbucket.org/beyonwiz/easy-ui-4/src/master/lib/python/Screens/VideoMode.py wrote: if not confirmed:
config.av.videoport.setValue(self.last_good[0])
config.av.videomode[self.last_good[0]].setValue(self.last_good[1])
config.av.videorate[self.last_good[1]].setValue(self.last_good[2])
self.hw.setMode(*self.last_good)

I've added debug lines to make sure last_good really is (HDMI, 1080p, multi).

To workaround this I must call createSetup(), but I'm totally unsatisfied not knowing why this one value isn't refreshed but the others are.
Last edited by sonicblue on Sun Jul 05, 2020 23:05, edited 1 time in total.

Grumpy_Geoff
Uber Wizard
Posts: 6490
Joined: Thu Mar 05, 2009 22:54
Location: Perth

Re: Hello World

Post by Grumpy_Geoff » Sun Jul 05, 2020 20:32

sonicblue wrote:
Sun Jul 05, 2020 18:02
config.av.videorate[self.last_good[1]].setValue(self.last_good[2])

I know nowt about this, but shouldn't this be the first array/list element '0' not the second '1' -
config.av.videorate[self.last_good[0]].setValue(self.last_good[2])

prl
Wizard God
Posts: 32705
Joined: Tue Sep 04, 2007 13:49
Location: Canberra; Black Mountain Tower transmitters

Re: Hello World

Post by prl » Sun Jul 05, 2020 22:25

Also, config.x.y.value = newvalue is the preferred way to set config variables, rather than using setValue().
Peter
T4 HDMI
U4, T4, T3, T2, V2 test/development machines
Sony BDV-9200W HT system
LG OLED55C9PTA 55" OLED TV

sonicblue
Master
Posts: 247
Joined: Wed Oct 25, 2017 14:30

Re: Hello World

Post by sonicblue » Sun Jul 05, 2020 22:56

Grumpy_Geoff wrote:
Sun Jul 05, 2020 20:32
I know nowt about this, but shouldn't this be the first array/list element '0' not the second '1' -
config.av.videorate[self.last_good[0]].setValue(self.last_good[2])

I don't believe so, because config.av.videorate is a dictionary of rates indexed by mode not port (last_good[0] points to a port). Plus, as mentioned the config value really is "multi" as evidenced by adding debug lines or simply opening and closing the AV settings menu or cycling another value in AV Settings with keyleft or keyright which calls createSetup() and refreshes the GUI to reveal the true config value is actually "multi" even though the GUI was still showing the previous value.

Speculation: perhaps the GUI doesn't get properly updated because an enigma thread which repaints the GUI in response to setValue() occurs part way through the mode change (since the very next line is setMode()) and doesn't get a chance to properly update the GUI element?
Last edited by sonicblue on Mon Jul 06, 2020 13:31, edited 1 time in total.

sonicblue
Master
Posts: 247
Joined: Wed Oct 25, 2017 14:30

Re: Hello World

Post by sonicblue » Sun Jul 05, 2020 23:53

I commented out the setMode() and the issue persists, so it's not the display mode change causing it.

I tried putting confirm() in a separate timer that gives the GUI plenty of time to repaint after disconfirming the display mode change dialog - no effect.

New theory: perhaps config.x.y.setValue() doesn't actually update its GUI element, only the config value. The reason it partially updates the GUI is that after disconfirming the display mode change, there may be a small window of time between that dialog closing and the AV Settings GUI reopening, that one or two lines of code are getting a chance to execute and update the config values, before AV Settings GUI interrupts and re-paints, using whatever the config values happen to be at that time.

i.e

Code: Select all

if not confirmed:
	config.av.videoport.setValue(self.last_good[0])
	config.av.videomode[self.last_good[0]].setValue(self.last_good[1])
	
	# AV Settings GUI interrupts here and reads config values?
	
	config.av.videorate[self.last_good[1]].setValue(self.last_good[2])
	self.hw.setMode(*self.last_good)

sonicblue
Master
Posts: 247
Joined: Wed Oct 25, 2017 14:30

Re: Hello World

Post by sonicblue » Sat Jul 11, 2020 21:07

prl wrote:
Sun Jun 07, 2020 22:21
And remember that nothing will display for a Screen unless it has a skin.

Is it possible to use the default skin that the menus use, like this one:

Image

I quite like the look of it and was hoping I could just copy-paste that skin for my plugin instead of building it manually from scratch, but I can't seem to find where it's located as the system screens don't define a skin. Not all system screens have the same skin so I presume it must be defined somewhere...

Also where are all the skin assets stored on the box itself? I'd like to peruse all the different button icons and stuff.

User avatar
peteru
Uber Wizard
Posts: 9737
Joined: Tue Jun 12, 2007 23:06
Location: Sydney, Australia
Contact:

Re: Hello World

Post by peteru » Sat Jul 11, 2020 21:19

That's a special case for screens that are based on ConfigListScreen.

"Beauty lies in the hands of the beer holder."
Blog.

sonicblue
Master
Posts: 247
Joined: Wed Oct 25, 2017 14:30

Re: Hello World

Post by sonicblue » Sun Jul 12, 2020 01:29

peteru wrote:
Sat Jul 11, 2020 21:19
That's a special case for screens that are based on ConfigListScreen.

Is that not what we're doing when our GUI classes inherit ConfigListScreen and call its ConfigListScreen.__init__?

sonicblue
Master
Posts: 247
Joined: Wed Oct 25, 2017 14:30

Re: Hello World

Post by sonicblue » Sun Jul 12, 2020 01:36

Ah it seems we just need self.skinName = ["Setup"].

sonicblue
Master
Posts: 247
Joined: Wed Oct 25, 2017 14:30

Re: Hello World

Post by sonicblue » Sun Jul 12, 2020 01:40

[deleted - redundant]
Last edited by sonicblue on Thu Jul 16, 2020 23:55, edited 1 time in total.

sonicblue
Master
Posts: 247
Joined: Wed Oct 25, 2017 14:30

Re: Hello World

Post by sonicblue » Sun Jul 12, 2020 02:08

Am I correct in thinking that these built-in system setup skins can't be extended or have anything added to them? eg. a status label or some extra icon indicators and such.

prl
Wizard God
Posts: 32705
Joined: Tue Sep 04, 2007 13:49
Location: Canberra; Black Mountain Tower transmitters

Re: Hello World

Post by prl » Sun Jul 12, 2020 14:39

sonicblue wrote:
Sun Jul 12, 2020 02:08
Am I correct in thinking that these built-in system setup skins can't be extended or have anything added to them? eg. a status label or some extra icon indicators and such.

No, that's not correct. You add the widget in the code, like self["status"] = Components.Label() and then add the corresponding widget to the screen's skin: <widget name="status" position="..." size="..." etc, etc />.

The change needs to be made in all skins that use the image. In the case of the Beyonwizes, that's only three skins. In other enigma2 images, it can be dozens.

If the screen skin is shared by many classes, then you need to add the widget to all those classes, or make the widget conditional: <widget name="status" position="..." size="..." conditional="status" etc, etc /> (only display if the code instance has a widget called self["status"].

It might help people give advice if you say exactly what you want to achieve.
Peter
T4 HDMI
U4, T4, T3, T2, V2 test/development machines
Sony BDV-9200W HT system
LG OLED55C9PTA 55" OLED TV

sonicblue
Master
Posts: 247
Joined: Wed Oct 25, 2017 14:30

Re: Hello World

Post by sonicblue » Sun Jul 12, 2020 18:12

prl wrote:
Sun Jul 12, 2020 14:39
You add the widget in the code, like self["status"] = Components.Label() and then add the corresponding widget to the screen's skin: <widget name="status" position="..." size="..." etc, etc />.

But isn't that only in the case where we define our own skin as a class variable in our own class? Because, as soon as I add self.skinName = ["Setup"] to my class, enigma ignores my class variable skin, and instead uses the skin that self.skinName = ["Setup"] points to.

What I would like to do is set self.skinName = ["Setup"] in my class (which appears to tell enigma to use the default system skin for setup menus) and then I would like to extend that skin by adding some extra widgets to it.

An alternative idea I had was to find the location of the system setup skin, and copy the section from it where it defines what self.skinName = ["Setup"] points to, and paste that into my own class variable skin, and then I can remove the self.skinName = ["Setup"] from my class.

It appears to be defined here:
https://bitbucket.org/beyonwiz/easy-skin-aus-hd/src/master/usr/share/enigma2/easy-skin-aus-hd/skin_setup.xml wrote: <screen name="Setup" position="0,0" size="1280, 720" backgroundColor="background" flags="wfNoBorder" title="Setup">
<panel name="SetupCommon" />
<panel name="SetupTextHelp" />
<panel name="SetupConfig"/>
<widget name="footnote" position="510,580" size="720,25" font="Regular;14" borderWidth="1" foregroundColor="grey" zPosition="1" transparent="1"/>
</screen>

It seems I will need to trace back the inheritance of those three panels and manually piece it together into a single complete skin definition to which I can then start adding extra widgets.

However I'm concerned that if I hard code it that way, my plugin will only be compatible with easy-skin-aus-hd (for example, because it uses button_200x60_red.png, which may be specific to easy-skin-aus-hd).

Whereas if I simply use self.skinName = ["Setup"], then enigma will use whatever system skin happens to be implementing <screen name="Setup"> (eg. if user selects full-metal-wizard skin, then user will get full metal wizard's version of system setup skin).

A workaround might be to simply include all hard coded assets (such as the button png) into a subfolder in my plugin's folder, but then if the user changes their global skin to, say, full-metal-wizard, then my plugin will still be using easy-skin-aus-hd assets.

What I really need is to say to enigma "give me the system setup skin, but extend/override these particular widgets" like you can do in classes. I'm not sure it's possible.

prl
Wizard God
Posts: 32705
Joined: Tue Sep 04, 2007 13:49
Location: Canberra; Black Mountain Tower transmitters

Re: Hello World

Post by prl » Sun Jul 12, 2020 19:14

sonicblue wrote:
Sun Jul 12, 2020 18:12
prl wrote:
Sun Jul 12, 2020 14:39
You add the widget in the code, like self["status"] = Components.Label() and then add the corresponding widget to the screen's skin: <widget name="status" position="..." size="..." etc, etc />.

But isn't that only in the case where we define our own skin as a class variable in our own class? Because, as soon as I add self.skinName = ["Setup"] to my class, enigma ignores my class variable skin, and instead uses the skin that self.skinName = ["Setup"] points to.

What I would like to do is set self.skinName = ["Setup"] in my class (which appears to tell enigma to use the default system skin for setup menus) and then I would like to extend that skin by adding some extra widgets to it.

When you create an instance of a class that inherits from Screen, then Screen.__init__() sets self.skinName to the name of your class (as a string, not as a list of strings).

However, if your class inherits from Setup (not just from ConfigListScreen, which is a screen mixin that doesn't inherit from Screen), then Setup.__init__() sets self.skinName to a list that includes "Screen".

That means that class MySetupScreen(Setup) will use the skin "Setup", and you don't need to do anything more.

But if you're writing a setup screen that doesn't inherit from Setup, but you want it to use the "Setup" skin, then you need to set self.skinName = ["Setup"] to something appropriate yourself, but it must be called after Screen.__init__() has been called.
sonicblue wrote:
Sun Jul 12, 2020 18:12
An alternative idea I had was to find the location of the system setup skin, and copy the section from it where it defines what self.skinName = ["Setup"] points to, and paste that into my own class variable skin, and then I can remove the self.skinName = ["Setup"] from my class.

self.skin is the skin of last resort - it will only be use if no skin listed in self.skinName matches a skin defined in the current skins files (which can also include screen skins in the default skin).
Peter
T4 HDMI
U4, T4, T3, T2, V2 test/development machines
Sony BDV-9200W HT system
LG OLED55C9PTA 55" OLED TV

User avatar
peteru
Uber Wizard
Posts: 9737
Joined: Tue Jun 12, 2007 23:06
Location: Sydney, Australia
Contact:

Re: Hello World

Post by peteru » Tue Jul 14, 2020 11:45

You can't inherit a screen skin and extend it via code. Skins are XML documents and there are no APIs to programmatically copy parts of a skin or insert new nodes. It wouldn't work too well anyway, since all the elements of a skin need to be laid out by the skin designer - the programmer can not predict how the skin will be structured in terms of layout.

If your code can not reuse a screen from the preinstalled skin, you will need to write your own skin for your screens and distribute it with the plugin. Other skin authors will then have an option to reskin your screens in their skin implementations. If you go down that path, it's best to ensure that your plugin's skin does not depend on components from other skins. Having that kind of dependency increases the chances that eventually your plugin will break when there is an implementation change.

"Beauty lies in the hands of the beer holder."
Blog.

sonicblue
Master
Posts: 247
Joined: Wed Oct 25, 2017 14:30

Re: Hello World

Post by sonicblue » Wed Jul 22, 2020 16:57

prl wrote:
Tue Jun 09, 2020 14:29
sonicblue wrote:
Tue Jun 09, 2020 13:42
For example if two timers are calling the same function, and the first timer is half way through the function when the second timer fires, will it:

a) interrupt the function being called by the first timer, then when it finishes, hand back execution to the first timer's function instance
b) as above, but without handing execution back
c) wait for the first timer's function instance to finish, then call the function the second time
Only one eApp main dispatch loop event is ever active at a time, so c).

I notice the author of VideoMode.py appears to have gone out of their way to use their own variable to track whether the timer's function is in progress, and if so, they are deliberately avoiding calling timer.stop() in that case:

Code: Select all


def __init__(self, session):

	self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
	{
		iPlayableService.evVideoSizeChanged: self.VideoChanged,
		iPlayableService.evVideoProgressiveChanged: self.VideoChanged,
		iPlayableService.evVideoFramerateChanged: self.VideoChanged,
	})
	
	self.delay = False 
	self.detecttimer = eTimer()
	self.detecttimer.callback.append(self.VideoChangeDetect)


def VideoChanged(self):  # called when the video stream changes
	
	if not self.detecttimer.isActive() and not self.delay: 
		self.delay = True
		self.detecttimer.start(delay, True)
	else:
		self.delay = True
		self.detecttimer.stop()
		self.detecttimer.start(delay, True)
	

def VideoChangeDetect(self, caller=""): 		
	
	self.delay = False
	


The only explanation I can infer is that timer.stop() may actually halt execution of the timer's function mid-way through it. Is this correct? It doesn't seem to be, as I've called timer.stop() inside my own timers' funcs, and it still allows execution to continue after that.

Or perhaps, is it only if timer.stop() is called from outside of the timer's thread that it halts the func?

Or perhaps, maybe the author planned on using self.delay elsewhere, but abandoned it and just didn't bother to revert the code?
Last edited by sonicblue on Thu Jul 23, 2020 09:20, edited 1 time in total.

User avatar
adoxa
Wizard
Posts: 1490
Joined: Thu Feb 23, 2017 22:58
Location: CQ
Contact:

Re: Hello World

Post by adoxa » Wed Jul 22, 2020 17:42

Short timers are repeat by default, so self.delay was probably used because of that; peteru changed it to one-shot (the True argument). Restarting a timer automatically stops it, so self.delay could disappear altogether and the whole if/else block can be replaced with just the start line.

prl
Wizard God
Posts: 32705
Joined: Tue Sep 04, 2007 13:49
Location: Canberra; Black Mountain Tower transmitters

Re: Hello World

Post by prl » Wed Jul 22, 2020 18:20

The self.delay stuff may be because the iPlayableService events are coming from a different thread, and it may be there to ensure thread safety.

However, there's no need to stop a timer before restarting it (look at the code for eTimer::start() in base/ebase.cpp), so:

Code: Select all

		if not self.detecttimer.isActive() and not self.delay:
			self.delay = True
			self.detecttimer.start(delay, True)
		else:
			self.delay = True
			self.detecttimer.stop()
			self.detecttimer.start(delay, True)
Is equivalent to:

Code: Select all

		if not self.detecttimer.isActive() and not self.delay:
			self.delay = True
			self.detecttimer.start(delay, True)
		else:
			self.delay = True
			self.detecttimer.start(delay, True)
Which reduces to:

Code: Select all

		self.detecttimer.start(delay, True)
and we can dispense with self.delay entirely, because its value isn't referenced anywhere any more.
Peter
T4 HDMI
U4, T4, T3, T2, V2 test/development machines
Sony BDV-9200W HT system
LG OLED55C9PTA 55" OLED TV

sonicblue
Master
Posts: 247
Joined: Wed Oct 25, 2017 14:30

Re: Hello World

Post by sonicblue » Thu Jul 23, 2020 09:17

prl wrote:
Wed Jul 22, 2020 18:20
The self.delay stuff may be because the iPlayableService events are coming from a different thread, and it may be there to ensure thread safety.

So it's possible that another iPlayableService event could interrupt VideoChanged() half way through itself? If true, do we know what happens once the interrupting thread finishes? eg. hand back execution to previous thread or discard previous thread?
adoxa wrote:
Wed Jul 22, 2020 17:42
Short timers are repeat by default, so self.delay was probably used because of that; peteru changed it to one-shot (the True argument).

Just to clarify, do you mean to say that peteru changed VideoChanged() to include the True argument, or that peteru modified the Timer C++ code to accept a True argument. I'm hoping not the latter, otherwise that could lead to a plugin being compatible with Beyonwiz but incompatible with other enigma boxes.

User avatar
adoxa
Wizard
Posts: 1490
Joined: Thu Feb 23, 2017 22:58
Location: CQ
Contact:

Re: Hello World

Post by adoxa » Thu Jul 23, 2020 11:19

The former - look at commit d6c76.

sonicblue
Master
Posts: 247
Joined: Wed Oct 25, 2017 14:30

Re: Hello World

Post by sonicblue » Thu Jul 23, 2020 15:51

Thanks. Just did some testing and found the iPlayableService events can indeed interrupt whatever function is executing, even non-timer ones like __init__(). In fact it's a coincidence that VideoMode.__init__() doesn't crash since it's only a fluke that the iPlayableService events aren't arriving before the timer is created further on. In my test example, even adding only one extra line of code (creating a dialog via self.session.instantiateDialog) delays the __init__() function just long enough to allow iPlayableService events to arrive and crash the system, so it is dangerously close to crashing already!

i.e timers should be created before registering for the iPlayableService events which use those timers.

edit: well now I can't reproduce it anymore, but I have the logfile to prove that it was happening. Perhaps it's more a quirk with restarting the GUI, whereas on a normal boot the _init_ function might get called way before any video stream playback is started.
Last edited by sonicblue on Thu Jul 23, 2020 16:11, edited 2 times in total.

prl
Wizard God
Posts: 32705
Joined: Tue Sep 04, 2007 13:49
Location: Canberra; Black Mountain Tower transmitters

Re: Hello World

Post by prl » Thu Jul 23, 2020 15:54

sonicblue wrote:
Thu Jul 23, 2020 09:17
... peteru modified the Timer C++ code to accept a True argument. I'm hoping not the latter, otherwise that could lead to a plugin being compatible with Beyonwiz but incompatible with other enigma boxes.

No, as adoxa says, eTimer.start() has always (or at least for a very long time) had an optional "oneShot" second argument.

Note that the Python API to eTimer is modified from what is in lib/base/ebase.h by lib/python/python_base.i, though not in any really important ways.

In general, enigma2 coders don't seem to be aware that it isn't necessary to call Timer.stop() before Timer.start() if the timer is already running.
Peter
T4 HDMI
U4, T4, T3, T2, V2 test/development machines
Sony BDV-9200W HT system
LG OLED55C9PTA 55" OLED TV

sonicblue
Master
Posts: 247
Joined: Wed Oct 25, 2017 14:30

Re: Hello World

Post by sonicblue » Sat Jul 25, 2020 16:21

Is it possible to associate an action map with a dialog screen? eg. defining what colour buttons do when the dialog screen is open?

I've tried defining an action map inside the dialog screen's class, and also inside the class which instantiates the dialog, but can't get it to work.

eg.

Code: Select all

class Daemon(Screen, ConfigListScreen):

	def __init__(self, session):
	
		Screen.__init__(self, session)
		
		self["actions"] = ActionMap(["SetupActions", "ColorActions", "MenuActions"],
		{		
			"blue": self.keyBlue		
		
		}, -2)	
		
		self.pictureInGraphics = self.session.instantiateDialog(PictureInGraphics)	
		self.pictureInGraphics.show()
		
	def keyBlue(self):
		log("Daemon.keyBlue()")


class PictureInGraphics(Screen):

	def __init__(self, session):

		self.skin = """<screen name="PictureInGraphics" position="0,0" size="1280,720" backgroundColor="#ff000000"
		transparent="0" flags="wfNoBorder"> <widget position="0,0" size="1280,720" source="session.VideoPicture"
		render="Pig" backgroundColor="#ff000000"/></screen>"""
		
		Screen.__init__(self, session)
		
		self["actions"] = ActionMap(["SetupActions", "ColorActions", "MenuActions"],
		{		
			"blue": self.keyBlue		
		
		}, -2)	

	def keyBlue(self):
		log("PictureInGraphics.keyBlue()")

I've also tried to copy VolumeControl.py as an example (which uses globalActionMap instead) but couldn't get it to work either:

Code: Select all

class VolumeControl:

	def __init__(self, session):
		global globalActionMap
		globalActionMap.actions["volumeUp"]=self.volUp
		self.volumeDialog = session.instantiateDialog(Volume)
		
	def volUp(self):
		self.setVolume(+1)
		
class Volume(Screen):

	def __init__(self, session):
		Screen.__init__(self, session)
		self.volumeBar = VolumeBar()
		self["Volume"] = self.volumeBar	
		

prl
Wizard God
Posts: 32705
Joined: Tue Sep 04, 2007 13:49
Location: Canberra; Black Mountain Tower transmitters

Re: Hello World

Post by prl » Sat Jul 25, 2020 17:51

If you want to enable button presses in a Screen, the ActionMap for that screen needs to be in the screen that the buttons are controlling.

It's a bit hard to work out quite what you're trying to do, and it's not helped by the fact that you don't describe what happens (e.g. how does your Daemon screen get instantiated? Does your PictureInGraphics screen get displayed (it would normally have show() called on it after it has configured itself properly by returning from __init__(), and the PictureInGraphics.show() would normally be called in Daemon after it, too, has properly configured itself). Do you see either "button pressed" output in the debug log?

So Daemon should be something like:

Code: Select all

class Daemon(Screen, ConfigListScreen):

	def __init__(self, session):

		Screen.__init__(self, session)
	
		self["actions"] = ActionMap(["SetupActions", "ColorActions", "MenuActions"],
		{	
			"blue": self.keyBlue	
	
		}, -2)

		self.onLayoutFinish.append(self.layoutFinished)

		self.pictureInGraphics = self.session.instantiateDialog(PictureInGraphics)

	def layoutFinished(self):
		self.pictureInGraphics.show()
That should make Daemon display PictureInGraphics as soon as Daemon has been configured (had its C++ instance initialised, had its skin applied, been placed in the dialog stack, etc).

Note that there is no mechanism in Daemon for user control of when PictureInGraphic is displayed, and no method in either for exiting the screens. They could both do with, say "ok": self.close, in their respective ActionMaps (not even closing a screen comes for free!), and you might put a "green": self.pictureInGraphics.show into Daemon instead of the code to show it unconditionally. In that case self.pictureInGraphics would need to be initialised before the ActionMap is created.
Peter
T4 HDMI
U4, T4, T3, T2, V2 test/development machines
Sony BDV-9200W HT system
LG OLED55C9PTA 55" OLED TV

sonicblue
Master
Posts: 247
Joined: Wed Oct 25, 2017 14:30

Re: Hello World

Post by sonicblue » Sat Jul 25, 2020 19:43

prl wrote:
Sat Jul 25, 2020 17:51
Do you see either "button pressed" output in the debug log?
I do not.


prl wrote:
Sat Jul 25, 2020 17:51
It's a bit hard to work out quite what you're trying to do, and it's not helped by the fact that you don't describe what happens (e.g. how does your Daemon screen get instantiated?

Here is a complete example. I am not sure why it doesn't work.

Code: Select all

import os
from Screens.Screen import Screen
from Components.ConfigList import ConfigListScreen
from Components.ActionMap import ActionMap
from Plugins.Plugin import PluginDescriptor

class Dialog(Screen):

	def __init__(self, session):	
		
		self.skin = """<screen name="Dialog" position="0,0" size="400,400" backgroundColor="#00000000" transparent="0" flags="wfNoBorder" zPosition="11"></screen>"""
		
		Screen.__init__(self, session)
		
		self["actions"] = ActionMap(["ColorActions"],
		{		
			"blue": self.keyBlue	
		}, -2)
		
	def keyBlue(self):
		print "Success"


class Daemon(Screen, ConfigListScreen):

	def __init__(self, session):
	
		Screen.__init__(self, session)	
		self.dialog = self.session.instantiateDialog(Dialog)
		self.onLayoutFinish.append(self.layoutFinished)
		
	def layoutFinished(self):
		print "Daemon.layoutFinished()"
		self.dialog.show()


def startDaemon(session, **kwargs):
	session.open(Daemon)
	
def Plugins(**kwargs):			
	DescriptorList = []   
	DescriptorList.append(
		PluginDescriptor(
			name="Daemon",		
			where = PluginDescriptor.WHERE_SESSIONSTART,		
			description=_("Daemon"),
			fnc=startDaemon
		)	
	)	
	return DescriptorList

sonicblue
Master
Posts: 247
Joined: Wed Oct 25, 2017 14:30

Re: Hello World

Post by sonicblue » Sat Jul 25, 2020 20:03

sonicblue wrote:
Sat Jul 25, 2020 19:43
prl wrote:
Sat Jul 25, 2020 17:51
Do you see either "button pressed" output in the debug log?
I do not.

In case you meant this:

Code: Select all

{1394}<105812.619> KEY: 401 make KEY_BLUE ('BLUE',)
{1394}<105812.934> KEY: 401 break KEY_BLUE ('BLUE',)
Then yes.

Pressing the blue button just brings up the extension list quickmenu on screen. However it should not, as my dialog Screen is on top (zPosition="11") and has an action map with a "blue" key in its dictionary.

User avatar
peteru
Uber Wizard
Posts: 9737
Joined: Tue Jun 12, 2007 23:06
Location: Sydney, Australia
Contact:

Re: Hello World

Post by peteru » Sun Jul 26, 2020 04:05

Couple of things that I can see that may warrant a look-see:
  • Daemon inherits from ConfigListScreen, but never calls it's __init__.
  • Last argument, to ActionMap is the priority. Is -2 too low?
They may not be the source of your problems, but are worth investigating.

"Beauty lies in the hands of the beer holder."
Blog.

prl
Wizard God
Posts: 32705
Joined: Tue Sep 04, 2007 13:49
Location: Canberra; Black Mountain Tower transmitters

Re: Hello World

Post by prl » Sun Jul 26, 2020 10:58

Having a screen called Daemon inheriting from Screen, but being instantiated as a WHERE_SESSIONSTART plugin sounds odd to me, anyway. Screens are interactive, daemons tend not to be.

Why do you think that Daemon should inherit from Screen?

That aside, it's still really unclear to me quite what you're trying to achieve. If it's to display a Picture in Graphics (PiG) panel into a setup screen (for example, to give immediate feedback about changes made in your VMode screen, then you're overcomplicating things.

If that's the case, you should just put something like:

Code: Select all

		<widget position="880,20" size="388,218" source="session.VideoPicture" render="Pig" zPosition="3" backgroundColor="#ff000000" />
in the screen's skin. I don't think that you need to write any Python code to do this.

If it's not that, I'm not sure what it is that you're trying to do, or why you're using session.instantiateDialog() rather that session.open() to have your background Daemon open a screen.
Peter
T4 HDMI
U4, T4, T3, T2, V2 test/development machines
Sony BDV-9200W HT system
LG OLED55C9PTA 55" OLED TV

sonicblue
Master
Posts: 247
Joined: Wed Oct 25, 2017 14:30

Re: Hello World

Post by sonicblue » Sun Jul 26, 2020 16:37

prl wrote:
Sun Jul 26, 2020 10:58
Having a screen called Daemon inheriting from Screen, but being instantiated as a WHERE_SESSIONSTART plugin sounds odd to me, anyway. Screens are interactive, daemons tend not to be.

I've since removed the inheritance of Screen and ConfiglistScreen as they are not needed for the daemon. I only left them in because I don't know how enigma works behind the scenes, eg. whether enigma still requires things to be a Screen in order for certain functionality to work.

The code I posted was just an example to display a blank dialog screen, 400x400px of black pixels, and then associate an action map with it, to try and get the action map to work when the dialog is open. It's not meant to do anything other than that.

I've since found a code example in ChannelSelection.py where the author opens a dialog via instantiateDialog() in which its Screen class defines an action map which works, proving that instantiateDialog() should be compatible with action maps.

I've since worked around the need to associate an action map with my instantiateDialog(), so this is purely out of curiosity as to why I can't get the action map to work with a screen created via instantiateDialog().

I'm deliberately using instantiateDialog() instead of session.open() as I found it doesn't cause any other active GUI's (such as infobar) to flicker when I open and close it (if you recall, I'm doing this to reset the sharpness bug on the V2, and I want it to be seamless or as unnoticeable as possible for the user).

sonicblue
Master
Posts: 247
Joined: Wed Oct 25, 2017 14:30

Re: Hello World

Post by sonicblue » Mon Jul 27, 2020 10:42

sonicblue wrote:
Sun Jul 26, 2020 16:37
I've since removed the inheritance of Screen and ConfiglistScreen as they are not needed for the daemon. I only left them in because I don't know how enigma works behind the scenes, eg. whether enigma still requires things to be a Screen in order for certain functionality to work.

So it turns out my daemon does need to be a Screen, since it uses the ServiceEventTracker class to detect changes to the video content, and that class "tracks service events into a screen" (its __init__ takes a Screen as an argument).

Weirdly the daemon has been working fine all day yesterday despite not being a Screen and passing a non-screen object (self) to ServiceEventTracker, however today it started to crash.

User avatar
MrQuade
Uber Wizard
Posts: 11844
Joined: Sun Jun 24, 2007 13:40
Location: Perth

Re: Hello World

Post by MrQuade » Mon Jul 27, 2020 11:19

Out of interest, have you tried the latest OpenATV firmware to check the newer drivers?
I have it running on one of my V2s, but that V2 is connected to an old CRT TV, so I can't do much meaningful testing with that one.
Logitech Harmony Ultimate+Elite RCs
Beyonwiz T2/3/U4/V2, DP-S1 PVRs
Denon AVR-X3400h, LG OLED65C7T TV
QNAP TS-410 NAS, Centos File Server (Hosted under KVM)
Ubiquiti UniFi Managed LAN/WLAN, Draytek Vigor130/Asus RT-AC86U Internet
Pixel 4,5&6, iPad 3 Mobile Devices

prl
Wizard God
Posts: 32705
Joined: Tue Sep 04, 2007 13:49
Location: Canberra; Black Mountain Tower transmitters

Re: Hello World

Post by prl » Mon Jul 27, 2020 11:51

Here's a working version of what I think you are trying to do. It creates the screen 30 seconds after session start so that it can be sure that the UI is fully up and running before it creates the screen.

It can be switched between using instantiateDialog() and open() by changing the global useInstantiateDialog.

Pressing blue prints a message to the debug log. Pressing GREEN removes the dialog (deactivates its ActionMap and hides it if using an instantiateDialog(), and closes it if using open()).

I've made the screen slightly less boring ;)
Attachments
Daemon.zip
(1.19 KiB) Downloaded 45 times
Peter
T4 HDMI
U4, T4, T3, T2, V2 test/development machines
Sony BDV-9200W HT system
LG OLED55C9PTA 55" OLED TV

sonicblue
Master
Posts: 247
Joined: Wed Oct 25, 2017 14:30

Re: Hello World

Post by sonicblue » Mon Jul 27, 2020 12:27

MrQuade wrote:
Mon Jul 27, 2020 11:19
Out of interest, have you tried the latest OpenATV firmware to check the newer drivers?
I have it running on one of my V2s, but that V2 is connected to an old CRT TV, so I can't do much meaningful testing with that one.

I saw your comment in the other thread and was interested in doing that, however I'm a bit terrified of screwing something up as it seems the process of getting OpenATV to work is a bit convoluted, and I've only just finally got the V2 working to my satisfaction. I really don't want to start tearing things apart now :lol:

It seems the feature set of OpenATV may not be sufficient either, so I'd still have to wait for the next V2 firmware update (correct me if I'm wrong on this - I was hoping we could somehow copy over the driver via FTP without having to rebuild the V2 image).

prl wrote:
Mon Jul 27, 2020 11:51
Here's a working version of what I think you are trying to do. It creates the screen 30 seconds after session start so that it can be sure that the UI is fully up and running before it creates the screen.

Thanks a lot! It seems the critical function was execBegin() 8)

User avatar
MrQuade
Uber Wizard
Posts: 11844
Joined: Sun Jun 24, 2007 13:40
Location: Perth

Re: Hello World

Post by MrQuade » Mon Jul 27, 2020 13:06

sonicblue wrote:
Mon Jul 27, 2020 12:27
I saw your comment in the other thread and was interested in doing that, however I'm a bit terrified of screwing something up as it seems the process of getting OpenATV to work is a bit convoluted, and I've only just finally got the V2 working to my satisfaction. I really don't want to start tearing things apart now :lol:
Nah, it's pretty straightforward. You just flash the latest OpenATV v6.5 recovery image like a normal firmware flash. The Beyonwiz images are the same thing, and you can go back and forwards as required.

I can double check that tonight just to be sure though ;). (I haven't rolled back to the Beyonwiz image recently to absolutely guarantee that, given that the new recovery bootloader has changed significantly).

sonicblue wrote:
Mon Jul 27, 2020 12:27
It seems the feature set of OpenATV may not be sufficient either, so I'd still have to wait for the next V2 firmware update (correct me if I'm wrong on this - I was hoping we could somehow copy over the driver via FTP without having to rebuild the V2 image).
My understanding is that the drivers are too closely linked to the kernel to allow them to be portable in that way.
Logitech Harmony Ultimate+Elite RCs
Beyonwiz T2/3/U4/V2, DP-S1 PVRs
Denon AVR-X3400h, LG OLED65C7T TV
QNAP TS-410 NAS, Centos File Server (Hosted under KVM)
Ubiquiti UniFi Managed LAN/WLAN, Draytek Vigor130/Asus RT-AC86U Internet
Pixel 4,5&6, iPad 3 Mobile Devices

prl
Wizard God
Posts: 32705
Joined: Tue Sep 04, 2007 13:49
Location: Canberra; Black Mountain Tower transmitters

Re: Hello World

Post by prl » Mon Jul 27, 2020 14:48

sonicblue wrote:
Mon Jul 27, 2020 12:27
Thanks a lot!

No problem. It's not something I've looked at much before.
sonicblue wrote:
Mon Jul 27, 2020 12:27
It seems the critical function was execBegin() 8)

Dialogs created with instantiateDialog() don't necessarily want to have user input. For example, see the use of SubtitleLabel in Screens/InfoBarGenerics.py.
Peter
T4 HDMI
U4, T4, T3, T2, V2 test/development machines
Sony BDV-9200W HT system
LG OLED55C9PTA 55" OLED TV

Post Reply

Return to “Developers Community”