class TRContourSelect(QtGui.QVBoxLayout): # - Split/Break contour def __init__(self): super(TRContourSelect, self).__init__() # -- Init self.table_dict = {0: {0: None}} # Empty table self.layer_names = [] # Empty layer list #self.table_columns = 'N,Shape,Contour,X,Y,Type,Relative'.split(',') self.table_columns = 'N,Sh,Cn,X,Y,Type,Rel'.split(',') # -- Widgets self.lay_head = QtGui.QGridLayout() self.edt_glyphName = QtGui.QLineEdit() self.cmb_layer = QtGui.QComboBox() self.btn_refresh = QtGui.QPushButton('&Refresh') self.btn_apply = QtGui.QPushButton('&Apply') self.btn_apply.setEnabled(False) # -- Build Layout self.lay_head.addWidget(QtGui.QLabel('G:'), 0, 0, 1, 1) self.lay_head.addWidget(self.edt_glyphName, 0, 1, 1, 5) self.lay_head.addWidget(self.btn_refresh, 0, 6, 1, 2) self.lay_head.addWidget(QtGui.QLabel('L:'), 1, 0, 1, 1) self.lay_head.addWidget(self.cmb_layer, 1, 1, 1, 5) self.lay_head.addWidget(self.btn_apply, 1, 6, 1, 2) self.addLayout(self.lay_head) # -- Node List Table self.tab_nodes = TRTableView(self.table_dict) self.addWidget(self.tab_nodes) #self.refresh() # Build Table self.btn_refresh.clicked.connect(lambda: self.refresh()) self.cmb_layer.currentIndexChanged.connect(lambda: self.changeLayer()) # -- Table Styling self.tab_nodes.horizontalHeader().setStretchLastSection(False) self.tab_nodes.setSortingEnabled(True) self.tab_nodes.horizontalHeader().sortIndicatorChanged.connect( lambda: self.tab_nodes.resizeColumnsToContents()) self.tab_nodes.verticalHeader().hide() self.tab_nodes.resizeColumnsToContents() self.tab_nodes.selectionModel().selectionChanged.connect( self.selectionChanged) self.tab_nodes.itemChanged.connect(self.valueChanged) def refresh(self, layer=None): # - Init self.glyph = eGlyph() self.edt_glyphName.setText(eGlyph().name) self.table_dict = {} node_count = 0 # - Populate layers if layer is None: self.layer_names = [ item.name for item in self.glyph.layers() if '#' not in item.name ] self.cmb_layer.clear() self.cmb_layer.addItems(self.layer_names) self.cmb_layer.setCurrentIndex( self.layer_names.index(self.glyph.activeLayer().name)) # - Populate table try: # Dirty Quick Fix - Solve later for sID, shape in enumerate(self.glyph.shapes(layer)): for cID, contour in enumerate(shape.contours): for nID, node in enumerate(contour.nodes()): table_values = [ node_count, sID, cID, round(node.x, 2), round(node.y, 2), node.type, round(eNode(node).distanceToPrev(), 2) ] self.table_dict[node_count] = OrderedDict( zip(self.table_columns, table_values)) node_count += 1 self.tab_nodes.setTable(self.table_dict, (False, False)) self.tab_nodes.resizeColumnsToContents() except AttributeError: pass def doCheck(self): if self.glyph.fg.id != fl6.CurrentGlyph( ).id and self.glyph.fl.name != fl6.CurrentGlyph().name: print '\nERRO:\tGlyph mismatch:\n\tCurrent active glyph: %s\n\tOutline panel glyph: %s' % ( fl6.CurrentGlyph(), self.glyph.fg) print 'WARN:\tNo action taken! Forcing refresh!' self.refresh() return 0 return 1 def changeLayer(self): if self.doCheck(): self.refresh(self.cmb_layer.currentText) def selectionChanged(self): if self.doCheck(): if self.cmb_layer.currentText == self.glyph.activeLayer().name: # - Prepare self.glyph.fl.unselectAllNodes() # - Process for cel_coords in self.tab_nodes.selectionModel( ).selectedIndexes: #print self.tab_nodes.item(cel_coords.row(), cel_coords.column()).text() selected_nid = int( self.tab_nodes.item(cel_coords.row(), 0).text()) self.glyph.nodes(self.cmb_layer.currentText )[selected_nid].selected = True # - Finish self.glyph.updateObject(self.glyph.fl, verbose=False) def valueChanged(self, item): if self.doCheck(): #print item.text(), item.row() try: # Dirty Quick Fix - Solve later # - Init x_col, y_col = self.table_columns.index( 'X'), self.table_columns.index('Y') active_nid = int(self.tab_nodes.item(item.row(), 0).text()) # - Process if item.column() == x_col or item.column() == y_col: new_x = float( self.tab_nodes.item(item.row(), x_col).text()) new_y = float( self.tab_nodes.item(item.row(), y_col).text()) active_node = eNode( self.glyph.nodes( self.cmb_layer.currentText)[active_nid]) active_node.reloc(new_x, new_y) # -- Finish self.glyph.update() self.glyph.updateObject(self.glyph.fl, verbose=False) except AttributeError: pass
class TRSmartCorner(QtGui.QVBoxLayout): # - Split/Break contour def __init__(self, parentWidget): super(TRSmartCorner, self).__init__() self.upper_widget = parentWidget # -- Init self.active_font = pFont() self.builder = None self.font_masters = self.active_font.masters() self.empty_preset = lambda row: OrderedDict([(row, OrderedDict([('Preset', 'Preset %s' %row)] + [(master, '0') for master in self.font_masters]))]) self.table_dict = self.empty_preset(0) self.last_preset = 0 # -- Widgets self.lay_head = QtGui.QGridLayout() self.edt_glyphName = QtGui.QLineEdit() self.edt_glyphName.setPlaceholderText('Glyph name') # -- Buttons self.btn_getBuilder = QtGui.QPushButton('Set &Builder') self.btn_findBuilder = QtGui.QPushButton('&From Font') self.btn_addPreset = QtGui.QPushButton('Add') self.btn_delPreset = QtGui.QPushButton('Remove') self.btn_resetPreset = QtGui.QPushButton('Reset') self.btn_loadPreset = QtGui.QPushButton('&Load Presets') self.btn_savePreset = QtGui.QPushButton('&Save Presets') self.btn_apply_smartCorner = QtGui.QPushButton('&Apply Smart Corner') self.btn_remove_smartCorner = QtGui.QPushButton('R&emove Smart Corner') self.btn_remove_presetCorner = QtGui.QPushButton('&Find and Remove') self.btn_apply_smartCorner.setToolTip('Apply Smart Corner preset on SELECTED nodes.') self.btn_remove_smartCorner.setToolTip('Remove Smart Corner on SELECTED nodes.') self.btn_remove_presetCorner.setToolTip('Find and remove all Smart Corners that equal the currently selected preset.') self.btn_apply_round = QtGui.QPushButton('&Round') self.btn_apply_mitre = QtGui.QPushButton('&Mitre') self.btn_apply_overlap = QtGui.QPushButton('&Overlap') self.btn_apply_trap = QtGui.QPushButton('&Trap') self.btn_rebuild = QtGui.QPushButton('Rebuild corner') self.btn_getBuilder.setMinimumWidth(70) self.btn_findBuilder.setMinimumWidth(70) self.btn_apply_round.setMinimumWidth(70) self.btn_apply_mitre.setMinimumWidth(70) self.btn_apply_overlap.setMinimumWidth(70) self.btn_apply_trap.setMinimumWidth(70) self.btn_rebuild.setMinimumWidth(70) self.btn_addPreset.setMinimumWidth(70) self.btn_delPreset.setMinimumWidth(70) self.btn_loadPreset.setMinimumWidth(140) self.btn_savePreset.setMinimumWidth(140) self.btn_apply_smartCorner.setMinimumWidth(140) self.btn_remove_smartCorner.setMinimumWidth(140) self.btn_remove_presetCorner.setMinimumWidth(140) self.btn_getBuilder.setCheckable(True) self.btn_getBuilder.setChecked(False) self.btn_findBuilder.setEnabled(False) self.btn_apply_round.setEnabled(False) self.btn_getBuilder.clicked.connect(lambda: self.getBuilder()) self.btn_addPreset.clicked.connect(lambda: self.preset_modify(False)) self.btn_delPreset.clicked.connect(lambda: self.preset_modify(True)) self.btn_resetPreset.clicked.connect(lambda: self.preset_reset()) self.btn_loadPreset.clicked.connect(lambda: self.preset_load()) self.btn_savePreset.clicked.connect(lambda: self.preset_save()) self.btn_apply_smartCorner.clicked.connect(lambda: self.apply_SmartCorner(False)) self.btn_remove_smartCorner.clicked.connect(lambda: self.apply_SmartCorner(True)) self.btn_remove_presetCorner.clicked.connect(lambda: self.remove_SmartCorner()) #self.btn_apply_round.clicked.connect(lambda: self.apply_round()) self.btn_apply_mitre.clicked.connect(lambda: self.apply_mitre(False)) self.btn_apply_overlap.clicked.connect(lambda: self.apply_mitre(True)) self.btn_apply_trap.clicked.connect(lambda: self.apply_trap()) self.btn_rebuild.clicked.connect(lambda: self.rebuild()) # -- Preset Table self.tab_presets = TRTableView(None) self.preset_reset() # -- Build Layout self.lay_head.addWidget(QtGui.QLabel('Value Presets:'), 0,0,1,8) self.lay_head.addWidget(self.btn_loadPreset, 1,0,1,4) self.lay_head.addWidget(self.btn_savePreset, 1,4,1,4) self.lay_head.addWidget(self.btn_addPreset, 2,0,1,2) self.lay_head.addWidget(self.btn_delPreset, 2,2,1,2) self.lay_head.addWidget(self.btn_resetPreset, 2,4,1,4) self.lay_head.addWidget(self.tab_presets, 3,0,5,8) self.lay_head.addWidget(QtGui.QLabel('Corner Actions:'),10, 0, 1, 8) self.lay_head.addWidget(self.btn_apply_round, 11, 0, 1, 2) self.lay_head.addWidget(self.btn_apply_mitre, 11, 2, 1, 2) self.lay_head.addWidget(self.btn_apply_overlap, 11, 4, 1, 2) self.lay_head.addWidget(self.btn_apply_trap, 11, 6, 1, 2) self.lay_head.addWidget(self.btn_rebuild, 12, 0, 1, 8) self.lay_head.addWidget(QtGui.QLabel('Smart Corner:'), 13,0,1,8) self.lay_head.addWidget(QtGui.QLabel('Builder: '), 14,0,1,1) self.lay_head.addWidget(self.edt_glyphName, 14,1,1,3) self.lay_head.addWidget(self.btn_getBuilder, 14,4,1,2) self.lay_head.addWidget(self.btn_findBuilder, 14,6,1,2) self.lay_head.addWidget(self.btn_remove_smartCorner, 15,0,1,4) self.lay_head.addWidget(self.btn_remove_presetCorner, 15,4,1,4) self.lay_head.addWidget(self.btn_apply_smartCorner, 16,0,1,8) self.addLayout(self.lay_head) # - Presets management ------------------------------------------------ def preset_reset(self): self.builder = None self.active_font = pFont() self.font_masters = self.active_font.masters() self.table_dict = self.empty_preset(0) self.tab_presets.clear() self.tab_presets.setTable(self.table_dict, sortData=(False, False)) self.tab_presets.horizontalHeader().setStretchLastSection(False) self.tab_presets.verticalHeader().hide() #self.tab_presets.resizeColumnsToContents() def preset_modify(self, delete=False): table_rawList = self.tab_presets.getTable(raw=True) if delete: for selection in self.tab_presets.selectionModel().selectedIndexes: table_rawList.pop(selection.row()) print selection.row() new_entry = OrderedDict() for key, data in table_rawList: new_entry[key] = OrderedDict(data) if not delete: new_entry[len(table_rawList)] = self.empty_preset(len(table_rawList)).items()[0][1] self.tab_presets.setTable(new_entry, sortData=(False, False)) def preset_load(self): fontPath = os.path.split(self.active_font.fg.path)[0] fname = QtGui.QFileDialog.getOpenFileName(self.upper_widget, 'Load presets from file', fontPath, 'TypeRig JSON (*.json)') if fname != None: with open(fname, 'r') as importFile: imported_data = json.load(importFile) # - Convert Data new_data = OrderedDict() for key, data in imported_data: new_data[key] = OrderedDict(data) self.tab_presets.setTable(new_data, sortData=(False, False)) print 'LOAD:\t Font:%s; Presets loaded from: %s.' %(self.active_font.name, fname) def preset_save(self): fontPath = os.path.split(self.active_font.fg.path)[0] fname = QtGui.QFileDialog.getSaveFileName(self.upper_widget, 'Save presets to file', fontPath, 'TypeRig JSON (*.json)') if fname != None: with open(fname, 'w') as exportFile: json.dump(self.tab_presets.getTable(raw=True), exportFile) print 'SAVE:\t Font:%s; Presets saved to: %s.' %(self.active_font.name, fname) def getPreset(self): table_raw = self.tab_presets.getTable(raw=True) ''' try: active_preset_index = self.tab_presets.selectionModel().selectedIndexes[0].row() except IndexError: active_preset_index = None ''' active_preset_index = self.tab_presets.selectionModel().selectedIndexes[0].row() if active_preset_index is None: active_preset_index = self.last_preset else: self.last_preset = active_preset_index return dict(table_raw[active_preset_index][1][1:]) # - Basic Corner ------------------------------------------------ def apply_mitre(self, doKnot=False): # - Init process_glyphs = getProcessGlyphs(pMode) active_preset = self.getPreset() # - Process if len(process_glyphs): for glyph in process_glyphs: if glyph is not None: wLayers = glyph._prepareLayers(pLayers) for layer in reversed(wLayers): if layer in active_preset.keys(): selection = glyph.selectedNodes(layer, filterOn=True, extend=eNode, deep=True) for node in reversed(selection): if not doKnot: node.cornerMitre(float(active_preset[layer])) else: node.cornerMitre(-float(active_preset[layer]), True) action = 'Mitre Corner' if not doKnot else 'Overlap Corner' glyph.update() glyph.updateObject(glyph.fl, '%s @ %s.' %(action, '; '.join(active_preset.keys()))) def apply_trap(self): # - Init process_glyphs = getProcessGlyphs(pMode) active_preset = self.getPreset() # - Process if len(process_glyphs): for glyph in process_glyphs: if glyph is not None: wLayers = glyph._prepareLayers(pLayers) for layer in reversed(wLayers): if layer in active_preset.keys(): selection = glyph.selectedNodes(layer, filterOn=True, extend=eNode, deep=True) for node in reversed(selection): preset_values = tuple([float(item.strip()) for item in active_preset[layer].split(',')]) node.cornerTrapInc(*preset_values) glyph.update() glyph.updateObject(glyph.fl, '%s @ %s.' %('Ink Trap', '; '.join(active_preset.keys()))) def rebuild(self): # - Init process_glyphs = getProcessGlyphs(pMode) # - Process if len(process_glyphs): for glyph in process_glyphs: if glyph is not None: wLayers = glyph._prepareLayers(pLayers) for layer in wLayers: selection = glyph.selectedNodes(layer, filterOn=True, extend=eNode, deep=True) if len(selection) > 1: node_first = selection[0] node_last = selection[-1] line_in = node_first.getPrevLine() if node_first.getPrevOn(False) not in selection else node_first.getNextLine() line_out = node_last.getNextLine() if node_last.getNextOn(False) not in selection else node_last.getPrevLine() crossing = line_in & line_out node_first.smartReloc(*crossing) node_first.parent.removeNodesBetween(node_first.fl, node_last.getNextOn()) glyph.update() glyph.updateObject(glyph.fl, 'Rebuild corner: %s nodes reduced; At layers: %s' %(len(selection), '; '.join(wLayers))) # - Smart Corner ------------------------------------------------ def getBuilder(self): if self.btn_getBuilder.isChecked(): if len(self.edt_glyphName.text): builder_glyph = self.active_font.glyph(self.edt_glyphName.text) else: builder_glyph = eGlyph() self.edt_glyphName.setText(builder_glyph.name) if builder_glyph is not None: temp_builder = builder_glyph.getBuilders() if len(temp_builder.keys()) and filter_name in temp_builder.keys(): self.builder = temp_builder[filter_name][0] self.btn_getBuilder.setText('Release') else: self.builder = None self.edt_glyphName.clear() self.btn_getBuilder.setText('Set Builder') def process_setFilter(self, glyph, shape, layer, builder, suffix='.old'): new_container = fl6.flShape() new_container.shapeData.name = shape.shapeData.name if len(shape.shapeData.name): shape.shapeData.name += suffix #!!!! TODO: transformation copy and delete new_container.include(shape, glyph.layer(layer)) new_container.shapeBuilder = builder.clone() new_container.update() glyph.layer(layer).addShape(new_container) #glyph.layer(layer).update() def process_smartCorner(self, glyph, preset): wLayers = glyph._prepareLayers(pLayers) nodes_at_shapes = {} # - Build selection for work_layer in wLayers: if work_layer in preset.keys(): # - Init selection_deep = [(item[0], item[2]) for item in glyph.selectedAtShapes(layer=work_layer, index=False, deep=True)] selection_shallow = [(item[0], item[2]) for item in glyph.selectedAtShapes(layer=work_layer, index=False, deep=False)] selection = selection_deep + selection_shallow # - Build note to shape reference nodes_at_shapes[work_layer] = [(shape, [node for shape, node in list(nodes)]) for shape, nodes in groupby(selection, key=itemgetter(0))] # - Process glyph for work_layer in wLayers: if work_layer in nodes_at_shapes.keys(): # - Build filter if not present for work_shape, node_list in nodes_at_shapes[work_layer]: if len(glyph.containers(work_layer)): for container in glyph.containers(work_layer): if work_shape not in container.includesList: self.process_setFilter(glyph, work_shape, work_layer, self.builder) else: self.process_setFilter(glyph, work_shape, work_layer, self.builder) # - Process the nodes process_nodes = [pNode(node) for shape, node_list in nodes_at_shapes[work_layer] for node in node_list] for work_node in process_nodes: angle_value = preset[work_layer] if 'DEL' not in angle_value.upper(): work_node.setSmartAngle(float(angle_value)) else: work_node.delSmartAngle() #glyph.update() #glyph.updateObject(glyph.fl, 'DONE:\t Glyph: %s; Filter: Smart corner; Parameters: %s' %(glyph.name, preset)) def apply_SmartCorner(self, remove=False): # NOTE: apply and remove here apply only to soelected nodes. if self.builder is not None: # - Init process_glyphs = getProcessGlyphs(pMode) active_preset = self.getPreset() if remove: # Build a special preset that deletes active_preset = {key:'DEL' for key in active_preset.keys()} # - Process if len(process_glyphs): for work_glyph in process_glyphs: if work_glyph is not None: self.process_smartCorner(work_glyph, active_preset) self.update_glyphs(process_glyphs, True) print 'DONE:\t Filter: Smart Corner; Glyphs: %s' %'; '.join([g.name for g in process_glyphs]) else: print 'ERROR:\t Please specify a Glyph with suitable Shape Builder (Smart corner) first!' def remove_SmartCorner(self): # Finds active preset in glyphs smart corners and removes them # - Init process_glyphs = getProcessGlyphs(pMode) active_preset = self.getPreset() # - Process if len(process_glyphs): for work_glyph in process_glyphs: if work_glyph is not None: # - Init wLayers = work_glyph._prepareLayers(pLayers) smart_corners, target_corners = [], [] # - Get all smart nodes/corners for layer in wLayers: for builder in work_glyph.getBuilders(layer)[filter_name]: smart_corners += builder.getSmartNodes() if len(smart_corners): for node in smart_corners: wNode = eNode(node) if wNode.getSmartAngleRadius() == float(active_preset[layer]): wNode.delSmartAngle() self.update_glyphs(process_glyphs, True) print 'DONE:\t Filter: Remove Smart Corner; Glyphs: %s' %'; '.join([g.name for g in process_glyphs]) def update_glyphs(self, glyphs, complete=False): for glyph in glyphs: glyph.update() if not complete: # Partial update - contour only for contour in glyph.contours(): contour.changed() else: # Full update - with undo snapshot glyph.updateObject(glyph.fl, verbose=False)