Find informations, such as the job (file) and send it to js

Hello guys.

First of all, I'm pretty good with python, but totally new with javascript.

I'm trying to create a small plugin. After doing all the tutorial.. Of course we learn about javascript and python. But getting all the informations (properties/variables) about the printer/job is missing.

1. How can I open a tab page after I click a button in a sidebar. I know how to create a sidebar and a tab. Do I need to do it in javascript before it's the frontend? Or python (octoprint) can redirect? I can find the baseurl with:

    @property
    def get_url_tab(self):
        return "#tab_" + self.template_folder_key

If I need to use javascript, how do i send the url from python to js? For now I use a static url:

$(function() {
    function MyPluginViewModel(parameters) {
        var self = this;

        self.settings = parameters[0]
        self.baseurl = OctoPrint.options.baseurl

        self.test = function(data) {
            //OctoPrint.simpleApiCommand("myplugin", "test");
            window.open(self.baseurl + "#tab_plugin_myplugin", "_self")
        };
    }

    OCTOPRINT_VIEWMODELS.push({
        construct: MyPluginViewModel,
        elements: ["#test"]
    });
});

Is it the good way?

2. Where can I find the job (or file selected) ?
Exemple of Sidebar STATE:
I tried to search the behavior of /template/sidebar/state.jinja2
But i never saw the init.py file for this template. (Just to see how the file is found with all the informations needed..)

Do I need to use the EventHandlerPlugin with on_event?
We can see that the file is selected with:

def on_event(self, event, payload):
        if event == octoprint.events.Events.FILE_SELECTED:
            self._logger.info("File Selected =>", payload)
            print("JOB:", self._printer.get_current_job())

But I think there is a better way. Because if I'm searching with self._printer.get_current_job() it find nothing. Might be because the Event is done before the file is selected.

I have so much questions. The communications between javascript and python is in a learning curve :wink:
Thanks for your help.

one option is in your js file you can create a dependency to the stateViewModel directly and then use it's data for your plugin's tab, sidebar, etc.

