diff --git a/Lib/typerig/proxy/fl/gui/widgets.py b/Lib/typerig/proxy/fl/gui/widgets.py index 7634956..0e146ce 100644 --- a/Lib/typerig/proxy/fl/gui/widgets.py +++ b/Lib/typerig/proxy/fl/gui/widgets.py @@ -28,7 +28,7 @@ from typerig.proxy.fl.gui.styles import css_tr_button # - Init ---------------------------------- -__version__ = '0.4.5' +__version__ = '0.4.6' # - Keep compatibility for basestring checks try: @@ -332,6 +332,23 @@ def expand(self): self.opt_sliders.show() self.__toggle_slider() + def blockSignals(self, state): + self.spin_box.blockSignals(state) + self.ctrl_btn_dec_10.blockSignals(state) + self.ctrl_btn_dec_1.blockSignals(state) + self.ctrl_btn_inc_1.blockSignals(state) + self.ctrl_btn_inc_10.blockSignals(state) + self.ctrl_slider.blockSignals(state) + + def setEnabled(self, state): + self.spin_box.setEnabled(state) + self.ctrl_btn_dec_10.setEnabled(state) + self.ctrl_btn_dec_1.setEnabled(state) + self.ctrl_btn_inc_1.setEnabled(state) + self.ctrl_btn_inc_10.setEnabled(state) + self.opt_sliders.setEnabled(state) + self.ctrl_slider.setEnabled(state) + def setValue(self, value): self.spin_box.setValue(value) self.__set_slider_no_signal(value) diff --git a/Scripts/TypeRig GUI/Panel/Delta.py b/Scripts/TypeRig GUI/Panel/Delta.py index 6a80856..ebb88b8 100644 --- a/Scripts/TypeRig GUI/Panel/Delta.py +++ b/Scripts/TypeRig GUI/Panel/Delta.py @@ -36,7 +36,7 @@ global pMode pLayers = None pMode = 0 -app_name, app_version = 'TypeRig | Delta', '5.2' +app_name, app_version = 'TypeRig | Delta', '5.3' TRToolFont = getTRIconFontPath() font_loaded = QtGui.QFontDatabase.addApplicationFont(TRToolFont) @@ -73,7 +73,7 @@ def __init__(self): self.tree_layer = TRDeltaLayerTree() # --- Layer selector: Actions (Context Menu) - act_resetAxis = QtGui.QAction('Reset Axis', self) + act_resetAxis = QtGui.QAction('Clear all', self) act_getVstem = QtGui.QAction('Get Vertical Stems', self) act_getHstem = QtGui.QAction('Get Horizontal Stems', self) @@ -83,7 +83,7 @@ def __init__(self): self.tree_layer.menu.addSeparator() self.tree_layer.menu.addAction(act_resetAxis) - act_resetAxis.triggered.connect(lambda: self.__reset_axis()) + act_resetAxis.triggered.connect(lambda: self.__reset_all()) act_getVstem.triggered.connect(lambda: self.get_stem(False)) act_getHstem.triggered.connect(lambda: self.get_stem(True)) @@ -97,6 +97,11 @@ def __init__(self): lay_actions = TRFlowLayout(spacing=10) + tooltip_button = 'Execute delta scale' + self.btn_execute_scale = CustomPushButton('action_play', tooltip=tooltip_button, enabled=False, obj_name='btn_panel') + lay_actions.addWidget(self.btn_execute_scale) + self.btn_execute_scale.clicked.connect(lambda: self.execute_target()) + tooltip_button = 'Get vertical stems' self.btn_get_stem_x = CustomPushButton('stem_vertical_alt', tooltip=tooltip_button, obj_name='btn_panel') lay_actions.addWidget(self.btn_get_stem_x) @@ -107,10 +112,25 @@ def __init__(self): lay_actions.addWidget(self.btn_get_stem_y) self.btn_get_stem_y.clicked.connect(lambda: self.get_stem(True)) - tooltip_button = 'Reset axis data' - self.btn_axis_reset = CustomPushButton('refresh', tooltip=tooltip_button, obj_name='btn_panel') + tooltip_button = 'Make undo snapshot' + self.btn_undo_snapshot = CustomPushButton('undo_snapshot', tooltip=tooltip_button, obj_name='btn_panel') + lay_actions.addWidget(self.btn_undo_snapshot) + self.btn_undo_snapshot.clicked.connect(lambda: self.__undo_snapshot()) + + tooltip_button = 'Set axis' + self.btn_axis_set = CustomPushButton('axis_set', tooltip=tooltip_button, obj_name='btn_panel') + lay_actions.addWidget(self.btn_axis_set) + self.btn_axis_set.clicked.connect(lambda: self.__set_axis(True)) + + tooltip_button = 'Reset axis' + self.btn_axis_reset = CustomPushButton('axis_remove', tooltip=tooltip_button, obj_name='btn_panel') lay_actions.addWidget(self.btn_axis_reset) - self.btn_axis_reset.clicked.connect(lambda: self.__reset_axis()) + self.btn_axis_reset.clicked.connect(lambda: self.__reset_axis(True)) + + tooltip_button = 'Reset all data' + self.btn_axis_reset_all = CustomPushButton('refresh', tooltip=tooltip_button, obj_name='btn_panel') + lay_actions.addWidget(self.btn_axis_reset_all) + self.btn_axis_reset_all.clicked.connect(lambda: self.__reset_all(True)) tooltip_button = 'Save axis data to external file' self.btn_file_save = CustomPushButton('file_save', tooltip=tooltip_button, obj_name='btn_panel') @@ -125,12 +145,12 @@ def __init__(self): tooltip_button = 'Save axis data to font file' self.btn_font_save = CustomPushButton('font_save', tooltip=tooltip_button, enabled=False, obj_name='btn_panel') lay_actions.addWidget(self.btn_font_save) - #self.btn_font_save.clicked.connect(lambda: self.__reset_axis()) + #self.btn_font_save.clicked.connect(lambda: self.__reset_all()) tooltip_button = 'Load axis data from font file' self.btn_font_open = CustomPushButton('font_open', tooltip=tooltip_button, enabled=False, obj_name='btn_panel') lay_actions.addWidget(self.btn_font_open) - #self.btn_file_open.clicked.connect(lambda: self.__reset_axis()) + #self.btn_file_open.clicked.connect(lambda: self.__reset_all()) box_delta_actions.setLayout(lay_actions) lay_main.addWidget(box_delta_actions) @@ -154,13 +174,14 @@ def __init__(self): lay_options.addWidget(self.chk_extrapolate) tooltip_button = 'Use target' - self.chk_target = CustomPushButton("node_target", checkable=True, cheked=False, enabled=False, tooltip=tooltip_button, obj_name='btn_panel_opt') + self.chk_target = CustomPushButton("node_target", checkable=True, cheked=False, tooltip=tooltip_button, obj_name='btn_panel_opt') lay_options.addWidget(self.chk_target) - #self.chk_target.clicked.connect(lambda: self.__toggle_controls()) + self.chk_target.clicked.connect(lambda: self.__prepare_target()) - tooltip_button = 'Proportional mode' - self.chk_proportional = CustomPushButton("diagonal_bottom_up", checkable=True, cheked=False, enabled=False,tooltip=tooltip_button, obj_name='btn_panel_opt') + tooltip_button = 'Proportional scale mode' + self.chk_proportional = CustomPushButton("diagonal_bottom_up", checkable=True, cheked=False, tooltip=tooltip_button, obj_name='btn_panel_opt') lay_options.addWidget(self.chk_proportional) + self.chk_proportional.clicked.connect(lambda: self.__toggle_proportional_scale()) tooltip_button = 'Scale from center' self.chk_center = CustomPushButton("node_target_expand", checkable=True, cheked=False, tooltip=tooltip_button, obj_name='btn_panel_opt') @@ -233,6 +254,12 @@ def __toggle_controls(self): self.cpn_value_stem_y.contract() self.cpn_value_lerp_t.contract() + def __toggle_proportional_scale(self): + if self.chk_proportional.isChecked(): + self.cpn_value_height.setEnabled(False) + else: + self.cpn_value_height.setEnabled(True) + def __change_value(self, cpn_object, value): cpn_object.setValue(cpn_object.getValue() + value) @@ -288,6 +315,26 @@ def __prepare_delta(self): # - Default return True + def __prepare_target(self): + if self.chk_target.isChecked(): + self.cpn_value_width.setEnabled(False) + self.cpn_value_height.setEnabled(False) + self.cpn_value_stem_x.setEnabled(False) + self.cpn_value_stem_y.setEnabled(False) + self.cpn_value_lerp_t.setEnabled(False) + self.cpn_value_ital.setEnabled(False) + + self.btn_execute_scale.setEnabled(True) + else: + self.cpn_value_width.setEnabled(True) + self.cpn_value_height.setEnabled(True) + self.cpn_value_stem_x.setEnabled(True) + self.cpn_value_stem_y.setEnabled(True) + self.cpn_value_lerp_t.setEnabled(True) + self.cpn_value_ital.setEnabled(True) + + self.btn_execute_scale.setEnabled(False) + def __refresh_ui(self): self.cpn_value_width.setValue(100) self.cpn_value_height.setValue(100) @@ -310,20 +357,7 @@ def __refresh_arrays(self): temp_service = [glyph._getServiceArray(layer_data[0]) for layer_data in self.axis_data] self.glyph_arrays[glyph.name] = [glyph, DeltaScale(temp_outline, self.axis_stems), DeltaScale(temp_service, self.axis_stems)] - def __reset_axis(self): - self.tree_layer.setTree(self.__init_tree(), tree_column_names) - self.axis_data = [] - self.axis_stems = [] - self.glyph_arrays = {} - - self.cpn_value_width.setValue(100) - self.cpn_value_height.setValue(100) - self.cpn_value_stem_x.setValue(100) - self.cpn_value_stem_y.setValue(100) - self.cpn_value_lerp_t.setValue(0) - self.cpn_value_ital.setValue(self.active_font.italic_angle) - - def __set_axis(self): + def __set_axis(self, verbose=False): self.masters_data = self.tree_layer.getTree() self.axis_data = self.masters_data[tree_axis_group_name] self.axis_stems = [] @@ -337,12 +371,54 @@ def __set_axis(self): warnings.warn('Missing or invalid stem data!', TRDeltaStemWarning) return - def __apply_scale(self): - if pMode == 0: - glyph, _delta_outline, _delta_service = self.glyph_arrays[fl6.CurrentGlyph().name] - glyph.updateObject(glyph.fl, '{} {} | \tGlyph: {}; Layer: {}'.format(app_name, app_version, glyph.name, self.active_layer)) + if verbose: output(0, '%s %s' %(app_name, app_version), 'Font: %s; Axis set.' %(self.active_font.name)) + + def __reset_axis(self, verbose=False): + self.axis_data = [] + self.axis_stems = [] + self.glyph_arrays = {} + + if verbose: output(0, '%s %s' %(app_name, app_version), 'Font: %s; Axis data cleared.' %(self.active_font.name)) + + def __reset_all(self, verbose=False): + self.tree_layer.setTree(self.__init_tree(), tree_column_names) + self.__reset_axis() + + self.cpn_value_width.blockSignals(True) + self.cpn_value_height.blockSignals(True) + self.cpn_value_stem_x.blockSignals(True) + self.cpn_value_stem_y.blockSignals(True) + self.cpn_value_lerp_t.blockSignals(True) + self.cpn_value_ital.blockSignals(True) + + self.cpn_value_width.setValue(100) + self.cpn_value_height.setValue(100) + self.cpn_value_stem_x.setValue(100) + self.cpn_value_stem_y.setValue(100) + self.cpn_value_lerp_t.setValue(0) + self.cpn_value_ital.setValue(self.active_font.italic_angle) + + self.cpn_value_width.blockSignals(False) + self.cpn_value_height.blockSignals(False) + self.cpn_value_stem_x.blockSignals(False) + self.cpn_value_stem_y.blockSignals(False) + self.cpn_value_lerp_t.blockSignals(False) + self.cpn_value_ital.blockSignals(False) + + if verbose: output(0, '%s %s' %(app_name, app_version), 'Reset.') + + def __undo_snapshot(self, verbose=True): + # - Init + process_glyphs = getProcessGlyphs(pMode) + process_glyphs_names = [glyph.name for glyph in process_glyphs] + undo_message = '{} {} | Undo Snapshot for glyphs: {};'.format(app_name, app_version, '; '.join(process_glyphs_names)) + + # - Set undo + if len(process_glyphs) > 2: + self.active_font.updateObject(self.active_font.fl, undo_message, verbose) else: - self.active_font.updateObject(glyph.fl, '{} {} | \tGlyphs: {}; Layer: {}'.format(app_name, app_version, '; '.join(list(self.glyph_arrays.keys())), self.active_layer)) + glyph = process_glyphs[0] + glyph.updateObject(glyph.fl, undo_message, verbose) # -- File operations def file_save_axis_data(self): @@ -404,11 +480,18 @@ def execute_scale(self, use_time=False): # - Scaling sx = float(self.cpn_value_width.getValue())/100. - sy = float(self.cpn_value_height.getValue())/100. + + if self.chk_proportional.isChecked(): + sy = sx + self.cpn_value_height.blockSignals(True) + self.cpn_value_height.setValue(self.cpn_value_width.getValue()) + self.cpn_value_height.blockSignals(False) + else: + sy = float(self.cpn_value_height.getValue())/100. # - Italic - #it = radians(-float(self.cpn_value_ital.getValue())) self.active_font.italic_angle - it = math.radians(-float(self.active_font.italic_angle)) # Use italic only in the context of Delta calculation not for actuall slanting + # Note: Use italic only in the context of Delta calculation not for actuall slanting + it = math.radians(-float(self.active_font.italic_angle)) # - Options opt_extrapolate = self.chk_extrapolate.isChecked() @@ -461,10 +544,66 @@ def execute_scale(self, use_time=False): glyph.update() + # - Update viewport + try: + self.active_canvas.refreshAll() + except: + pass + + def execute_target(self): + # - Init + glyphs_done = [] + + system_ready = self.__prepare_delta() + if not system_ready: return + + process_target = self.masters_data[tree_axis_target_name] + if not len(process_target): return + + if pMode == 0: + system_process = [self.glyph_arrays[fl6.CurrentGlyph().name]] + else: + system_process = self.glyph_arrays.values() + + # - Process + for glyph, delta_outline, delta_service in system_process: + for process_layer_data in process_target: + # - Unpack axis data + layer_name, curr_sw_dx, curr_sw_dy, sx, sy, _ = process_layer_data + + curr_sw_dx = float(curr_sw_dx) + curr_sw_dy = float(curr_sw_dy) + sx = float(sx)/100. + sy = float(sy)/100. + it = math.radians(-float(self.active_font.italic_angle)) + + # - Options + opt_extrapolate = self.chk_extrapolate.isChecked() + opt_center = self.chk_center.isChecked() + opt_metrics = self.chk_metrics.isChecked() + opt_anchors = self.chk_anchors.isChecked() + + # - Process + outline_scale = delta_outline.scale_by_stem((curr_sw_dx, curr_sw_dy), (sx, sy), (0., 0.), (0., 0.), it, extrapolate=opt_extrapolate) + service_scale = delta_service.scale_by_stem((curr_sw_dx, curr_sw_dy), (sx, sy), (0., 0.), (0., 0.), it, extrapolate=opt_extrapolate) + + # - Apply transformations + glyph._setPointArray(outline_scale, layer=layer_name, keep_center=opt_center) + glyph._setServiceArray(service_scale, layer=layer_name, set_metrics=opt_metrics, set_anchors=opt_anchors) + + glyphs_done.append(glyph.name) + glyph.update() + + # - Update viewport try: self.active_canvas.refreshAll() except: pass + + # - Finish it + self.__undo_snapshot(False) + output(0, '%s %s' %(app_name, app_version), 'Font: %s;' %(self.active_font.name)) + output(0, '%s %s' %(app_name, app_version), 'Glyph(s): %s.' %('; '.join(glyphs_done))) # - Tabs ------------------------------- class tool_tab(QtGui.QWidget): diff --git a/Scripts/TypeRig GUI/Panel/DeltaOld.py b/Scripts/TypeRig GUI/Panel/DeltaOld.py deleted file mode 100644 index 44090a3..0000000 --- a/Scripts/TypeRig GUI/Panel/DeltaOld.py +++ /dev/null @@ -1,484 +0,0 @@ -#FLM: TR: Delta Old -# ----------------------------------------------------------- -# (C) Vassil Kateliev, 2020-2021 (http://www.kateliev.com) -# (C) Karandash Type Foundry (http://www.karandash.eu) -#------------------------------------------------------------ - -# No warranties. By using this you agree -# that you use it at your own risk! - -# - Dependencies ----------------- -from __future__ import absolute_import, print_function -import warnings, os, json, random -from math import radians -from collections import OrderedDict - -import fontlab as fl6 -import fontgate as fgt - -from typerig.proxy.fl.objects.font import pFont -from typerig.proxy.fl.objects.glyph import eGlyph - -from typerig.core.base.message import * -from typerig.core.func.math import linInterp, ratfrac -from typerig.core.func import transform -from typerig.core.objects.array import PointArray -from typerig.core.objects.delta import DeltaArray, DeltaScale - -from PythonQt import QtCore -from typerig.proxy.fl.gui import QtGui -from typerig.proxy.fl.gui.widgets import * -from typerig.proxy.fl.gui.dialogs import TR2ComboDLG - -# - Init ------------------------------- -global pLayers -global pMode -pLayers = None -pMode = 0 -app_name, app_version = 'TypeRig | Delta', '4.48' - -# -- Strings -tree_column_names = ('Layer','X', 'Y', 'Width', 'Height', 'Color') -tree_masters_group_name = 'Master Layers' -tree_axis_group_name = 'Virtual Axis' -tree_axis_target_name = 'Target Layers' -fileFormats = 'TypeRig Delta Panel Target (*.json);;' - -default_sx = '100.' -default_sy = '100.' - -# - Tabs ------------------------------- -class tool_tab(QtGui.QWidget): - def __init__(self): - super(tool_tab, self).__init__() - - # - Init - - # - Widgets - # - Glyph list - self.lst_glyphName = QtGui.QListWidget() - self.lst_glyphName.setMinimumHeight(50) - self.lst_glyphName.setAlternatingRowColors(True) - - # -- Buttons - self.btn_refresh = QtGui.QPushButton('&Refresh') - self.btn_setAxis = QtGui.QPushButton('Set &Axis') - self.btn_setAxis_c = QtGui.QPushButton('Set &Axis') - self.btn_resetAxis = QtGui.QPushButton('Reset Axis') - self.btn_getVstem = QtGui.QPushButton('Get &Vertical Stems') - self.btn_getHstem = QtGui.QPushButton('Get &Horizontal Stems') - self.btn_setLayer = QtGui.QPushButton('Layer changed') - self.btn_execute = QtGui.QPushButton('Execute') - self.btn_file_load_patchboard = QtGui.QPushButton('Load') - self.btn_file_save_patchboard = QtGui.QPushButton('Save') - - # -- Options - self.btn_opt_extrapolate = QtGui.QPushButton('Extrapolate') - self.btn_opt_italic = QtGui.QPushButton('Italic') - self.btn_opt_update_preview = QtGui.QPushButton('Live preview') - self.btn_opt_keep_center = QtGui.QPushButton('Keep Center') - self.btn_opt_metrics = QtGui.QPushButton('Metrics') - self.btn_opt_anchors = QtGui.QPushButton('Anchors') - self.btn_opt_target = QtGui.QPushButton('Use Target') - - self.btn_opt_extrapolate.setCheckable(True) - self.btn_opt_italic.setCheckable(True) - self.btn_opt_update_preview.setCheckable(True) - self.btn_opt_keep_center.setCheckable(True) - self.btn_opt_metrics.setCheckable(True) - self.btn_opt_anchors.setCheckable(True) - self.btn_opt_target.setCheckable(True) - - #self.btn_opt_extrapolate.setChecked(True) - #self.btn_opt_keep_center.setChecked(True) - #self.btn_opt_target.setChecked(True) - - self.btn_refresh.clicked.connect(self.refresh) - self.btn_setAxis.clicked.connect(self.set_axis) - self.btn_setAxis_c.clicked.connect(self.set_axis) - self.btn_resetAxis.clicked.connect(self.reset_axis) - self.btn_getVstem.clicked.connect(lambda: self.get_stem(False)) - self.btn_getHstem.clicked.connect(lambda: self.get_stem(True)) - self.btn_setLayer.clicked.connect(self.set_current_layer) - self.btn_execute.clicked.connect(lambda: self.execute_scale(True)) - - self.btn_file_save_patchboard.clicked.connect(self.file_save_patchboard) - self.btn_file_load_patchboard.clicked.connect(self.file_load_patchboard) - - # -- Layer selector - self.tree_layer = TRDeltaLayerTree() - - # -- Additional Actions (Context Menu) - act_setItem_mask = QtGui.QAction('Set mask', self) - act_setItem_unmask = QtGui.QAction('Remove mask', self) - act_setItem_value = QtGui.QAction('Set value', self) - - act_setAxis = QtGui.QAction('Set Axis', self) - act_resetAxis = QtGui.QAction('Reset Axis', self) - act_getVstem = QtGui.QAction('Get Vertical Stems', self) - act_getHstem = QtGui.QAction('Get Horizontal Stems', self) - - self.tree_layer.menu.addSeparator() - self.tree_layer.menu.addAction(act_setItem_mask ) - self.tree_layer.menu.addAction(act_setItem_unmask ) - self.tree_layer.menu.addAction(act_setItem_value ) - self.tree_layer.menu.addSeparator() - self.tree_layer.menu.addAction(act_getVstem) - self.tree_layer.menu.addAction(act_getHstem) - self.tree_layer.menu.addSeparator() - self.tree_layer.menu.addAction(act_setAxis) - self.tree_layer.menu.addAction(act_resetAxis) - - act_setItem_mask.triggered.connect(lambda: self.tree_layer._setItemData('mask.', 0, 0, False)) - act_setItem_unmask.triggered.connect(lambda: self.tree_layer._setItemData('mask.', 0, 1, True)) - act_setItem_value.triggered.connect(lambda: self.tree_layer._setItemData(*TR2ComboDLG('Delta Setup', 'Please enter new value for selected columns', 'Value:', 'Column:', tree_column_names).values)) - - act_setAxis.triggered.connect(lambda: self.set_axis()) - act_resetAxis.triggered.connect(lambda: self.reset_axis()) - - act_getVstem.triggered.connect(lambda: self.get_stem(False)) - act_getHstem.triggered.connect(lambda: self.get_stem(True)) - - # - Build Layout - layoutV = QtGui.QVBoxLayout() - - # -- Layer selector - layoutV.addWidget(self.tree_layer) - - # -- Set Glyph list - self.fld_glyphs = TRCollapsibleBox('Process Glyphs') - lay_glyphs = QtGui.QVBoxLayout() - lay_glyphs_b = QtGui.QHBoxLayout() - lay_glyphs.addWidget(self.lst_glyphName) - lay_glyphs_b.addWidget(self.btn_refresh) - lay_glyphs_b.addWidget(self.btn_setAxis_c) - lay_glyphs.addLayout(lay_glyphs_b) - self.fld_glyphs.setContentLayout(lay_glyphs) - layoutV.addWidget(self.fld_glyphs) - - # -- Delta Setup controls - self.fld_setup = TRCollapsibleBox('Delta Setup') - layoutG = QtGui.QGridLayout() - layoutG.addWidget(QtGui.QLabel('Axis Setup:'), 0, 0, 1, 10) - layoutG.addWidget(self.btn_getVstem, 1, 0, 1, 5) - layoutG.addWidget(self.btn_getHstem, 1, 5, 1, 5) - layoutG.addWidget(self.btn_resetAxis, 2, 0, 1, 5) - layoutG.addWidget(self.btn_setAxis, 2, 5, 1, 5) - layoutG.addWidget(self.btn_file_save_patchboard, 3, 0, 1, 5) - layoutG.addWidget(self.btn_file_load_patchboard, 3, 5, 1, 5) - - layoutG.addWidget(QtGui.QLabel('Options:'), 4, 0, 1, 10) - layoutG.addWidget(self.btn_opt_extrapolate, 5, 0, 1, 5) - layoutG.addWidget(self.btn_opt_italic, 5, 5, 1, 5) - layoutG.addWidget(self.btn_opt_anchors, 6, 0, 1, 5) - layoutG.addWidget(self.btn_opt_metrics, 6, 5, 1, 5) - layoutG.addWidget(self.btn_opt_target, 8, 0, 1, 5) - layoutG.addWidget(self.btn_opt_keep_center, 8, 5, 1, 5) - layoutG.addWidget(self.btn_opt_update_preview, 9, 0, 1, 10) - - self.fld_setup.setContentLayout(layoutG) - layoutV.addWidget(self.fld_setup) - - # -- Set Sliders - self.fld_weight = TRCollapsibleBox('Stem Weight Control') - self.fld_scale = TRCollapsibleBox('Compensative scaler') - - lay_weight = QtGui.QVBoxLayout() - lay_scale = QtGui.QVBoxLayout() - - # --- Mixer - lay_weight.addWidget(QtGui.QLabel('Vertical Stem Weight (X):')) - self.mixer_dx = TRSliderCtrl('1', '1000', '0', 1) - self.mixer_dx.sld_axis.valueChanged.connect(self.execute_scale) - lay_weight.addLayout(self.mixer_dx) - lay_weight.addSpacing(10) - - lay_weight.addWidget(QtGui.QLabel('Horizontal Stem Weight (Y):')) - self.mixer_dy = TRSliderCtrl('1', '1000', '0', 1) - self.mixer_dy.sld_axis.valueChanged.connect(self.execute_scale) - lay_weight.addLayout(self.mixer_dy) - - self.fld_weight.setContentLayout(lay_weight) - layoutV.addWidget(self.fld_weight) - - # --- Scaler - lay_scale.addWidget(QtGui.QLabel('Width')) - self.scalerX = TRSliderCtrl('1', '200', '100', 1) - self.scalerX.sld_axis.valueChanged.connect(self.execute_scale) - lay_scale.addLayout(self.scalerX) - lay_scale.addSpacing(10) - - lay_scale.addWidget(QtGui.QLabel('Height')) - self.scalerY = TRSliderCtrl('1', '200', '100', 1) - self.scalerY.sld_axis.valueChanged.connect(self.execute_scale) - lay_scale.addLayout(self.scalerY) - - self.fld_scale.setContentLayout(lay_scale) - layoutV.addWidget(self.fld_scale) - - # -- Tail - layoutE = QtGui.QHBoxLayout() - layoutE.addWidget(self.btn_setLayer) - layoutE.addWidget(self.btn_execute) - layoutV.addLayout(layoutE) - - self.__lbl_warn = QtGui.QLabel('') - layoutV.addWidget(self.__lbl_warn) - self.__lbl_warn.hide() - - # -- Finish - self.refresh() - self.tree_layer.setTree(self.__init_tree(), tree_column_names) - self.setLayout(layoutV) - self.setMinimumSize(300, self.sizeHint.height()) - - # - Functions ----------------------------------------- - # - File operations - def file_save_patchboard(self): - fontPath = os.path.split(self.active_font.fg.path)[0] - fname = QtGui.QFileDialog.getSaveFileName(self, 'Save Axis Patch-board to file', fontPath, fileFormats) - - if fname != None: - with open(fname, 'w') as exportFile: - json.dump(self.tree_layer.getTree(), exportFile) - output(7, app_name, 'Font: %s; Patch-board saved to: %s.' %(self.active_font.name, fname)) - - def file_load_patchboard(self): - fontPath = os.path.split(self.active_font.fg.path)[0] - fname = QtGui.QFileDialog.getOpenFileName(self, 'Load Axis Patch-board from file', fontPath, fileFormats) - - if fname != None: - with open(fname, 'r') as importFile: - imported_data = json.load(importFile) - - self.masters_data = imported_data - self.tree_layer.setTree(self.masters_data, tree_column_names) - output(6, app_name, 'Font: %s; Patch-board loaded from: %s.' %(self.active_font.name, fname)) - - # -- Special - def __where(self, data, search, ret=1): - for item in data: - if search == item[0]: return item[ret] - - warnings.warn('Axis/target missing layer: %s!' %search, TRDeltaAxisWarning) - return 0 - - def __init_tree(self): - return_data = [] - return_data.append((tree_masters_group_name, [(layer, '', '', default_sx, default_sy, eGlyph().layer(layer).wireframeColor) for layer in self.active_font.masters()])) - return_data.append((tree_axis_group_name,[])) - return_data.append((tree_axis_target_name,[])) - return OrderedDict(return_data) - - def __doCheck(self): - if pMode > 0: return 1 - if self.active_glyph.fg.id != fl6.CurrentGlyph().id and self.active_glyph.fl.name != fl6.CurrentGlyph().name: - warnings.warn('Glyph mismatch! No action taken! Forcing refresh!', GlyphWarning) - self.refresh() - return 0 - return 1 - - # -- Operation - def refresh(self): - # - Init - self.axis_points = [] - self.axis_stems = [] - self.data_glyphs = getProcessGlyphs(pMode) - self.data_glyphs = [glyph for glyph in self.data_glyphs if not glyph.isEmpty()] - self.glyph_arrays = {} - self.glyph_arrays_service = {} - - self.active_glyph = eGlyph() - self.active_font = pFont() - self.active_workspace = pWorkspace() - self.active_canvas = self.active_workspace.getCanvas(True) - - self.working_names = [glyph.name for glyph in self.data_glyphs] if len(self.data_glyphs) > 1 else [self.active_glyph.name] - self.lst_glyphName.clear() - self.lst_glyphName.addItems(self.working_names) - - if len(self.active_font.masters()) > 1: - # - Activate - self.__lbl_warn.setText('') - self.__lbl_warn.setStyleSheet('') - self.btn_setAxis.setEnabled(True) - self.btn_getVstem.setEnabled(True) - self.btn_getHstem.setEnabled(True) - - self.mixer_dx.reset() - self.mixer_dy.reset() - self.scalerX.reset() - self.scalerY.reset() - else: - # - Deactivate - self.__lbl_warn.show() - self.__lbl_warn.setText('Insufficient number of Master layers!
Delta Panel is currently disabled!') - self.__lbl_warn.setStyleSheet('padding: 10; font-size: 10pt; background: lightpink;') - self.btn_setAxis.setEnabled(False) - self.btn_getVstem.setEnabled(False) - self.btn_getHstem.setEnabled(False) - - def get_stem(self, get_y=False): - if self.__doCheck(): - self.masters_data = self.tree_layer.getTree() - self.active_glyph = eGlyph() - - for group, data in self.masters_data.items(): - for layer_data in data: - try: - selection = self.active_glyph.selectedNodes(layer_data[0], True) - - if get_y: - layer_data[2] = abs(selection[0].y - selection[-1].y) - else: - layer_data[1] = abs(selection[0].x - selection[-1].x) - except IndexError: - warnings.warn('Missing or incompatible layer: %s!' %layer_data[0], LayerWarning) - continue - - self.tree_layer.setTree(OrderedDict(self.masters_data), tree_column_names) - - def set_current_layer(self): - try: - if not self.btn_opt_target.isChecked(): - max_dx = max(self.axis_data, key=lambda i: float(i[1]))[1] - max_dy = max(self.axis_data, key=lambda i: float(i[2]))[2] - min_dx = min(self.axis_data, key=lambda i: float(i[1]))[1] - min_dy = min(self.axis_data, key=lambda i: float(i[2]))[2] - self.mixer_dx.edt_pos.setText(round(float(self.__where(self.axis_data, self.active_glyph.layer().name, 1)))) - self.mixer_dy.edt_pos.setText(round(float(self.__where(self.axis_data, self.active_glyph.layer().name, 2)))) - else: - max_dx = max(self.masters_data[tree_axis_target_name], key=lambda i: float(i[1]))[1] - max_dy = max(self.masters_data[tree_axis_target_name], key=lambda i: float(i[2]))[2] - min_dx = min(self.masters_data[tree_axis_target_name], key=lambda i: float(i[1]))[1] - min_dy = min(self.masters_data[tree_axis_target_name], key=lambda i: float(i[2]))[2] - self.mixer_dx.edt_pos.setText(round(float(self.__where(self.masters_data[tree_axis_target_name], self.active_glyph.layer().name, 1)))) - self.mixer_dy.edt_pos.setText(round(float(self.__where(self.masters_data[tree_axis_target_name], self.active_glyph.layer().name, 2)))) - - - self.mixer_dx.edt_1.setText(min_dx) - self.mixer_dy.edt_1.setText(min_dy) - self.mixer_dx.edt_1.setText(max_dx) - self.mixer_dy.edt_1.setText(max_dy) - - self.mixer_dx.refreshSlider() - self.mixer_dy.refreshSlider() - - except (ValueError, IndexError): - warnings.warn('Invalid Axis/Target or Axis/Target not set!', TRDeltaAxisWarning) - - def reset_axis(self): - self.masters_data = self.tree_layer.getTree() - #self.masters_data[tree_masters_group_name] += self.masters_data[tree_axis_group_name] - self.masters_data[tree_axis_group_name] = [] - self.masters_data[tree_axis_target_name] = [] - self.tree_layer.setTree(self.masters_data, tree_column_names) - - def set_axis(self): - self.masters_data = self.tree_layer.getTree() - self.axis_data = self.masters_data[tree_axis_group_name] - - if len(self.axis_data): - # - Init - self.axis_stems = [] - for layer_data in self.axis_data: - try: - x_stem, y_stem = float(layer_data[1]), float(layer_data[2]) - self.axis_stems.append([(x_stem, y_stem)]) - - except ValueError: - warnings.warn('Missing or invalid stem data!', TRDeltaStemWarning) - return - - for wGlyph in self.data_glyphs: - temp_outline = [wGlyph._getPointArray(layer_data[0]) for layer_data in self.axis_data] - temp_service = [wGlyph._getServiceArray(layer_data[0]) for layer_data in self.axis_data] - self.glyph_arrays[wGlyph.name] = DeltaScale(temp_outline, self.axis_stems) - self.glyph_arrays_service[wGlyph.name] = DeltaScale(temp_service, self.axis_stems) - - # - Set Sliders - # -- X - self.mixer_dx.edt_0.setText(round(float(self.axis_data[0][1]))) - self.mixer_dx.edt_1.setText(round(float(self.axis_data[-1][1]))) - self.mixer_dx.edt_pos.setText(round(float(self.__where(self.axis_data, self.active_glyph.layer().name, 1)))) - self.mixer_dx.refreshSlider() - # -- Y - self.mixer_dy.edt_0.setText(round(float(self.axis_data[0][2]))) - self.mixer_dy.edt_1.setText(round(float(self.axis_data[-1][2]))) - self.mixer_dy.edt_pos.setText(round(float(self.__where(self.axis_data, self.active_glyph.layer().name, 2)))) - self.mixer_dy.refreshSlider() - - # - Build - self.active_font.updateObject(self.active_font.fl, 'Glyph(s): {} Axis :{} @ {}'.format('; '.join(self.working_names), ' :'.join([item[0] for item in self.axis_data]), self.active_glyph.layer().name)) - - def execute_scale(self, force_preview=False): - if len(self.glyph_arrays.keys()): - if self.btn_opt_update_preview.isChecked() or force_preview: - # - Stems - curr_sw_dx = float(self.mixer_dx.sld_axis.value) - curr_sw_dy = float(self.mixer_dy.sld_axis.value) - - # - Scaling - sx = float(self.scalerX.sld_axis.value)/100. - sy = float(self.scalerY.sld_axis.value)/100. - - # - Options - opt_extrapolate = self.btn_opt_extrapolate.isChecked() - opt_italic = radians(-float(self.active_font.italic_angle)) if self.btn_opt_italic.isChecked() else 0. - opt_metrics = self.btn_opt_metrics.isChecked() - opt_anchors = self.btn_opt_anchors.isChecked() - - # - Process - for wGlyph in self.data_glyphs: - if self.btn_opt_target.isChecked(): - process_target = self.masters_data[tree_axis_target_name] - - if len(process_target): - for process_layer_data in process_target: - layer_name, layer_dx, layer_dy, layer_width, layer_height, _color = process_layer_data - - if not self.btn_opt_update_preview.isChecked(): - # - Stems - curr_sw_dx = float(layer_dx) - curr_sw_dy = float(layer_dy) - - # - Scaling - sx = float(layer_width)/100. - sy = float(layer_height)/100. - - outline_scale = self.glyph_arrays[wGlyph.name].scale_by_stem((curr_sw_dx, curr_sw_dy), (sx,sy), (0.,0.), (0.,0.), opt_italic, extrapolate=opt_extrapolate) - wGlyph._setPointArray(outline_scale, layer_name, keep_center=self.btn_opt_keep_center.isChecked()) - - service_scale = self.glyph_arrays_service[wGlyph.name].scale_by_stem((curr_sw_dx, curr_sw_dy), (sx,sy), (0.,0.), (0.,0.), opt_italic, extrapolate=opt_extrapolate) - wGlyph._setServiceArray(service_scale, layer_name, opt_metrics, opt_anchors) - - if not self.btn_opt_update_preview.isChecked(): - output(0, app_name, 'Processed: %s' %wGlyph.name) - - else: - warnings.warn('Empty/Invalid Target Table provided! No action taken!', GlyphWarning) - else: - outline_scale = self.glyph_arrays[wGlyph.name].scale_by_stem((curr_sw_dx, curr_sw_dy), (sx,sy), (0.,0.), (0.,0.), opt_italic, extrapolate=opt_extrapolate) - wGlyph._setPointArray(outline_scale, keep_center=self.btn_opt_keep_center.isChecked()) - - service_scale = self.glyph_arrays_service[wGlyph.name].scale_by_stem((curr_sw_dx, curr_sw_dy), (sx,sy), (0.,0.), (0.,0.), opt_italic, extrapolate=opt_extrapolate) - wGlyph._setServiceArray(service_scale, set_metrics=opt_metrics, set_anchors=opt_anchors) - - wGlyph.update() - - try: - self.active_canvas.refreshAll() - except: - pass - - if opt_metrics: self.active_font.fl.changed() - - -# - Test ---------------------- -if __name__ == '__main__': - test = tool_tab() - test.setWindowTitle('%s %s' %(app_name, app_version)) - test.setGeometry(100, 100, 280, 800) - test.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint) # Always on top!! - - test.show() \ No newline at end of file