Add Input Shaper panel

Original panel by Mikko Gozalo

Modified layour to fit 16:9 screens
Auto-home
Ability to measure X and Y independently
Get the current active values not the values from the config
Ask for save
Other minor tweaks

close #301

Co-authored-by: alfrix <alfredomonclus@gmail.com>
This commit is contained in:
Mikko Gozalo 2021-10-09 20:51:18 +08:00 committed by Alfredo Monclus
parent 7e988b3f35
commit fa0d696120
3 changed files with 207 additions and 2 deletions

View File

@ -171,7 +171,7 @@ icon: complete
method: printer.gcode.script
params: {"script":"SAVE_CONFIG"}
confirm:
{{ gettext('Save configuration') }}
{{ gettext('Save configuration?') }}
{{ gettext('Klipper will reboot') }}
@ -180,6 +180,12 @@ name: {{ gettext('Settings') }}
icon: settings
panel: settings
[menu __main config input_shaper]
name: {{ gettext('Input Shaper') }}
icon: move
panel: input_shaper
enable: {{ printer.input_shaper }}
[menu __print]
name: {{ gettext('Print Control') }}

View File

@ -264,7 +264,8 @@ class Printer:
"count": len(self.get_power_devices())
},
"probe": self.config_section_exists("probe"),
"firmware_retraction": self.config_section_exists("firmware_retraction")
"firmware_retraction": self.config_section_exists("firmware_retraction"),
"input_shaper": self.config_section_exists("input_shaper")
}
}

198
panels/input_shaper.py Normal file
View File

