Beginner question on plugin development - displaying results from python func

Hi, I am trying to understand how the plugin works.

Say in the __ init __.py I have a function

def get_current_time(self):
    return time.time()

I want to have the result from this function to display as a text in the tab view "samplePlugin_tab.jinja2".
How can I link the two? Is Javascript needs to be involved?

Yes, js will be involved and is added if you create the plugin the way the cookiecutter adds it. Your initial skeleton for the plugin will be created. In that will be a js file for your viewmodel and you would use the send_plugin_message function in your plugin to send data to the front end. Then on the front end use the onDataUpdaterPluginMessage callback in js to either inject your text into the id in your jinja file, or assign the value sent to the js side to a knockout observable.

Another new approach is available in 1.5.0 using additional-state-data, that you can return your time in that hook, and then catch it on the js side with fromHistoryData or fromCurrentData viewmodel callbacks.

A third approach is using SimpleApiPlugin mixin and have the js side retrieve information from the python side, and adjust observables with return values.

Here's an example from BLV.

data is collected from gcode received and then sent to the front end here and then the viewmodel picks up that message here and makes the plotly graph on the tab.

Hi, jneilliii,

I tried to mimic the example with following:

  1. create a update_time functionin __ init __.py
  2. I put this function inside on_after_startup(self)
  3. in javascript file, I created a onDataUpdaterPluginMessage function, and set the time_data variable from the python script to self.timeData(), a ko.observable()
  4. And then reference the ko.observable in tab jinja2

Problem:
If I set breakpoint in javascript on line self.timeData(data.time_data()) using Firefox's Web Developer's Debugger, I can actually see time_data() has successfully stored the time.time() variable, but it is still not showing in the actual tab.

Is it because "on_after_startup" only gets to run once?

Content of __ init __.py

def get_current_time(self):
    return time.time()

def update_time(self):
    self._plugin_manager.send_plugin_message(self._identifier, dict(time_data=self.get_current_time()))

def on_after_startup(self):
    self.update_time()

Content of javascript

self.timeData = ko.observable();
self.onDataUpdaterPluginMessage = function (plugin, data) {
		if (plugin !== "AutoBedLevelChecker") {
			return;
		}
        if (typeof plugin == 'undefined'){
            return;
        }
        if(self.settingsOpen){
            return;
        }
        if (data.hasOwnProperty("time_data")) {
            self.timeData(data.time_data())
        }
     }

Content of tab.jinja2

<p> timeData: <strong data-bind="text: timeData"></strong></p>

Remove the () from this call to data.time_data(). There's probably an error in the browser console relating to it not being a function.

if (data.hasOwnProperty("time_data")) {
            self.timeData(data.time_data)
        }
1 Like

Ah, that is the problem.

Thank you for pointing that out.

Another change I made was to put update_time() within a hook: octoprint.comm.protocol.gcode.received".

The on_after_startup() only gets executed once, where octoprint.comm.protocol.gcode.received" will update the datetime variable on each gcode received.

You will want to be careful with that hook, as it can get quite chatty as it's every communication back from the printer.

I figured this is not the best use of the hook in this kind of application. Is there another way to trigger the self._plugin_manager.send_plugin_message from javascript?

Something like pressing a button in Tab view, or have it run like a server in the background?

There are really a couple of options. It really depends on if you want it demand loaded or pre-loaded in the UI on page load and updated on a regular interval, etc. If you can be more clear as to where you're going with the "time" observable it might be easier to direct you toward the right hook/approach.

Running in the background you may want to look at the octoprint.util.RepeatedTimer class, it's documented if you search for it on https://docs.octoprint.org

To communicate JS > Server side, you'll need to implement an API. The Simple API plugin mixin can do this quite well.

I am still learning about the basics, I'd like to know the options. For on-demand load, what method is suitable, and for regular interval, what options do I have?

Many thanks to both Charlie and jneilliii. Happy holidays.

For demand load the easiest approach is using simpleapiplugin mixin and the OctoPrint.simpleApiGet() with get or OctoPrint.simpleApiCommand() for post commands in your js file using a click event possibly.

The regular interval approach is a combination of the repeatedTimer util in python like Charlie mentioned and the callback function would have a send_plugin_message in it and then caught on the js side like I mentioned earlier.

That's the two most common approaches.

def foo():
x = 'hello world'
return x # return 'hello world' would do, too

foo()
print x # NameError - x is not defined outside the function

y = foo()
print y # this works

x = foo()
print x # this also works, and it's a completely different x than that inside
# foo()

z = bar(x) # of course, now you can use x as you want

z = bar(foo()) # but you don't have to

I think this might help you in displaying the results from a python function.
Hope this works for you and you get your solution for this.

AARP Medicare

This doesn't really make much sense, and even if you were to runt that code it would fail - bar() is not defined. The context of the question is about an OctoPrint plugin, printing to console won't help if you can't access the console.