GCODE Viewer API

I was wondering if there is an API interface to get the GCODE viewer to show the file that is being printed or if a javascript code is available to integrate in an app

Thanks

this do? http://docs.octoprint.org/en/master/api/job.html#retrieve-information-about-the-current-job

I just used my octo-client software from npmjs.com to quickly query the currently-running job's filename using NodeJS. I assume that you have Node and npm installed:

cd sites
mkdir octo-getjob && cd octo-getjob
touch octo-getjob
chmod a+x octo-getjob
npm init    # When asked for the entry point, indicate "octo-getjob"
npm install --save octo-client
nano node_modules/octo-client/config.js # Edit as necessary with your API key
nano octo-getjob

octo-getjob contents:

#!/usr/bin/env node

var OctoPrint = require('octo-client');

OctoPrint.job(function(response){
  console.log(response.job.file.name);
});
./octo-getjob
# Displays "pizero-case-parts-nocutout.gcode"

I am looking for an API that can actually show the graphic of the GCODE just like in ../#gcode view so that I can embed that in my web page

Oh...

I could think of three approaches.

  1. Copy the existing code to create just the content you want and make your own API endpoint
  2. Create a script which does a curl of the Gcode tab page and then remove the HTML that you're not interested in.
  3. Create a proxy which does this

Could you give an example of the approaches, that will help

Any of the above would be a fair amount of work, to be honest. I attempted to determine a quick-and-dirty method of using awk at the command line to pare down the page but it's not something that would be easy.

Perhaps others would like to take on the challenge.

Hello from 5 years into the future. I found myself wanting the same functionality as you this morning. I took a look at the plugin source code and decided against trying to integrate with that for the time being, so instead opted to hack some functionality on top.

I made a TamperMonkey script that will automatically activate a fullscreen view of the gcode visualizer if you go to http://octopi.local/?fullscreen=true#gcode. It also adds a button to the gcode visualizer tab so that you can enter fullscreen mode via a button press.

If you don't want to use TamperMonkey you can run the script directly in the console, but TamperMonkey is nice because it will automatically source the script when the page loads. It's all a bit hacky but it works, so that's good enough for now, and maybe someone else can improve it.

// ==UserScript==
// @name         octoprint-gcode-visualizer
// @version      0.1
// @description  view the octoprint gcode visualizer in full screen when ?fullscreen=true and #gcode hash are in url
// @author       gagregrog
// @match        http://octopi.local/*
// @grant        none
// ==/UserScript==