$(function() {
    function MyPluginViewModel(parameters) {
        var self = this;

        self.settings = parameters[0]
        self.printerStateViewModel= parameters[1];
        self.baseurl = OctoPrint.options.baseurl

        self.test = function(data) {
            //OctoPrint.simpleApiCommand("myplugin", "test");
            window.open(self.baseurl + "#tab_plugin_myplugin", "_self")
        };
    }
    OCTOPRINT_VIEWMODELS.push({
        construct: MyPluginViewModel,
        elements: ["#test"],
        dependencies: ["settingsViewModel", "printerStateViewModel"]
    });

then in your jinja file for whatever you want to display you data-bind to it from the info available from stateViewModel like this for example.

Print Time Left: <strong data-bind="text: printerStateViewModel.printTimeLeftString, attr: {title: printerStateViewModel.printTimeLeftExactString}" title="-">-</strong>

you can right-click any of those areas to try and see how it's binding and then just append printerStateViewModel. to the same thing to get it to show in yours.

the other option is using the embedded js client of OctoPrint if you just need to reference the information within a js function.

https://docs.octoprint.org/en/master/jsclientlib/job.html

for example you can open the developer tools and run this command in the console.

OctoPrint.job.get().done(function(data){console.log(data);});

and get a response like

{
    "job": {
        "estimatedPrintTime": null,
        "filament": {
            "length": null,
            "volume": null
        },
        "file": {
            "date": null,
            "name": null,
            "origin": null,
            "path": null,
            "size": null
        },
        "lastPrintTime": null,
        "user": null
    },
    "progress": {
        "completion": null,
        "filepos": null,
        "printTime": null,
        "printTimeLeft": null,
        "printTimeOrigin": null
    },
    "state": "Operational"
}

Hello.

Thanks for the reply.

When you say, that I can right-click, where can I right-click? I saw the dependencies in the documentation, but they don't show the attributes. I tried with filesViewModel, and printerStateViewModel.

How did you find the data-bind attributes for printerStateViewModel? such as printTimeLeftExactString

Most of the documentation have no hyperlink, about the attributes or variables or exemples included.. Every time, I have to go to octoprint github and search for each item.
Like I did with /template/sidebar/state.jinja2 (but i saw nothing connected to it)
That's why I'm lost in the documentation, Haha.

Edit:
The goal: If a file is selected and I can click on the sidebar button. This button will redirect me to my tab page. And from this, I can see the informations about the file and the gcode...

Sorry about that, Right-click and select inspect. You'll see something like this.

image

which shows the filedisplay for regular text, and filepath for the hover over the file name. Digging through the src is another valid approach to find what is able to be bound to per view model.

Why a sidebar button just to switch tab? You could just bind your tab's dependency to the printerStateViewModel and it will always just show what you want in the tab.

Edit: you could just have the following in your sidebar jinja if you really want to have a button.

<a href="#tab_plugin_<plugin_identifier>" class="btn btn-primary"> <i class="fas fa-info"></i> Details </a>

Oh yeah! the inspect elements with developer tools. It can work.
But I need to know which plugins use this ModelView, to inspect it.
Why it's not in the documentation, lmao.

How to see it in the jinja2 page? Since it's inside the strong and not outside?

<strong data-bind="text: filedisplay, attr: {title: filename}" title="-">-</strong>
<input type="text" class="input-block-level" data-bind="text: filedisplay, attr: {title: filename}" readonly>
<label  data-bind="text: filedisplay, attr: {title: filename}" > {{ filename }} </label>

I'm missing something to understand this lol.

EDIT:
The Goal: I want to modify the gcode (or maybe create a copy inside a folder) before it print.

    1. I create a checkbox in the sidebar (In the State's sidebar, under the print/pause/cancel), to enable the plugin and after, I manually go to the tab page and modify the gcode.
    1. I create a button in the sidebar (In the State's sidebar, under the print/pause/cancel), so it redirect me to the tab page, by the click event.. to modify the gcode.

I recommend you check out the gcode editor plugin.

1 Like

Thanks, that's what I'm doing right now. :slight_smile:
I'm watching all the plugins I have.
I'll try to read all the knockout documentation. I already used jinja with python and django. All the javascript part is new. The communication between javascript and python is... my brain is mixed about who does what lol.

generally from js front-end to python back-end you would use API commands (SimpleApiPlugin or BluePrint plugin mixins) and for sending information from back-end python to front-end UI through js you use send_plugin_message (python side) and onDataUpdaterPluginMessage (javascript side) to do stuff in the UI.

1 Like

Thanks you very much, for your help.

I realized that it was a problem with printerStateViewModel and /template/sidebar/state.jinja2. Something was in conflict. So nothing was working.

Since I saw that, I changed everything. I'll only use the tab and the settings.

The main problem is how to use the ModelView variables/attributes, inside my own template. Without copy/paste everything

Exemple: How to use printerProfilesViewModel with the attribute availableOrigins and volumeOrigin.

test.js

self.settings = parameters[0];
self.printerStateViewModel = parameters[1];
self.printerProfilesViewModel = parameters[2];
self.filesViewModel = parameters[3];

OCTOPRINT_VIEWMODELS.push({
        construct: TestViewModel,
        dependencies: [
            "settingsViewModel",
            "printerStateViewModel",
            "printerProfilesViewModel",
            "filesViewModel",
        ],
        elements: ["#test_dialog"]
    });

and inside my template

test_tab.jinja2

<div id="test_dialog">
  <div class="control-group">
      <label class="control-label">{{ _('Origin') }}</label>
      <div class="controls">
          <select data-bind="value: printerProfilesViewModel.volumeOrigin, options: printerProfilesViewModel.availableOrigins, optionsText: 'name', optionsValue: 'key'">
              <option value="lowerleft">Lower Left</option>
              <option value="center">Center</option>
          </select>
      </div>
  </div>
</div>

How to use the developer tool to find all the available attributes? I'm still using the github to see everything.

I'm trying to edit the html/attributes, but nothing appears as choices.

When I do:

test.js
self.volumeOrigin = self.printerProfilesViewModel.volumeOrigin;
self.availableOrigins = self.printerProfilesViewModel.availableOrigins();

and remove printerProfilesViewModel inside the template. It almost work. But I have the error:

Error instantiating testViewModel : TypeError: self.printerProfilesViewModel.availableOrigins is not a function

EDIT:
If I remove the parentheses to availableOrigins, no error, but nothing is shown.

try binding to printerProfilesViewModel.currentProfileData.volume.origin.

1 Like

Thank you.
I think I'll close the subject.
I'm almost done, just need to join my python script that I did before I start learning about plugin.
I just hope, that most slicers use the same gcode :face_with_hand_over_mouth: