Some Work done

This commit is contained in:
Marc 2015-04-18 11:45:57 +02:00
parent e985c083c5
commit e48e196e4e
10 changed files with 567 additions and 166 deletions

6
babel.cfg Normal file
View File

@ -0,0 +1,6 @@
[python: */**.py]
[jinja2: */**.jinja2]
extensions=jinja2.ext.autoescape, jinja2.ext.with_
[javascript: */**.js]
extract_messages = gettext, ngettext

View File

@ -5,17 +5,30 @@ __author__ = "Marc Hannappel <sunpack@web.de>"
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
__copyright__ = "Copyright (C) 2014 The OctoPrint Project - Released under terms of the AGPLv3 License"
from octoprint.settings import settings
import octoprint.plugin
class CustomControlPlugin(octoprint.plugin.SettingsPlugin,
octoprint.plugin.TemplatePlugin,
octoprint.plugin.AssetPlugin):
def get_settings_defaults(self):
return dict();
return dict(
controls = []
)
def on_settings_save(self, data):
s = settings()
s.set(["controls"], data["controls"])
def get_assets(self):
return {
"js": ["js/customControl.js"],
"js": [
"js/customControl.js",
"js/containerDialog.js",
],
"css": ["css/customControls.css"]
}
# If you want your plugin to be registered within OctoPrint under a different name than what you defined in setup.py

View File

@ -0,0 +1,3 @@
#customControls .custom_section_horizontal{display:inline-block}
#customControls .custom_section_vertical_section{min-width:15px; min-height:15px; margin: 8px 8px; padding: 8px 8px; border:1px dashed #000000}
#customControls .custom_section_horizontal_section{float:left; min-width:15px; min-height:15px; margin: 8px 8px; padding: 8px 8px; border:1px dashed #000000}

View File

@ -0,0 +1,33 @@
$(function () {
function ContainerDialogViewModel(parameters) {
var self = this;
self.element = ko.observable();
self.layouts = ko.observableArray([
{ name: gettext("Vertical"), key: "vertical" },
{ name: gettext("Horizontal"), key: "horizontal" },
{ name: gettext("Horizontal grid"), key: "horizontal_grid" }
]);
self.show = function (f) {
var dialog = $("#containerDialog");
var primarybtn = $('.btn-primary', dialog);
primarybtn.unbind('click').bind('click', f);
dialog.modal({
show: 'true',
backdrop: 'static',
keyboard: false
});
}
}
// view model class, parameters for constructor, container to bind to
OCTOPRINT_VIEWMODELS.push([
ContainerDialogViewModel,
[],
"#containerDialog"
]);
});

View File

@ -1,141 +1,109 @@
$(function () {
$.fn.isChildOf = function (element) {
return $(element).has(this).length > 0;
}
// from http://jsfiddle.net/KyleMit/X9tgY/
$.fn.contextMenu = function (settings) {
return this.each(function () {
// Open context menu
$(this).on("contextmenu", function (e) {
// return native menu if pressing control
if (e.ctrlKey) return;
$(settings.menuSelector)
.data("invokedOn", $(e.target))
.show()
.css({
position: "absolute",
left: getMenuPosition(e.clientX, 'width', 'scrollLeft'),
top: getMenuPosition(e.clientY, 'height', 'scrollTop'),
"z-index": 9999
}).off('click')
.on('click', function (e) {
if (e.target.tagName.toLowerCase() == "input")
return;
$(this).hide();
var $invokedOn = $(this).data("invokedOn");
var $selectedMenu = $(e.target);
settings.menuSelected.call(this, $invokedOn, $selectedMenu);
});
return false;
});
//make sure menu closes on any click
$(document).click(function () {
$(settings.menuSelector).hide();
});
});
function getMenuPosition(mouse, direction, scrollDir) {
var win = $(window)[direction](),
scroll = $(window)[scrollDir](),
menu = $(settings.menuSelector)[direction](),
position = mouse + scroll;
// opening menu would pass the side of the page
if (mouse + menu > win && menu < mouse)
position -= menu;
return position;
}
};
ko.bindingHandlers.contextMenu = {
init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
var val = ko.utils.unwrapObservable(valueAccessor());
$(element).contextMenu(val);
},
update: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
var val = ko.utils.unwrapObservable(valueAccessor());
$(element).contextMenu(val);
}
}
function CustomControlViewModel(parameters) {
var self = this;
self.loginState = parameters[0];
self.settingsViewModel = parameters[1];
self.controlViewModel = parameters[2];
self.containerDialogViewModel = parameters[3];
self.popup = undefined;
self.controls = ko.observableArray([]);
self.controlsFromServer = [];
self.additionalControls = [];
self.staticID = 0;
self.onBeforeBinding = function () {
self.settings = self.settingsViewModel.settings;
self.rerenderControls();
self.staticID = 0;
self._showPopup = function (options, eventListeners) {
if (self.popup !== undefined) {
self.popup.remove();
}
self.popup = new PNotify(options);
if (eventListeners) {
var popupObj = self.popup.get();
_.each(eventListeners, function (value, key) {
popupObj.on(key, value);
})
}
};
self.onAllBound = function (allViewModels) {
var additionalControls = [];
_.each(allViewModels, function (viewModel) {
if (viewModel.hasOwnProperty("getAdditionalControls")) {
additionalControls = additionalControls.concat(viewModel.getAdditionalControls());
self.onStartup = function () {
self.requestData();
};
self.requestData = function () {
$.ajax({
url: API_BASEURL + "printer/command/custom",
method: "GET",
dataType: "json",
success: function (response) {
self._fromResponse(response);
}
});
if (additionalControls.length > 0) {
self.additionalControls = additionalControls;
self.rerenderControls();
}
};
self._fromResponse = function (response) {
self.controlsFromServer = response.controls;
self.rerenderControls();
};
self.rerenderControls = function () {
self.staticID = 0;
var allControls = self.controlsFromServer.concat(self.additionalControls);
self.controls(self._processControls(allControls))
self.controls(self._processControls(undefined, self.controlsFromServer))
};
self._processControls = function (controls) {
self._processControls = function (parent, controls) {
for (var i = 0; i < controls.length; i++) {
controls[i] = self._processControl(controls[i]);
controls[i] = self._processControl(parent, controls[i]);
}
return controls;
};
self._processControl = function (control) {
if (control.hasOwnProperty("template") && control.hasOwnProperty("key") && control.hasOwnProperty("template_key") && !control.hasOwnProperty("output")) {
self._processControl = function (parent, control) {
control.id = ko.observable("settingsCustomControl_id" + self.staticID++);
control.parent = parent;
if (control.hasOwnProperty("template") && control.hasOwnProperty("key") && control.hasOwnProperty("template_key") && !control.hasOwnProperty("output"))
control.output = ko.observable("");
}
if (control.hasOwnProperty("children")) {
if (control.processed)
control.children = ko.observableArray(self._processControls(control.children()));
else
control.children = ko.observableArray(self._processControls(control.children));
if (control.processed) {
control.children(self._processControls(control, control.children()));
if (control.hasOwnProperty("layout") && !(control.layout() == "vertical" || control.layout() == "horizontal" || control.layout() == "horizontal_grid"))
control.layout("vertical");
else if (!control.hasOwnProperty("layout"))
control.layout = ko.observable("vertical");
}
else {
control.name = ko.observable(control.name);
control.children = ko.observableArray(self._processControls(control, control.children));
if (!control.hasOwnProperty("layout") || !(control.layout == "vertical" || control.layout == "horizontal" || control.layout == "horizontal_grid"))
control.layout = ko.observable("vertical");
else
control.layout = ko.observable(control.layout);
if (!control.hasOwnProperty("layout") || !(control.layout == "vertical" || control.layout == "horizontal")) {
control.layout = "vertical";
control.width = ko.observable(control.hasOwnProperty("width") ? control.width : "2");
control.offset = ko.observable(control.hasOwnProperty("offset") ? control.offset : "");
}
}
if (control.hasOwnProperty("input")) {
for (var i = 0; i < control.input.length; i++) {
control.input[i].value = ko.observable(control.input[i].default);
if (!control.input[i].hasOwnProperty("slider")) {
control.input[i].slider = false;
if (!control.processed) {
control.input[i].value = ko.observable(control.input[i].default);
control.input[i].default = ko.observable(control.input[i].default);
}
if (control.processed)
control.input[i].value(control.input[i].default());
if (!control.input[i].hasOwnProperty("slider"))
control.input[i].slider = ko.observable(false);
else if (!control.processed)
control.input[i].slider = ko.observable(control.input[i].slider);
}
}
@ -144,25 +112,24 @@
js = control.javascript;
// if js is a function everything's fine already, but if it's a string we need to eval that first
if (!_.isFunction(js)) {
/*if (!_.isFunction(js)) {
control.javascript = function (data) {
eval(js);
};
}
}*/
}
if (control.hasOwnProperty("enabled")) {
js = control.enabled;
// if js is a function everything's fine already, but if it's a string we need to eval that first
if (!_.isFunction(js)) {
/*if (!_.isFunction(js)) {
control.enabled = function (data) {
return eval(js);
}
}
}*/
}
control.id = "settingsCustomControl_id" + self.staticID++;
control.processed = true;
return control;
};
@ -174,11 +141,23 @@
return "settingsCustomControls_controlTemplate";
}
}
self.rowCss = function (customControl) {
var span = "span2";
var offset = "";
if (customControl.hasOwnProperty("width") && customControl.width() != "") {
span = "span" + customControl.width();
}
if (customControl.hasOwnProperty("offset") && customControl.offset() != "") {
offset = "offset" + customControl.offset();
}
return span + " " + offset;
};
self.searchElement = function (list, id) {
for (var i = 0; i < list.length; i++)
{
if (list[i].id == id)
if (list[i].id() == id)
return list[i];
if (list[i].hasOwnProperty("children")) {
@ -191,25 +170,114 @@
return undefined;
}
self.controlContextMenu = function (invokedOn, selectedMenu)
self.controlContextMenu = function (invokedOn, contextParent, selectedMenu)
{
if (selectedMenu.attr('cmd') == "createContainer") {
if (invokedOn.attr('id') == "base") {
// TODO: make a create dialog
self.controlsFromServer.push({ children: [], id: "settingsCustomControl_id" + self.staticID++ });
self.rerenderControls();
switch (selectedMenu.attr('cmd')) {
case "createContainer": {
if (invokedOn.attr('id') == "base") {
self.containerDialogViewModel.element({
name: undefined,
children:[],
layout: "vertical",
width: "2",
offset: ""
});
self.containerDialogViewModel.show(function (e) {
self.controlsFromServer.push(self.containerDialogViewModel.element());
self.rerenderControls();
});
}
else {
var parentElement = self.searchElement(self.controlsFromServer, contextParent.attr('id'));
if (parentElement == undefined) {
self._showPopup({
title: gettext("Something went wrong while creating the new Element"),
type:"error"
});
return;
}
self.containerDialogViewModel.element({
parent: parentElement,
name: undefined,
children: [],
layout: "vertical",
width: "2",
offset: ""
});
self.containerDialogViewModel.show(function (e) {
parentElement.children.push(self._processControl(parentElement, self.containerDialogViewModel.element()));
});
}
break;
}
else {
var parentElement = self.searchElement(self.controlsFromServer, invokedOn.attr('id'));
if (parentElement == undefined)
{
// TODO: make an Warning dialog
alert("Something went wrong while creating the new Element");
case "editContainer": {
var element = self.searchElement(self.controlsFromServer, contextParent.attr('id'));
if (element == undefined) {
self._showPopup({
title: gettext("Something went wrong while creating the new Element"),
type: "error"
});
return;
}
// TODO: make a create dialog
parentElement.children.push(self._processControl({ children: [], id: "settingsCustomControl_id" + self.staticID++ }));
var dialog = $('#containerDialog');
var primarybtn = $('.btn-primary', dialog);
var el = {
parent: element.parent,
name: ko.observable(element.name()),
layout: ko.observable(element.layout()),
width: ko.observable("2"),
offset: ko.observable("")
};
if (element.hasOwnProperty("width"))
el.width(element.width());
if (element.hasOwnProperty("offset"))
el.offset(element.offset());
self.containerDialogViewModel.element(el);
primarybtn.unbind('click').bind('click', function (e) {
var ele = self.containerDialogViewModel.element();
element.name(ele.name());
element.layout(ele.layout());
if (ele.parent.layout() == "horizontal_grid") {
if (ele.width() != undefined)
element.width(ele.width());
if (ele.offset() != undefined)
element.offset(ele.offset());
}
});
dialog.modal({
show: 'true',
backdrop: 'static',
keyboard: false
});
break;
}
case "deleteContainer": {
var element = self.searchElement(self.controlsFromServer, contextParent.attr('id'));
if (element == undefined) {
self._showPopup({
title: gettext("Something went wrong while creating the new Element"),
type: "error"
});
return;
}
showConfirmationDialog("", function (e) {
if (element.parent != undefined)
element.parent.children.remove(element);
else {
self.controlsFromServer = _.without(self.controlsFromServer, element);
self.rerenderControls();
}
});
}
}
}
@ -217,12 +285,36 @@
self.editStyle = function (type) {
}
self.recursiveDeleteProperties = function (list) {
for (var i = 0; i < list.length; i++) {
if (list[i].parent && list[i].parent.hasOwnProperty("layout") && list[i].parent.layout() != "horizontal_grid")
{
delete list[i].width;
delete list[i].offset;
}
delete list[i].id;
delete list[i].parent;
delete list[i].processed;
if (list[i].hasOwnProperty("children"))
self.recursiveDeleteProperties(list[i].children());
}
}
self.onSettingsBeforeSave = function () {
self.recursiveDeleteProperties(self.controlsFromServer);
self.settingsViewModel.settings.plugins.octoprint_customControl.controls = self.controlsFromServer;
}
self.onSettingsAfterSave = function () {
self.controlViewModel.requestData();
self.requestData();
}
}
// view model class, parameters for constructor, container to bind to
OCTOPRINT_VIEWMODELS.push([
CustomControlViewModel,
["loginStateViewModel", "settingsViewModel", "controlViewModel"],
"#settings_plugin_customControl"
["loginStateViewModel", "settingsViewModel", "controlViewModel", "containerDialogViewModel"],
"#settings_plugin_octoprint_customControl"
]);
});

View File

@ -1,11 +0,0 @@
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title></title>
</head>
<body>
</body>
</html>

View File

@ -0,0 +1,39 @@
<div id="containerDialog" class="modal hide">
<div class="modal-header">
<h3>{{ _('Container Dialog') }}</h3>
</div>
<div class="modal-body">
<form class="form-horizontal" data-bind="with: element">
<div class="control-group">
<label class="control-label">{{ _('Name') }}</label>
<div class="controls">
<input data-bind="value: name, valueAllowUnset: true"></input>
</div>
</div>
<div class="control-group">
<label class="control-label">{{ _('Layout') }}</label>
<div class="controls">
<select data-bind="options: $parent.layouts, optionsText: 'name', optionsValue: 'key', value: layout, valueAllowUnset: false"></select>
</div>
</div>
<!-- ko if: $data.parent && $data.parent.layout && $data.parent.layout() == 'horizontal_grid' -->
<div class="control-group">
<label class="control-label">{{ _('Width') }}</label>
<div class="controls">
<input data-bind="value: width, valueAllowUnset: false"></input>
</div>
</div>
<div class="control-group">
<label class="control-label">{{ _('Offset') }}</label>
<div class="controls">
<input data-bind="value: offset, valueAllowUnset: true"></input>
</div>
</div>
<!-- /ko -->
</form>
</div>
<div class="modal-footer">
<a href="#" class="btn" data-dismiss="modal">{{ _('Cancel') }}</a>
<a href="#" class="btn btn-primary" data-dismiss="modal" aria-hidden="true">{{ _('Confirm') }}</a>
</div>
</div>

View File

@ -26,14 +26,6 @@
<li class="btn-danger">
<a href="#" cmd="editStyle">Danger</a>
</li>
<li class="dropdown-submenu">
<a href="#">Custom</a>
<ul class="dropdown-menu">
<li><input type="text" placeholder="Background Color 1" cmd="editStyle"></input></li>
<li><input type="text" placeholder="Background Color 2" cmd="editStyle"></input></li>
<li><input type="text" placeholder="Foreground Color" cmd="editStyle"></input></li>
</ul>
</li>
</ul>
</li>
</ul>
@ -46,35 +38,26 @@
</ul>
</li>
<li class="divider"></li>
<li><a href="#" cmd="editCommand">Edit Container</a></li>
<li><a href="#" cmd="deleteCommand">Delete Container</a></li>
<li class="divider"></li>
<li class="dropdown-submenu">
<a href="#">Style</a>
<ul class="dropdown-menu">
<li><input type="text" placeholder="Background Color 1" cmd="editStyle"></input></li>
<li><input type="text" placeholder="Background Color 2" cmd="editStyle"></input></li>
<li><input type="text" placeholder="Foreground Color" cmd="editStyle"></input></li>
</ul>
</li>
<li><a href="#" cmd="editContainer">Edit Container</a></li>
<li><a href="#" cmd="deleteContainer">Delete Container</a></li>
</ul>
<!-- Templates for custom controls -->
<script type="text/html" id="settingsCustomControls_containerTemplate">
<div style="min-height:15px; margin: 8px 16px; padding: 8px 16px; border:1px dashed #000000" data-bind="contextMenu: { menuSelector: '#containerContextMenu', menuSelected: $root.controlContextMenu }, attr: { 'id': id }">
<div class="custom_container" data-bind="contextMenu: { menuSelector: '#containerContextMenu', menuSelected: $root.controlContextMenu }, attr: { 'id': id }, css: { 'custom_section_vertical_section': !($parent && $parent.layout && $parent.layout() == 'horizontal'), 'custom_section_horizontal_section': $parent && $parent.layout && $parent.layout() == 'horizontal' }">
<!-- ko if: name -->
<h1 data-bind="text: name"></h1>
<!-- /ko -->
<!-- ko if: layout == 'vertical' -->
<!-- ko if: layout() == 'vertical' -->
<div class="custom_section custom_section_vertical" data-bind="template: { name: $root.displayMode, foreach: children }"></div>
<!-- /ko -->
<!-- ko if: layout == 'horizontal' -->
<!-- ko if: layout() == 'horizontal' -->
<div class="custom_section custom_section_horizontal" data-bind="template: { name: $root.displayMode, foreach: children }"></div>
<!-- /ko -->
<!-- ko if: layout == 'horizontal_grid' -->
<!-- ko if: layout() == 'horizontal_grid' -->
<div class="row-fluid custom_section custom_section_horizontal_grid">
<!-- ko foreach: children -->
<div data-bind="template: { name: $root.displayMode }, css: $root.rowCss($data)"></div>
@ -84,7 +67,7 @@
</div>
</script>
<script type="text/html" id="settingsCustomControls_controlTemplate">
<form class="form-inline custom_control" style="min-height:15px; border:1px solid #000000" data-bind="contextMenu: { menuSelector: '#commandContextMenu', menuSelected: $root.controlContextMenu }, attr: { 'id': id }">
<form class="form-inline control-tab custom_control" style="min-height:15px; border:1px solid #000000" data-bind="contextMenu: { menuSelector: '#commandContextMenu', menuSelected: $root.controlContextMenu }, attr: { 'id': id }">
<!-- ko template: { name: 'customControls_controlTemplate_input', data: $data, if: $data.hasOwnProperty('input') } --><!-- /ko -->
<!-- ko template: { name: 'customControls_controlTemplate_command', data: $data, if: $data.hasOwnProperty('command') || $data.hasOwnProperty('commands') || $data.hasOwnProperty('script') || $data.hasOwnProperty('javascript') } --><!-- /ko -->
<!-- ko template: { name: 'customControls_controlTemplate_output', data: $data, if: $data.hasOwnProperty('output') } --><!-- /ko -->

View File

@ -1,2 +1,10 @@
OctoPrint
###
# This file is only here to make sure that something like
#
# pip install -e .[develop]
#
# works as expected. Requirements can be found in setup.py in the
# variables INSTALL_REQUIRES and EXTRA_REQUIRES.
###
.

247
setup.py
View File

@ -1,5 +1,6 @@
# coding=utf-8
import setuptools
from setuptools import setup, Command
import os
########################################################################################################################
@ -40,6 +41,28 @@ plugin_additional_data = []
########################################################################################################################
# I18N setup
I18N_MAPPING_FILE = "babel.cfg"
I18N_DOMAIN = "messages"
I18N_INPUT_DIRS = "."
I18N_OUTPUT_DIR_PY = os.path.join(plugin_package, "translations")
I18N_OUTPUT_DIR_JS = os.path.join(plugin_package, "static", "js", "i18n")
I18N_POT_FILE = os.path.join(I18N_OUTPUT_DIR_PY, "messages.pot")
# Requirements for out application
INSTALL_REQUIRES = [
"OctoPrint"
]
# Requirements for developing etc
EXTRA_REQUIRES = dict(
develop=[
# Translation dependencies
"babel",
"po2json"
]
)
def package_data_dirs(source, sub_folders):
import os
dirs = []
@ -56,10 +79,208 @@ def package_data_dirs(source, sub_folders):
return dirs
def _recursively_handle_files(directory, file_matcher, folder_handler=None, file_handler=None):
applied_handler = False
def requirements(filename):
return filter(lambda line: line and not line.startswith("#"), map(lambda line: line.strip(), open(filename).read().split("\n")))
for filename in os.listdir(directory):
path = os.path.join(directory, filename)
if file_handler is not None and file_matcher(filename):
file_handler(path)
applied_handler = True
elif os.path.isdir(path):
sub_applied_handler = _recursively_handle_files(path, file_matcher, folder_handler=folder_handler, file_handler=file_handler)
if sub_applied_handler:
applied_handler = True
if folder_handler is not None:
folder_handler(path, sub_applied_handler)
return applied_handler
class CleanCommand(Command):
description = "clean build artifacts"
user_options = []
boolean_options = []
def initialize_options(self):
pass
def finalize_options(self):
pass
def run(self):
import shutil
import glob
# build folder
if os.path.exists('build'):
print "Deleting build directory"
shutil.rmtree('build')
# eggs
eggs = glob.glob("*.egg-info")
for egg in eggs:
print "Deleting %s directory" % egg
shutil.rmtree(egg)
# pyc files
def delete_folder_if_empty(path, applied_handler):
if not applied_handler:
return
if len(os.listdir(path)) == 0:
shutil.rmtree(path)
print "Deleted %s since it was empty" % path
def delete_file(path):
os.remove(path)
print "Deleted %s" % path
import fnmatch
_recursively_handle_files(
os.path.abspath(plugin_package),
lambda name: fnmatch.fnmatch(name.lower(), "*.pyc"),
folder_handler=delete_folder_if_empty,
file_handler=delete_file
)
# pyc files
def delete_folder_if_empty(path, applied_handler):
if not applied_handler:
return
if len(os.listdir(path)) == 0:
shutil.rmtree(path)
print "Deleted %s since it was empty" % path
def delete_file(path):
os.remove(path)
print "Deleted %s" % path
import fnmatch
_recursively_handle_files(
os.path.abspath(plugin_package),
lambda name: fnmatch.fnmatch(name.lower(), "*.pyc"),
folder_handler=delete_folder_if_empty,
file_handler=delete_file
)
class NewTranslation(Command):
description = "create a new translation"
user_options = [
('locale=', 'l', 'locale for the new translation'),
]
boolean_options = []
def __init__(self, dist, **kw):
from babel.messages import frontend as babel
self.babel_init_messages = babel.init_catalog(dist)
Command.__init__(self, dist, **kw)
def initialize_options(self):
self.locale = None
self.babel_init_messages.initialize_options()
def finalize_options(self):
self.babel_init_messages.locale = self.locale
self.babel_init_messages.input_file = I18N_POT_FILE
self.babel_init_messages.output_dir = I18N_OUTPUT_DIR_PY
self.babel_init_messages.finalize_options()
def run(self):
self.babel_init_messages.run()
class ExtractTranslation(Command):
description = "extract translations"
user_options = []
boolean_options = []
def __init__(self, dist, **kw):
from babel.messages import frontend as babel
self.babel_extract_messages = babel.extract_messages(dist)
Command.__init__(self, dist, **kw)
def initialize_options(self):
self.babel_extract_messages.initialize_options()
def finalize_options(self):
self.babel_extract_messages.mapping_file = I18N_MAPPING_FILE
self.babel_extract_messages.output_file = I18N_POT_FILE
self.babel_extract_messages.input_dirs = I18N_INPUT_DIRS
self.babel_extract_messages.msgid_bugs_address = plugin_author_email
self.babel_extract_messages.copyright_holder = plugin_author
self.babel_extract_messages.finalize_options()
def run(self):
self.babel_extract_messages.run()
class RefreshTranslation(Command):
description = "refresh translations"
user_options = [
('locale=', 'l', 'locale for the translation to refresh'),
]
boolean_options = []
def __init__(self, dist, **kw):
from babel.messages import frontend as babel
self.babel_extract_messages = babel.extract_messages(dist)
self.babel_update_messages = babel.update_catalog(dist)
Command.__init__(self, dist, **kw)
def initialize_options(self):
self.locale = None
self.babel_extract_messages.initialize_options()
self.babel_update_messages.initialize_options()
def finalize_options(self):
self.babel_extract_messages.mapping_file = I18N_MAPPING_FILE
self.babel_extract_messages.output_file = I18N_POT_FILE
self.babel_extract_messages.input_dirs = I18N_INPUT_DIRS
self.babel_extract_messages.msgid_bugs_address = plugin_author_email
self.babel_extract_messages.copyright_holder = plugin_author
self.babel_extract_messages.finalize_options()
self.babel_update_messages.input_file = I18N_POT_FILE
self.babel_update_messages.output_dir = I18N_OUTPUT_DIR_PY
self.babel_update_messages.locale = self.locale
def run(self):
self.babel_extract_messages.run()
self.babel_update_messages.run()
class CompileTranslation(Command):
description = "compile translations"
user_options = []
boolean_options = []
def __init__(self, dist, **kw):
from babel.messages import frontend as babel
self.babel_compile_messages = babel.compile_catalog(dist)
Command.__init__(self, dist, **kw)
def initialize_options(self):
self.babel_compile_messages.initialize_options()
def finalize_options(self):
self.babel_compile_messages.directory = I18N_OUTPUT_DIR_PY
def run(self):
self.babel_compile_messages.run()
import po2json
for lang_code in os.listdir(I18N_OUTPUT_DIR_PY):
full_path = os.path.join(I18N_OUTPUT_DIR_PY, lang_code)
if os.path.isdir(full_path):
client_po_dir = os.path.join(full_path, "LC_MESSAGES")
po2json.update_js_file(
"%s/%s.po" % (client_po_dir, I18N_DOMAIN),
lang_code,
I18N_OUTPUT_DIR_JS,
I18N_DOMAIN
)
def params():
# Our metadata, as defined above
@ -70,6 +291,14 @@ def params():
author_email = plugin_author_email
url = plugin_url
license = plugin_license
cmdclass = {
'clean': CleanCommand,
'babel_new': NewTranslation,
'babel_extract': ExtractTranslation,
'babel_refresh': RefreshTranslation,
'babel_compile': CompileTranslation
};
# we only have our plugin package to install
packages = [plugin_package]
@ -82,8 +311,14 @@ def params():
# this plugin is not zip_safe.
zip_safe = False
# Read the requirements from our requirements.txt file
install_requires = requirements("requirements.txt")
install_requires = INSTALL_REQUIRES
extras_require = EXTRA_REQUIRES
if os.environ.get('READTHEDOCS', None) == 'True':
# we can't tell read the docs to please perform a pip install -e .[develop], so we help
# it a bit here by explicitly adding the development dependencies, which include our
# documentation dependencies
install_requires = install_requires + extras_require['develop']
# Hook the plugin into the "octoprint.plugin" entry point, mapping the plugin_identifier to the plugin_package.
# That way OctoPrint will be able to find the plugin and load it.
@ -93,4 +328,4 @@ def params():
return locals()
setuptools.setup(**params())
setup(**params())