@ -0,0 +1,198 @@
import gi
import re
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, Pango
from ks_includes.KlippyGcodes import KlippyGcodes
from ks_includes.screen_panel import ScreenPanel
def create_panel(*args):
return InputShaperPanel(*args)
# X and Y frequencies
XY_FREQ = [
{'name': 'X', 'config': 'shaper_freq_x', 'min': 0, 'max': 133},
{'name': 'Y', 'config': 'shaper_freq_y', 'min': 0, 'max': 133},
]
SHAPERS = ['zv', 'mzv', 'zvd', 'ei', '2hump_ei', '3hump_ei']
class InputShaperPanel(ScreenPanel):
def initialize(self, panel_name):
_ = self.lang.gettext
self.has_sensor = False
self.calibrating_axis = None
auto_calibration_label = Gtk.Label()
auto_calibration_label.set_markup('<big><b>Auto Calibration</b></big>')
auto_calibration_label.set_hexpand(True)
self.calibrate_btn = self._gtk.ButtonImage("move", _('Finding ADXL'), "color1", word_wrap=False)
self.calibrate_btn.connect("clicked", self.on_popover_clicked)
self.calibrate_btn.set_sensitive(False)
auto_grid = Gtk.Grid()
auto_grid.attach(auto_calibration_label, 0, 0, 1, 1)
auto_grid.attach(self.calibrate_btn, 1, 0, 1, 1)
manual_calibration_label = Gtk.Label()
manual_calibration_label.set_markup('<big><b>Manual Calibration</b></big>')
manual_calibration_label.set_vexpand(True)
disclaimer = Gtk.Label()
disclaimer.set_markup('<small>NOTE: Edit your printer.cfg to save manual calibration changes.</small>')
disclaimer.set_line_wrap(True)
disclaimer.set_halign(Gtk.Align.CENTER)
input_grid = Gtk.Grid()
input_grid.attach(manual_calibration_label, 0, 0, 3, 1)
input_grid.attach(disclaimer, 0, 1, 3, 1)
self.freq_xy_adj = {}
self.freq_xy_combo = {}
for i, dim_freq in enumerate(XY_FREQ):
axis_lbl = Gtk.Label()
axis_lbl.set_markup("<b>{}</b>".format(dim_freq['name']))
axis_lbl.set_hexpand(False)
axis_lbl.set_vexpand(True)
axis_lbl.set_halign(Gtk.Align.START)
axis_lbl.set_valign(Gtk.Align.CENTER)
axis_lbl.set_line_wrap(True)
self.freq_xy_adj[dim_freq['config']] = Gtk.Adjustment(0, dim_freq['min'], dim_freq['max'], 0.1)
scale = Gtk.Scale(orientation=Gtk.Orientation.HORIZONTAL, adjustment=self.freq_xy_adj[dim_freq['config']])
scale.set_digits(1)
scale.set_hexpand(True)
scale.set_valign(Gtk.Align.END)
scale.set_has_origin(True)
scale.get_style_context().add_class("option_slider")
scale.connect("button-release-event", self.set_opt_value, dim_freq['config'])
shaper_slug = dim_freq['config'].replace('_freq_', '_type_')
self.freq_xy_combo[shaper_slug] = Gtk.ComboBoxText()
for shaper in SHAPERS:
self.freq_xy_combo[shaper_slug].append(shaper, shaper)
self.freq_xy_combo[shaper_slug].set_active(0)
input_grid.attach(axis_lbl, 0, i + 2, 1, 1)
input_grid.attach(scale, 1, i + 2, 1, 1)
input_grid.attach(self.freq_xy_combo[shaper_slug], 2, i + 2, 1, 1)
self.status = Gtk.Label("")
self.status.set_hexpand(True)
self.status.set_vexpand(False)
self.status.set_halign(Gtk.Align.START)
self.status.set_ellipsize(Pango.EllipsizeMode.END)
box = Gtk.VBox()
box.add(auto_grid)
box.add(input_grid)
box.add(self.status)
self.content.add(box)
pobox = Gtk.VBox()
test_x = self._gtk.Button(_("Measure X"))
test_x.connect("clicked", self.start_calibration, "x")
pobox.pack_start(test_x, True, True, 5)
test_y = self._gtk.Button(_("Measure Y"))
test_y.connect("clicked", self.start_calibration, "y")
pobox.pack_start(test_y, True, True, 5)
test_both = self._gtk.Button(_("Measure Both"))
test_both.connect("clicked", self.start_calibration, "both")
pobox.pack_start(test_both, True, True, 5)
self.labels['popover'] = Gtk.Popover()
self.labels['popover'].add(pobox)
self.labels['popover'].set_position(Gtk.PositionType.LEFT)
def on_popover_clicked(self, widget):
self.labels['popover'].set_relative_to(widget)
self.labels['popover'].show_all()
def start_calibration(self, widget, method):
self.labels['popover'].popdown()
if self._screen.printer.get_stat("toolhead", "homed_axes") != "xyz":
self._screen._ws.klippy.gcode_script(KlippyGcodes.HOME)
self.calibrating_axis = method
if method == "x":
self._screen._ws.klippy.gcode_script('SHAPER_CALIBRATE AXIS=X')
if method == "y":
self._screen._ws.klippy.gcode_script('SHAPER_CALIBRATE AXIS=Y')
if method == "both":
self._screen._ws.klippy.gcode_script('SHAPER_CALIBRATE')
self.calibrate_btn.set_label(self.lang.gettext('Calibrating...'))
self.calibrate_btn.set_sensitive(False)
def set_opt_value(self, widget, opt, *args):
shaper_freq_x = self.freq_xy_adj['shaper_freq_x'].get_value()
shaper_freq_y = self.freq_xy_adj['shaper_freq_y'].get_value()
shaper_type_x = self.freq_xy_combo['shaper_type_x'].get_active_text()
shaper_type_y = self.freq_xy_combo['shaper_type_y'].get_active_text()
self._screen._ws.klippy.gcode_script(
'SET_INPUT_SHAPER SHAPER_FREQ_X={} SHAPER_TYPE_X={} SHAPER_FREQ_Y={} SHAPER_TYPE_Y={}'.format(
shaper_freq_x, shaper_type_x, shaper_freq_y, shaper_type_y
)
)
def save_config(self):
_ = self.lang.gettext
script = {"script": "SAVE_CONFIG"}
self._screen._confirm_send_action(
None,
_("Save configuration?") + "\n\n" + _("Klipper will reboot"),
"printer.gcode.script",
script
)
def activate(self):
# This will return the current values
self._screen._ws.klippy.gcode_script(
'SET_INPUT_SHAPER'
)
# Check for the accelerometer
self._screen._ws.klippy.gcode_script(
'ACCELEROMETER_QUERY'
)
# Send at least two commands, with my accelerometer the first command after a reboot will fail
self._screen._ws.klippy.gcode_script(
'MEASURE_AXES_NOISE'
)
def process_update(self, action, data):
_ = self.lang.gettext
if action == "notify_gcode_response":
self.status.set_text('{}'.format(data.replace('shaper_', '').replace('damping_', '')))
data = data.lower()
if 'got 0' in data:
self.calibrate_btn.set_label(_('Check ADXL Wiring'))
self.calibrate_btn.set_sensitive(False)
if 'unknown command:"accelerometer_query"' in data:
self.calibrate_btn.set_label(_('ADXL Not Configured'))
self.calibrate_btn.set_sensitive(False)
if 'adxl345 values' in data or 'axes noise' in data:
self.has_sensor = True
self.calibrate_btn.set_sensitive(True)
self.calibrate_btn.set_label(_('Auto-calibrate'))
# Recommended shaper_type_y = ei, shaper_freq_y = 48.4 Hz
if 'recommended shaper_type_' in data:
results = re.search(r'shaper_type_(?P<axis>[xy])\s*=\s*(?P<shaper_type>.*?), shaper_freq_.\s*=\s*('
r'?P<shaper_freq>[0-9.]+)', data).groupdict()
self.freq_xy_adj['shaper_freq_' + results['axis']].set_value(float(results['shaper_freq']))
self.freq_xy_combo['shaper_type_' + results['axis']].set_active(SHAPERS.index(results['shaper_type']))
if self.calibrating_axis == results['axis']:
self.calibrate_btn.set_sensitive(True)
self.calibrate_btn.set_label(_('Calibrated'))
self.save_config()
elif self.calibrating_axis == "both" and results['axis'] == 'y':
self.calibrate_btn.set_sensitive(True)
self.calibrate_btn.set_label(_('Calibrated'))
self.save_config()
# shaper_type_y:ei shaper_freq_y:48.400 damping_ratio_y:0.100000
if 'shaper_type_' in data:
results = re.search(r'shaper_type_(?P<axis>[xy]):(?P<shaper_type>.*?) shaper_freq_.:('
r'?P<shaper_freq>[0-9.]+)', data).groupdict()
self.freq_xy_adj['shaper_freq_' + results['axis']].set_value(float(results['shaper_freq']))
self.freq_xy_combo['shaper_type_' + results['axis']].set_active(SHAPERS.index(results['shaper_type']))