(function() {
  'use strict';

  let styles = null;
  const zIndex = 999999999;
  const start = Date.now();

  const elements = {
    gcode: null,
    container: null,
    wrapper: null,
    canvas: null,
    verticalSlider: null,
    horizontalSlider: null,
    originalZoomButton: null,
    newZoomBtn: document.createElement('button'),
    exitFullscreen: document.createElement('button'),
  };

  info('Initializing...');
  init();

  function init() {
    // get a reference to all of the elements that we need
    elements.gcode = document.getElementById('gcode');
    elements.container = document.getElementById('canvas_container');
    elements.wrapper = elements.container.getElementsByClassName('gcode_canvas_wrapper1')[0];
    elements.canvas = document.getElementById('gcode_canvas');
    elements.verticalSlider = document.getElementById('gcode_layer_slider');
    elements.horizontalSlider = document.getElementById('gcode_command_slider');
    for (const label of elements.gcode.getElementsByTagName('label')) {
      if (!elements.originalZoomButton && label.textContent?.includes('Zoom in on model')) {
        elements.originalZoomButton = label.getElementsByTagName('input')[0];
      }
    }

    // if any of the elements aren't found, try again in a moment
    if ([elements.container, elements.wrapper, elements.canvas, elements.verticalSlider, elements.horizontalSlider, elements.originalZoomButton].some(ele => !ele)) {
      // abort if not found after 10 seconds
      if (Date.now() - start < 10000) {
        setTimeout(init, 100);
      } else {
        err('Could not find the required elements!');
      }
      return;
    }

    initializeButtonsAndListeners();
    initializeStyles();
    info('Initialized!');
    stashOriginalStyles();

    enableFullscreenIfQueryPresent(true);
  }

  function initializeButtonsAndListeners() {
    addButtons();

    // listener to update the dimensions if the window is resized
    window.addEventListener('resize', () => {
      updateDimensions();
      enableFullscreenIfQueryPresent(false);
    }, true);

    // let new button click old button to trigger centering of the image
    elements.newZoomBtn.addEventListener('click', () => elements.originalZoomButton.click());
  }

  function initializeStyles() {
    const dim = getMaxDim();
    // capture overrides and original styles to toggle back and forth
    styles = [
      {
        element: document.body,
        styles: [
          { key: 'overflow', override: 'hidden', },
        ],
      },
      {
        element: elements.container,
        styles: [
          { key: 'background', override: 'black', },
          { key: 'position', override: 'fixed', },
          { key: 'width', override: '100%', },
          { key: 'height', override: '100%', },
          { key: 'top', override: 0, },
          { key: 'left', override: 0, },
          { key: 'zIndex', override: zIndex, },
        ],
      },
      {
        element: elements.wrapper,
        styles: [
          { key: 'width', override: '100%', },
          { key: 'height', override: '100%', },
        ],
      },
      {
        element: elements.canvas,
        styles: [
          { key: 'height', override: `${dim}px`, },
          { key: 'width', override: `${dim}px`, },
        ],
      },
      {
        element: elements.verticalSlider,
        styles: [
          { key: 'top', override: '15px', },
          { key: 'right', override: '15px', },
          { key: 'maxHeight', override: 'calc(100% - 75px)', },
        ],
      },
      {
        element: elements.horizontalSlider,
        styles: [
          { key: 'bottom', override: '15px', },
          { key: 'left', override: '27px', },
          { key: 'position', override: 'fixed', },
          { key: 'zIndex', override: zIndex + 1, },
          { key: 'maxWidth', override: 'calc(100% - 54px)', },
        ],
      },
    ];
  }

  function updateDimensions() {
    const dim = getMaxDim();
    const canvasStylesGroup = styles.find(({element}) => element === elements.canvas);
    canvasStylesGroup.styles.forEach(style => {
      style.override = `${dim}px`;
    });
  }

  function addButtons() {
    addEnterFullscreenButton();
    addExitFullscreenButton();
    addZoomButton();
  }

  function addEnterFullscreenButton() {
    const gcode = document.getElementById('gcode');
    for (const p of gcode.getElementsByTagName('p')) {
      if (p.getElementsByTagName('button').length && p.textContent.includes('Reset viewport')) {
        const fs = document.createElement('button');
        fs.classList.add('btn');
        fs.classList.add('btn-block');
        fs.textContent = 'Fullscreen';
        fs.addEventListener('click', () => {
          const newUrl = new URL(location);
          newUrl.searchParams.set('fullscreen', 'true');
          history.replaceState({}, '', newUrl);
          enableFullscreenStyles(true);
        });
        p.prepend(fs);
        break;
      }
    }
  }

  function addExitFullscreenButton() {
    elements.exitFullscreen.style.display = 'none';
    elements.exitFullscreen.style.position = 'fixed';
    elements.exitFullscreen.style.top = '45px';
    elements.exitFullscreen.style.left = '15px';
    elements.exitFullscreen.style.zIndex = zIndex + 1;
    elements.exitFullscreen.style.color = 'black';
    elements.exitFullscreen.textContent = 'Exit Fullscreen';
    elements.exitFullscreen.addEventListener('click', () => {
      info('Exiting fullscreen');
      disableFullscreenStyles();
      const newUrl = new URL(location);
      newUrl.searchParams.delete('fullscreen');
      history.replaceState({}, '', newUrl);
    });

    document.body.appendChild(elements.exitFullscreen);
  }

  function addZoomButton() {
    elements.newZoomBtn.style.display = 'none';
    elements.newZoomBtn.style.position = 'fixed';
    elements.newZoomBtn.style.top = '15px';
    elements.newZoomBtn.style.left = '15px';
    elements.newZoomBtn.style.zIndex = zIndex + 1;
    elements.newZoomBtn.style.color = 'black';
    elements.newZoomBtn.textContent = 'Zoom and Center';

    // event listener will be added when initialized
    document.body.appendChild(elements.newZoomBtn);
  }

  function stashOriginalStyles() {
    styles.forEach(styleGroup => {
      styleGroup.styles.forEach((stash) => {
        stash.original = styleGroup.element.style[stash.key];
      });
    });
  }

  function enableFullscreenIfQueryPresent(shouldAnnounce) {
    // only enable fullscreen if query string is set and hash matches
    const url = new URL(window.location);
    if (url.searchParams.get('fullscreen') === 'true' && url.hash === '#gcode') {
      enableFullscreenStyles(shouldAnnounce);
    }
  }

  function enableFullscreenStyles(shouldAnnounce) {
    if (shouldAnnounce) {
      info('Entering fullscreen!');
    }
    // activate all overrides
    styles.forEach(styleGroup => {
      styleGroup.styles.forEach((stash) => {
        styleGroup.element.style[stash.key] = stash.override;
      });
    });

    // show the additional buttons
    elements.exitFullscreen.style.display = 'block';
    elements.newZoomBtn.style.display = 'block';
  }

  function disableFullscreenStyles() {
    // hide the additional buttons
    elements.exitFullscreen.style.display = 'none';
    elements.newZoomBtn.style.display = 'none';

    // activate all original styles
    styles.forEach(styleGroup => {
      styleGroup.styles.forEach((stash) => {
        styleGroup.element.style[stash.key] = stash.original;
      });
    });
  }

  function getMaxDim() {
    // determine the maximum dimensions
    const vw = Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0);
    const vh = Math.max(document.documentElement.clientHeight || 0, window.innerHeight || 0);
    const dim = Math.min(vw, vh);

    return dim;
  }

  function info() {
    console.log('[FULLSCREEN_GCODE_VISUALIZER]', ...arguments);
  }

  function err() {
    console.error('[FULLSCREEN_GCODE_VISUALIZER]', ...arguments);
  }
})();
Looks like this