diff options
Diffstat (limited to 'Lib/defconQt')
| -rw-r--r-- | Lib/defconQt/__main__.py | 15 | ||||
| -rw-r--r-- | Lib/defconQt/fontView.py | 56 | ||||
| -rw-r--r-- | Lib/defconQt/glyphView.py | 269 | ||||
| -rw-r--r-- | Lib/defconQt/objects/defcon.py | 15 | ||||
| -rw-r--r-- | Lib/defconQt/objects/sizeGripItem.py | 205 | ||||
| -rw-r--r-- | Lib/defconQt/util/glyphList.py | 21 |
6 files changed, 460 insertions, 121 deletions
diff --git a/Lib/defconQt/__main__.py b/Lib/defconQt/__main__.py index 270764d..517f8ce 100644 --- a/Lib/defconQt/__main__.py +++ b/Lib/defconQt/__main__.py @@ -1,9 +1,10 @@ -from defconQt.objects.defcon import TFont from defconQt import representationFactories from defconQt import icons_db # noqa from defconQt.fontView import Application, MainWindow +from defconQt.objects.defcon import TFont import sys import os +from PyQt5.QtCore import QSettings from PyQt5.QtGui import QIcon @@ -17,10 +18,20 @@ def main(): representationFactories.registerAllFactories() app = Application(sys.argv) # TODO: http://stackoverflow.com/a/21330349/2037879 - app.setOrganizationName("A. Tétar & Co.") + app.setOrganizationName("TruFont") app.setOrganizationDomain("trufont.github.io") app.setApplicationName("TruFont") app.setWindowIcon(QIcon(":/resources/app.png")) + settings = QSettings() + glyphListPath = settings.value("settings/glyphListPath", "", type=str) + if glyphListPath and os.path.exists(glyphListPath): + from defconQt.util import glyphList + try: + glyphList = glyphList.parseGlyphList(glyphListPath) + except Exception as e: + print(e) + else: + app.GL2UV = glyphList window = MainWindow(font) window.show() sys.exit(app.exec_()) diff --git a/Lib/defconQt/fontView.py b/Lib/defconQt/fontView.py index 56bfbfe..70f7f44 100644 --- a/Lib/defconQt/fontView.py +++ b/Lib/defconQt/fontView.py @@ -19,8 +19,8 @@ from PyQt5.QtGui import ( QTextCursor) from PyQt5.QtWidgets import ( QAbstractItemView, QAction, QApplication, QCheckBox, QComboBox, QDialog, - QDialogButtonBox, QFileDialog, QGridLayout, QGroupBox, QLabel, QLineEdit, - QListWidget, QListWidgetItem, QMainWindow, QMenu, QMessageBox, + QDialogButtonBox, QFileDialog, QGridLayout, QGroupBox, QHBoxLayout, QLabel, + QLineEdit, QListWidget, QListWidgetItem, QMainWindow, QMenu, QMessageBox, QPlainTextEdit, QPushButton, QRadioButton, QSlider, QSplitter, QTabWidget, QTextEdit, QToolTip, QTreeView, QVBoxLayout, QWidget) from collections import OrderedDict @@ -74,6 +74,7 @@ class Application(QApplication): super(Application, self).__init__(*args, **kwargs) self._currentGlyph = None self._currentMainWindow = None + self.GL2UV = None def allFonts(self): fonts = [] @@ -1342,17 +1343,36 @@ class GlyphSetTab(QWidget): importMenu.addAction("Import from current font", self.importFromCurrentFont) self.importButton.setMenu(importMenu) - - mainLayout = QGridLayout() + glyphListPath = settings.value("settings/glyphListPath", type=str) + self.glyphListBox = QCheckBox("Glyph list path:", self) + self.glyphListBox.setChecked(bool(glyphListPath)) + self.glyphListEdit = QLineEdit(glyphListPath, self) + self.glyphListEdit.setEnabled(bool(glyphListPath)) + self.glyphListEdit.setReadOnly(True) + self.glyphListButton = QPushButton("Browse…", self) + self.glyphListButton.setEnabled(bool(glyphListPath)) + self.glyphListButton.pressed.connect(self.getGlyphList) + self.glyphListButton.setFixedWidth(72) + self.glyphListBox.toggled.connect(self.glyphListEdit.setEnabled) + self.glyphListBox.toggled.connect(self.glyphListButton.setEnabled) + + firstLayout = QGridLayout() l = 0 - mainLayout.addWidget(self.defaultGlyphSetBox, l, 0, 1, 2) - mainLayout.addWidget(self.defaultGlyphSetDrop, l, 3, 1, 3) + firstLayout.addWidget(self.defaultGlyphSetBox, l, 0, 1, 2) + firstLayout.addWidget(self.defaultGlyphSetDrop, l, 3, 1, 3) l += 1 - mainLayout.addWidget(splitter, l, 0, 1, 6) + firstLayout.addWidget(splitter, l, 0, 1, 6) l += 1 - mainLayout.addWidget(self.addGlyphSetButton, l, 0) - mainLayout.addWidget(self.removeGlyphSetButton, l, 1) - mainLayout.addWidget(self.importButton, l, 2) + firstLayout.addWidget(self.addGlyphSetButton, l, 0) + firstLayout.addWidget(self.removeGlyphSetButton, l, 1) + firstLayout.addWidget(self.importButton, l, 2) + secondLayout = QHBoxLayout() + secondLayout.addWidget(self.glyphListBox, 0) + secondLayout.addWidget(self.glyphListEdit, 1) + secondLayout.addWidget(self.glyphListButton, 2) + mainLayout = QVBoxLayout() + mainLayout.addLayout(firstLayout) + mainLayout.addLayout(secondLayout) self.setLayout(mainLayout) def addGlyphSet(self, glyphNames=[], glyphSetName="New glyph set"): @@ -1384,7 +1404,7 @@ class GlyphSetTab(QWidget): del self.glyphSets[self._cachedName] def importFromCurrentFont(self): - currentMainWindow = QApplication.instance().currentMainWindow + currentMainWindow = QApplication.instance().currentMainWindow() glyphs = currentMainWindow.getGlyphs() info = currentMainWindow.font.info name = "%s %s" % (info.familyName, info.styleName) @@ -1419,6 +1439,17 @@ class GlyphSetTab(QWidget): index += 1 settings.endArray() + def getGlyphList(self): + fileFormats = ( + "Text file (*.txt)", + "All files (*.*)", + ) + path, _ = QFileDialog.getOpenFileName( + self, "Open File", '', ";;".join(fileFormats) + ) + if path: + self.glyphListEdit.setText(path) + def writeValues(self): # store content of the textEdit in the glyphSet dict glyphNames = self.glyphSetContents.toPlainText().split() @@ -1433,6 +1464,9 @@ class GlyphSetTab(QWidget): defaultGlyphSet = self.defaultGlyphSetDrop.currentText() if defaultGlyphSet != latinDefault.name: settings.setValue("settings/defaultGlyphSet", defaultGlyphSet) + glyphListPath = self.glyphListEdit.text() + if glyphListPath: + settings.setValue("settings/glyphListPath", glyphListPath) class MetricsWindowTab(QTabWidget): diff --git a/Lib/defconQt/glyphView.py b/Lib/defconQt/glyphView.py index c2aeafe..811dd14 100644 --- a/Lib/defconQt/glyphView.py +++ b/Lib/defconQt/glyphView.py @@ -6,6 +6,7 @@ import pickle from defcon import Anchor, Component from defconQt import icons_db # noqa from defconQt.objects.defcon import TContour, TGlyph +from defconQt.objects.sizeGripItem import ResizeHandleItem, SizeGripItem from defconQt.pens.copySelectionPen import CopySelectionPen from defconQt.util import platformSpecific from fontTools.misc import bezierTools @@ -16,7 +17,7 @@ from PyQt5.QtGui import ( from PyQt5.QtWidgets import ( QAction, QActionGroup, QApplication, QColorDialog, QComboBox, QDialog, QDialogButtonBox, QGraphicsEllipseItem, QGraphicsItem, QGraphicsLineItem, - QGraphicsPathItem, QGraphicsPixmapItem, QGraphicsRectItem, QGraphicsScene, + QGraphicsPathItem, QGraphicsPixmapItem, QGraphicsScene, QGraphicsSimpleTextItem, QGraphicsView, QGridLayout, QLabel, QLineEdit, QListWidget, QMainWindow, QMenu, QRadioButton, QSizePolicy, QStyle, QStyleOptionGraphicsItem, QToolBar, QWidget) @@ -197,6 +198,65 @@ class AddLayerDialog(QDialog): return (name, result) +class LayerActionsDialog(QDialog): + + def __init__(self, currentGlyph, parent=None): + super().__init__(parent) + self.setWindowModality(Qt.WindowModal) + self.setWindowTitle("Layer actions…") + self._workableLayers = [] + for layer in currentGlyph.layerSet: + if layer != currentGlyph.layer: + self._workableLayers.append(layer) + + layout = QGridLayout(self) + + copyBox = QRadioButton("Copy", self) + moveBox = QRadioButton("Move", self) + swapBox = QRadioButton("Swap", self) + self.otherCheckBoxes = (moveBox, swapBox) + copyBox.setChecked(True) + + self.layersList = QListWidget(self) + self.layersList.addItems( + layer.name for layer in self._workableLayers) + if self.layersList.count(): + self.layersList.setCurrentRow(0) + self.layersList.itemDoubleClicked.connect(self.accept) + + buttonBox = QDialogButtonBox( + QDialogButtonBox.Ok | QDialogButtonBox.Cancel) + buttonBox.accepted.connect(self.accept) + buttonBox.rejected.connect(self.reject) + + l = 0 + layout.addWidget(copyBox, l, 0, 1, 2) + layout.addWidget(moveBox, l, 2, 1, 2) + layout.addWidget(swapBox, l, 4, 1, 2) + l += 1 + layout.addWidget(self.layersList, l, 0, 1, 6) + l += 1 + layout.addWidget(buttonBox, l, 0, 1, 6) + self.setLayout(layout) + + @classmethod + def getLayerAndAction(cls, parent, currentGlyph): + dialog = cls(currentGlyph, parent) + result = dialog.exec_() + currentItem = dialog.layersList.currentItem() + newLayer = None + if currentItem is not None: + newLayerName = currentItem.text() + for layer in dialog._workableLayers: + if layer.name == newLayerName: + newLayer = layer + action = "Copy" + for checkBox in dialog.otherCheckBoxes: + if checkBox.isChecked(): + action = checkBox.text() + return (newLayer, action, result) + + class GenericSettings(object): def __init__(self, title, parent, callback): @@ -307,7 +367,8 @@ class MainGfxWindow(QMainWindow): menuBar.addMenu(fileMenu) glyphMenu = QMenu("&Glyph", self) - glyphMenu.addAction("&Go to…", self.changeGlyph, "G") + glyphMenu.addAction("&Go To…", self.changeGlyph, "G") + glyphMenu.addAction("&Layer Actions…", self.layerActions, "L") menuBar.addMenu(glyphMenu) self._displaySettings = DisplayStyleSettings( @@ -364,6 +425,10 @@ class MainGfxWindow(QMainWindow): self.setWindowTitle(glyph.name, glyph.getParent()) self.adjustSize() + def layerActions(self): + if self.view is not None: + self.view.layerActions() + def _changeGlyph(self, glyph): oldView = self.view # Preserve the selected layer (by setting the glyph from that layer) @@ -626,7 +691,7 @@ class OffCurvePointItem(QGraphicsEllipseItem): self._needsUngrab = False def delete(self): - self.parentItem()._CPDeleted() + self.parentItem()._CPDeleted(self) def itemChange(self, change, value): if change == QGraphicsItem.ItemPositionChange: @@ -646,10 +711,9 @@ class OffCurvePointItem(QGraphicsEllipseItem): else: value.setY(0) elif change == QGraphicsItem.ItemPositionHasChanged: - self.parentItem()._CPMoved(value) - # TODO: consider what to do w offCurves - # elif change == QGraphicsItem.ItemSelectedHasChanged: - # pass#self.parentItem()._CPSelChanged(value) + self.parentItem()._CPMoved(self, value) + elif change == QGraphicsItem.ItemSelectedHasChanged: + self.parentItem()._CPSelected(self, value) return value def mousePressEvent(self, event): @@ -775,26 +839,33 @@ class OnCurvePointItem(QGraphicsPathItem): index += 1 return index % len(self._contour.segments) - def _CPDeleted(self): + def _CPDeleted(self, item): + # XXX: is this sufficient guard? + if self.isSelected(): + return pointIndex = self.getPointIndex() children = self.childItems() - selected = 1 - if not (children[1].isVisible() and children[1].isSelected()): - selected = 3 + if item == children[1]: + delta = -1 + segmentOn = 0 + else: + delta = 1 + segmentOn = 3 - firstSibling = self._contour[pointIndex + selected - 2] - secondSibling = self._contour[pointIndex + (selected - 2) * 2] + firstSibling = self._contour.getPoint(pointIndex + delta) + secondSibling = self._contour.getPoint(pointIndex + delta * 2) if (firstSibling.segmentType is None and secondSibling.segmentType is None): # we have two offCurves, wipe them + self._contour.getPoint(pointIndex + segmentOn).segmentType = "line" self._contour.removePoint(firstSibling) self._contour.removePoint(secondSibling) - def _CPMoved(self, newValue): + def _CPMoved(self, item, newValue): pointIndex = self.getPointIndex() children = self.childItems() # nodes are stored after lines (for stacking order) - if children[1].isSelected(): + if item == children[1]: selected = 1 propagate = 3 else: @@ -806,8 +877,8 @@ class OnCurvePointItem(QGraphicsPathItem): if not len(children) > 4: elemIndex = pointIndex - 2 + selected - self._contour[elemIndex].x = self.pos().x() + newValue.x() - self._contour[elemIndex].y = self.pos().y() + newValue.y() + self._contour.getPoint(elemIndex).x = self.pos().x() + newValue.x() + self._contour.getPoint(elemIndex).y = self.pos().y() + newValue.y() if not (self._isSmooth and children[propagate].isVisible()): self.setShallowDirty() return @@ -829,11 +900,21 @@ class OnCurvePointItem(QGraphicsPathItem): children[propagate].setFlag(QGraphicsItem.ItemSendsGeometryChanges) children[propagate - 1].setLine(line.x1(), line.y1(), tmpLine.x2(), tmpLine.y2()) - propagateInContour = pointIndex - 2 + propagate - self._contour[propagateInContour].x = self.pos().x() + tmpLine.x2() - self._contour[propagateInContour].y = self.pos().y() + tmpLine.y2() + propagateIn = pointIndex - 2 + propagate + self._contour.getPoint(propagateIn).x = self.pos().x() + tmpLine.x2() + self._contour.getPoint(propagateIn).y = self.pos().y() + tmpLine.y2() self.setShallowDirty() + def _CPSelected(self, item, value): + pointIndex = self.getPointIndex() + children = self.childItems() + if item == children[1]: + delta = -1 + else: + delta = 1 + + self._contour[pointIndex + delta].selected = value + def itemChange(self, change, value): if change == QGraphicsItem.ItemPositionChange: if self.scene()._integerPlane: @@ -848,12 +929,14 @@ class OnCurvePointItem(QGraphicsPathItem): children = self.childItems() if children[1].isVisible(): prevPos = children[1].pos() - self._contour[pointIndex - 1].x = self.pos().x() + prevPos.x() - self._contour[pointIndex - 1].y = self.pos().y() + prevPos.y() + point = self._contour.getPoint(pointIndex - 1) + point.x = self.pos().x() + prevPos.x() + point.y = self.pos().y() + prevPos.y() if children[3].isVisible(): nextPos = children[3].pos() - self._contour[pointIndex + 1].x = self.pos().x() + nextPos.x() - self._contour[pointIndex + 1].y = self.pos().y() + nextPos.y() + point = self._contour.getPoint(pointIndex + 1) + point.x = self.pos().x() + nextPos.x() + point.y = self.pos().y() + nextPos.y() self.setShallowDirty() elif change == QGraphicsItem.ItemSelectedHasChanged: self._point.selected = value @@ -886,11 +969,11 @@ class OnCurvePointItem(QGraphicsPathItem): scene = self.scene() scene._blocked = True # if we have line segment, insert offCurve points - insertIndex = (ptIndex + (i - 1) // 2) % len(self._contour) - if self._contour[insertIndex].segmentType == "line": - nextToCP = self._contour[(ptIndex - 2 + i) % len(self._contour)] + insertIndex = ptIndex + (i - 1) // 2 + if self._contour.getPoint(insertIndex).segmentType == "line": + nextToCP = self._contour.getPoint(ptIndex - 2 + i) assert(nextToCP.segmentType is not None) - self._contour[insertIndex].segmentType = "curve" + self._contour.getPoint(insertIndex).segmentType = "curve" if i == 1: first, second = ( self._point.x, self._point.y), (nextToCP.x, nextToCP.y) @@ -1104,86 +1187,32 @@ class VGuidelinesTextItem(QGraphicsSimpleTextItem): self.setFont(font) -class ResizeHandleItem(QGraphicsRectItem): - - def __init__(self, parent=None): - super(QGraphicsRectItem, self).__init__(parent) - self.setPointPath() - self.setBrush(QBrush(QColor(60, 60, 60))) - self.setPen(QPen(Qt.NoPen)) - self.setFlag(QGraphicsItem.ItemIgnoresParentOpacity) - self.setFlag(QGraphicsItem.ItemIsMovable) - # self.setFlag(QGraphicsItem.ItemIsSelectable) - self.setCursor(Qt.SizeFDiagCursor) - - rect = self.parentItem().boundingRect() - self.setPos(rect.width(), rect.height()) - - def itemChange(self, change, value): - if change == QGraphicsItem.ItemSelectedChange: - if not value: - self.setVisible(value) - return value - - def mouseMoveEvent(self, event): - self.parentItem()._pixmapGeometryChanged(event) - - def setPointPath(self, scale=None): - if scale is None: - scene = self.scene() - if scene is not None: - scale = scene.getViewScale() - else: - scale = 1 - if scale > 4: - scale = 4 - self.prepareGeometryChange() - self.setRect(-onHalf / scale, -onHalf / scale, - onWidth / scale, onHeight / scale) - - class PixmapItem(QGraphicsPixmapItem): - def __init__(self, x, y, pixmap, parent=None): + def __init__(self, x, y, pixmap, scale, parent=None): super(QGraphicsPixmapItem, self).__init__(pixmap, parent) - self.setPos(x, y) - self.setFlag(QGraphicsItem.ItemIsMovable) + self._pixmap = pixmap + self.setOffset(x, -y) self.setFlag(QGraphicsItem.ItemIsSelectable) + self.setShapeMode(QGraphicsPixmapItem.BoundingRectShape) self.setTransform(QTransform().fromScale(1, -1)) - self.setOpacity(.5) + self.setOpacity(.6) self.setZValue(-1) - - rect = self.boundingRect() - self._rWidth = rect.width() - self._rHeight = rect.height() - handle = ResizeHandleItem(self) - handle.setVisible(False) - - def _pixmapGeometryChanged(self, event): - modifiers = event.modifiers() - pos = event.scenePos() - if modifiers & Qt.ControlModifier: - # rotate - refLine = QLineF(self.x(), self.y(), self.x() + - self._rWidth, self.y() - self._rHeight) - curLine = QLineF(self.x(), self.y(), pos.x(), pos.y()) - self.setRotation(refLine.angleTo(curLine)) - else: - # scale - dy = (pos.y() - self.y()) / self._rHeight - if modifiers & Qt.ShiftModifier: - # keep original aspect ratio - dx = -dy - else: - dx = (pos.x() - self.x()) / self._rWidth - self.setTransform(QTransform().fromScale(dx, dy)) - event.accept() + sizeGripItem = SizeGripItem(scale, self) + sizeGripItem.setVisible(False) + + def setRect(self, rect): + dTopLeft = rect.topLeft() - self.pos() + if not dTopLeft.isNull(): + self.setOffset(dTopLeft) + self.setPixmap(self._pixmap.scaled( + rect.size().toSize() + )) def itemChange(self, change, value): if change == QGraphicsItem.ItemSelectedChange: - children = self.childItems() - if not children[0].isUnderMouse(): - children[0].setVisible(value) + sizeGripItem = self.childItems()[0] + sizeGripItem.setVisible(value) return value @@ -1247,8 +1276,8 @@ class GlyphScene(QGraphicsScene): else: return pos = event.scenePos() - newPix = PixmapItem(pos.x(), pos.y(), dragPix) - self._addRegisterItem(newPix) + pixmapItem = PixmapItem(pos.x(), pos.y(), dragPix, self.getViewScale()) + self._addRegisterItem(pixmapItem) event.acceptProposedAction() def getItemForPoint(self, point): @@ -1289,8 +1318,6 @@ class GlyphScene(QGraphicsScene): elif isinstance(item, (AnchorItem, ComponentItem, OffCurvePointItem)): item.delete() - elif isinstance(item, PixmapItem): - self.removeItem(item) self._blocked = False self._glyphObject.dirty = True event.accept() @@ -1776,6 +1803,8 @@ class GlyphScene(QGraphicsScene): for contour in glyph: segments = contour.segments for index, seg in enumerate(segments): + if seg[-1].segmentType == "move": + continue prev = segments[index - 1][-1] if len(seg) == 3: i = self.computeIntersections( @@ -1805,6 +1834,9 @@ class GlyphScene(QGraphicsScene): for dot in self._knifeDots: self.removeItem(dot) self._knifeDots = [] + # no-move clicks + if self._cachedIntersections is None: + return # reverse so as to not invalidate our cached segment indexes for loc, ts in reversed(list(self._cachedIntersections.items())): contour, index = loc @@ -2181,14 +2213,13 @@ class GlyphView(QGraphicsView): def updateActiveLayerPath(self): self.updateLayerPath( self._layer, representationKey="defconQt.NoComponentsQPainterPath") + self.addStartPoints() def updateLayerPath(self, layer, representationKey="defconQt.QPainterPath"): glyph = layer[self._name] - # scene = self.scene() # unused path = glyph.getRepresentation(representationKey) self._sceneItems[layer].setPath(path) - self.addStartPoints() def _getSceneItems(self, key, clear=False): items = self._sceneItems.get(key, None) @@ -2317,6 +2348,34 @@ class GlyphView(QGraphicsView): component.baseGlyph = newGlyph.name self._glyph.appendComponent(component) + def layerActions(self): + newLayer, action, ok = LayerActionsDialog.getLayerAndAction( + self, self._glyph) + if ok and newLayer is not None: + # TODO: whole glyph for now, but consider selection too + if not self._glyph.name in newLayer: + newLayer.newGlyph(self._glyph.name) + otherGlyph = newLayer[self._glyph.name] + otherGlyph.disableNotifications() + if action == "Swap": + tempGlyph = TGlyph() + otherGlyph.drawPoints(tempGlyph.getPointPen()) + tempGlyph.width = otherGlyph.width + otherGlyph.clearContours() + self._glyph.drawPoints(otherGlyph.getPointPen()) + otherGlyph.width = self._glyph.width + if action != "Copy": + self._glyph.disableNotifications() + self._glyph.clearContours() + # XXX: we shouldn't have to do this manually but it seems there + # is a timing problem + self._glyph.destroyAllRepresentations() + if action == "Swap": + tempGlyph.drawPoints(self._glyph.getPointPen()) + self._glyph.width = tempGlyph.width + self._glyph.enableNotifications() + otherGlyph.enableNotifications() + def _makeLayerGlyph(self, layer): name = self._name glyph = layer.newGlyph(name) diff --git a/Lib/defconQt/objects/defcon.py b/Lib/defconQt/objects/defcon.py index 496f1c6..48de173 100644 --- a/Lib/defconQt/objects/defcon.py +++ b/Lib/defconQt/objects/defcon.py @@ -1,6 +1,7 @@ from defcon import Font, Contour, Glyph, Point from defcon.objects.base import BaseObject -from fontTools.agl import AGL2UV +from PyQt5.QtWidgets import QApplication +import fontTools class TFont(Font): @@ -59,10 +60,15 @@ class TGlyph(Glyph): dirty = property(BaseObject._get_dirty, _set_dirty) def autoUnicodes(self): + app = QApplication.instance() + if app.GL2UV is not None: + GL2UV = app.GL2UV + else: + GL2UV = fontTools.agl.AGL2UV hexes = "ABCDEF0123456789" name = self.name - if name in AGL2UV: - uni = AGL2UV[name] + if name in GL2UV: + uni = GL2UV[name] elif (name.startswith("uni") and len(name) == 7 and all(c in hexes for c in name[3:])): uni = int(name[3:], 16) @@ -93,6 +99,9 @@ class TContour(Contour): selected=point.selected) pointPen.endPath() + def getPoint(self, index): + return self[index % len(self)] + class TPoint(Point): __slots__ = ["_selected"] diff --git a/Lib/defconQt/objects/sizeGripItem.py b/Lib/defconQt/objects/sizeGripItem.py new file mode 100644 index 0000000..94e07ba --- /dev/null +++ b/Lib/defconQt/objects/sizeGripItem.py @@ -0,0 +1,205 @@ +# SizeGripItem - A size grip QGraphicsItem for interactive resizing. +# +# Python port by Felipe Correa da Silva Sanches +# based on the original C++ code by Cesar L. B. Silveira +# +# Copyright 2011, Cesar L. B. Silveira. +# Copyright 2015, Felipe Correa da Silva Sanches <juca@members.fsf.org>. +# Copyright 2015, Adrien Tétar. +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. + +from PyQt5.QtCore import Qt +from PyQt5.QtGui import QBrush, QPen +from PyQt5.QtWidgets import QGraphicsItem, QGraphicsRectItem + +Top = 1 << 0 +Bottom = 1 << 1 +Left = 1 << 2 +Right = 1 << 3 +Center = 1 << 4 +TopLeft = Top | Left +BottomLeft = Bottom | Left +TopRight = Top | Right +BottomRight = Bottom | Right + +possibleFlags = (Top, Bottom, Left, TopLeft, BottomLeft, Right, TopRight, + BottomRight, Center) + + +class ResizeHandleItem(QGraphicsRectItem): + def __init__(self, positionFlags, scale, parent): + super(ResizeHandleItem, self).__init__(parent) + self.setPointPath(scale) + self.positionFlags = positionFlags + self.setBrush(QBrush(Qt.lightGray)) + self.setFlag(QGraphicsItem.ItemIsMovable) + self.setFlag(QGraphicsItem.ItemSendsGeometryChanges) + if self.positionFlags in (Top, Bottom): + cursor = Qt.SizeVerCursor + elif self.positionFlags in (Left, Right): + cursor = Qt.SizeHorCursor + elif self.positionFlags in (BottomLeft, TopRight): + cursor = Qt.SizeBDiagCursor + elif self.positionFlags in (TopLeft, BottomRight): + cursor = Qt.SizeFDiagCursor + elif self.positionFlags == Center: + cursor = Qt.SizeAllCursor + self.setCursor(cursor) + + def itemChange(self, change, value): + if change == QGraphicsItem.ItemPositionChange: + return self.restrictPosition(value) + return value + + def mouseMoveEvent(self, event): + pos = self.mapToParent(event.pos()) + parent = self.parentItem() + if self.positionFlags == TopLeft: + parent.setTopLeft(pos) + elif self.positionFlags == Top: + parent.setTop(pos.y()) + elif self.positionFlags == TopRight: + parent.setTopRight(pos) + elif self.positionFlags == Right: + parent.setRight(pos.x()) + elif self.positionFlags == BottomRight: + parent.setBottomRight(pos) + elif self.positionFlags == Bottom: + parent.setBottom(pos.y()) + elif self.positionFlags == BottomLeft: + parent.setBottomLeft(pos) + elif self.positionFlags == Left: + parent.setLeft(pos.x()) + elif self.positionFlags == Center: + parent.setCenter(pos) + parent.doResize() + + def restrictPosition(self, newPos): + parent = self.parentItem() + retVal = newPos + + if self.positionFlags & Top or self.positionFlags & Bottom: + retVal.setY(newPos.y()) + if self.positionFlags & Left or self.positionFlags & Right: + retVal.setX(newPos.x()) + + if self.positionFlags & Top and retVal.y() > parent.rect.bottom(): + retVal.setY(parent.rect.bottom()) + elif self.positionFlags & Bottom and retVal.y() < parent.rect.top(): + retVal.setY(parent.rect.top()) + + if self.positionFlags & Left and retVal.x() > parent.rect.right(): + retVal.setX(parent.rect.right()) + elif self.positionFlags & Right and retVal.x() < parent.rect.left(): + retVal.setX(parent.rect.left()) + + return retVal + + def setPointPath(self, scale=None): + if scale is None: + scene = self.scene() + if scene is not None: + scale = scene.getViewScale() + else: + scale = 1 + if scale > 4: + scale = 4 + self.prepareGeometryChange() + self.setPen(QPen(Qt.black, 1.0 / scale)) + self.setRect(-4 / scale, -4 / scale, 8 / scale, 8 / scale) + + +class SizeGripItem(QGraphicsItem): + def __init__(self, scale, parent): + super(SizeGripItem, self).__init__(parent) + self.setFlag(QGraphicsItem.ItemIgnoresParentOpacity) + + for flag in possibleFlags: + ResizeHandleItem(flag, scale, self) + self.updateBoundingRect() + + def boundingRect(self): + return self.rect + + def paint(self, painter, option, widget): + pass + + def setTopLeft(self, pos): + self.rect.setTopLeft(pos) + + def setTop(self, y): + self.rect.setTop(y) + + def setTopRight(self, pos): + self.rect.setTopRight(pos) + + def setRight(self, x): + self.rect.setRight(x) + + def setBottomRight(self, pos): + self.rect.setBottomRight(pos) + + def setBottom(self, y): + self.rect.setBottom(y) + + def setBottomLeft(self, pos): + self.rect.setBottomLeft(pos) + + def setLeft(self, x): + self.rect.setLeft(x) + + def setCenter(self, pos): + self.rect.moveCenter(pos) + + def doResize(self): + self.parentItem().setRect(self.rect) + self.updateHandleItemPositions() + + def updateBoundingRect(self): + self.rect = self.parentItem().boundingRect() + self.updateHandleItemPositions() + + def updateHandleItemPositions(self): + for item in self.childItems(): + item.setFlag(QGraphicsItem.ItemSendsGeometryChanges, False) + flags = item.positionFlags + if flags == TopLeft: + item.setPos(self.rect.topLeft()) + elif flags == Top: + item.setPos(self.rect.left() + self.rect.width() / 2 - 1, + self.rect.top()) + elif flags == TopRight: + item.setPos(self.rect.topRight()) + elif flags == Right: + item.setPos(self.rect.right(), + self.rect.top() + self.rect.height() / 2 - 1) + elif flags == BottomRight: + item.setPos(self.rect.bottomRight()) + elif flags == Bottom: + item.setPos(self.rect.left() + self.rect.width() / 2 - 1, + self.rect.bottom()) + elif flags == BottomLeft: + item.setPos(self.rect.bottomLeft()) + elif flags == Left: + item.setPos(self.rect.left(), + self.rect.top() + self.rect.height() / 2 - 1) + elif flags == Center: + item.setPos(self.rect.center()) + item.setFlag(QGraphicsItem.ItemSendsGeometryChanges, True) diff --git a/Lib/defconQt/util/glyphList.py b/Lib/defconQt/util/glyphList.py new file mode 100644 index 0000000..ee97d78 --- /dev/null +++ b/Lib/defconQt/util/glyphList.py @@ -0,0 +1,21 @@ +import re + +_parseGL_RE = re.compile("([A-Za-z_0-9.]+);([0-9A-F]{4})") + + +def parseGlyphList(path): + GL2UV = {} + with open(path) as file: + for line in file: + if not line or line[:1] == '#': + continue + m = _parseGL_RE.match(line) + if not m: + raise SyntaxError("syntax error in glyphlist: %s" + .format(repr(line[:20]))) + glyphName = m.group(1) + if glyphName in GL2UV: + print("warning: glyphName redefined in glyphList: {}".format( + glyphName)) + GL2UV[glyphName] = int(m.group(2), 16) + return GL2UV |
