diff options
| author | Adrien Tétar | 2015-05-09 18:46:10 +0200 |
|---|---|---|
| committer | Adrien Tétar | 2015-05-09 18:46:10 +0200 |
| commit | e83660ec042e9d6fe0daab5459c19c681639a4a9 (patch) | |
| tree | b4911a11b65c4a952162f973f691d234eb7abdb0 | |
| parent | 7dd7d2f4ccb2337f3f14e3317c7f328023420bb8 (diff) | |
| download | trufont-e83660ec042e9d6fe0daab5459c19c681639a4a9.tar.bz2 | |
Checkpoint
| -rw-r--r-- | Lib/defconQt/fontView.py | 41 | ||||
| -rw-r--r-- | Lib/defconQt/fontinfo.py | 36 | ||||
| -rw-r--r-- | Lib/defconQt/representationFactories/__init__.py | 21 | ||||
| -rw-r--r-- | Lib/defconQt/spacecenter.py | 197 | ||||
| -rw-r--r-- | Lib/defconQt/svgViewer.py | 188 | ||||
| -rw-r--r-- | Lib/defconQt/syntaxhighlighter.py | 1 |
6 files changed, 343 insertions, 141 deletions
diff --git a/Lib/defconQt/fontView.py b/Lib/defconQt/fontView.py index 61e7792..a13a60a 100644 --- a/Lib/defconQt/fontView.py +++ b/Lib/defconQt/fontView.py @@ -37,6 +37,12 @@ class CharacterWidget(QWidget): def updateFont(self, font): self.font = font self.glyphs = [font[k] for k in font.unicodeData.sortGlyphNames(font.keys(), glyphSortDescriptors)] + self.adjustSize() + self.update() + + def updateGlyphs(self): + self.glyphs = [self.font[k] for k in self.font.unicodeData.sortGlyphNames(self.font.keys(), glyphSortDescriptors)] + self.adjustSize() self.update() def _sizeEvent(self, width, squareSize=None): @@ -199,9 +205,9 @@ class MainWindow(QMainWindow): fontMenu.addAction("Font &info", self.fontInfo, "Ctrl+I") fontMenu.addAction("Font &features", self.fontFeatures, "Ctrl+F") + fontMenu.addAction("&Add glyph", self.addGlyph, "Ctrl+U") fontMenu.addSeparator() fontMenu.addAction("&Space center", self.spaceCenter, "Ctrl+Y") - fontMenu.addAction("&Glyph view", self.glyphView, "Ctrl+G") helpMenu = QMenu("&Help", self) self.menuBar().addMenu(helpMenu) @@ -227,10 +233,12 @@ class MainWindow(QMainWindow): self.characterWidget.glyphOpened.connect(self._glyphOpened) self.setWindowTitle(os.path.basename(self.font.path.rstrip(os.sep))) # TODO: dump the hardcoded path - self.setWindowIcon(QIcon("C:\\Users\\Adrien\\Downloads\\defconQt\\Lib\\defconQt\\resources\\icon.png")); + #self.setWindowIcon(QIcon("C:\\Users\\Adrien\\Downloads\\defconQt\\Lib\\defconQt\\resources\\icon.png")) def newFile(self): + # TODO: ask for save before leaving self.font = Font() + self.setWindowTitle("Untitled.ufo") self.characterWidget.updateFont(self.font) def openFile(self, path=None): @@ -249,10 +257,10 @@ class MainWindow(QMainWindow): # self.font.path = path # done by defcon def saveFileAs(self): - path, _ = QFileDialog.getSaveFileName(self, "Save File", '', + path, ok = QFileDialog.getSaveFileName(self, "Save File", '', "UFO Fonts (*.ufo)") - print(path) - self.saveFile(path) + if ok: + self.saveFile(path) def saveAndExit(self): # TODO: check if font changed @@ -303,23 +311,20 @@ class MainWindow(QMainWindow): def spaceCenter(self): # TODO: see up here + # TODO: show selection in a space center, rewind selection if we raise window (rf) from spaceCenter import MainSpaceWindow if not (hasattr(self, 'spaceCenterWindow') and self.spaceCenterWindow.isVisible()): - # XXX: window collapses when passing self as parent... - self.spaceCenterWindow = MainSpaceWindow(self.font, "Hiyazee otaHawa.") + self.spaceCenterWindow = MainSpaceWindow(self.font, "Hiyazee otaHawa.", parent=self) self.spaceCenterWindow.show() else: self.spaceCenterWindow.raise_() - - def glyphView(self): - # TODO: see up here - from svgViewer import MainGfxWindow - if not (hasattr(self, 'glyphViewWindow') and self.glyphViewWindow.isVisible()): - # XXX: window collapses when passing self as parent... - self.glyphViewWindow = MainGfxWindow(self.font, self.font["a"], self) - self.glyphViewWindow.show() - else: - self.glyphViewWindow.raise_() + + def addGlyph(self): + gName, ok = QInputDialog.getText(self, "Add glyph", "Name of the glyph:") + # Not overwriting existing glyphs. Should it warn in this case? (rf) + if ok and gName != '': + self.font.newGlyph(gName) + self.characterWidget.updateGlyphs() def about(self): QMessageBox.about(self, "About Me", @@ -337,6 +342,8 @@ if __name__ == '__main__': representationFactories.registerAllFactories() #with PyCallGraph(output=GraphvizOutput()): app = QApplication(sys.argv) + # TODO: http://stackoverflow.com/a/21330349/2037879 + app.setWindowIcon(QIcon("C:\\Users\\Adrien\\Downloads\\defconQt\\Lib\\defconQt\\resources\\icon.png")) window = MainWindow(Font("C:\\CharterNova-Regular.ufo")) window.resize(565, 430) window.show() diff --git a/Lib/defconQt/fontinfo.py b/Lib/defconQt/fontinfo.py index ce27b33..1ce956a 100644 --- a/Lib/defconQt/fontinfo.py +++ b/Lib/defconQt/fontinfo.py @@ -84,11 +84,11 @@ class GeneralTab(QWidget): # TODO: give visual feedback of input data validity using QLineEdit lose focus event # http://snorf.net/blog/2014/08/09/using-qvalidator-in-pyqt4-to-validate-user-input/ versionLabel = QLabel("Version:") - self.versionMajorEdit = QLineEdit(str(font.info.versionMajor)) + self.versionMajorEdit = QLineEdit(str(font.info.versionMajor or '')) self.versionMajorEdit.setAlignment(Qt.AlignRight) self.versionMajorEdit.setValidator(QIntValidator()) versionDotLabel = QLabel(".") - self.versionMinorEdit = QLineEdit(str(font.info.versionMinor).zfill(3)) + self.versionMinorEdit = QLineEdit(str(font.info.versionMinor or ''))#.zfill(3)) self.versionMinorEdit.setValidator(QIntValidator()) mainLayout.addWidget(versionLabel, 6, 0) @@ -97,7 +97,7 @@ class GeneralTab(QWidget): mainLayout.addWidget(self.versionMinorEdit, 6, 3) unitsPerEmLabel = QLabel("Units per em:") - self.unitsPerEmEdit = QLineEdit(str(font.info.unitsPerEm)) + self.unitsPerEmEdit = QLineEdit(str(font.info.unitsPerEm or '')) self.unitsPerEmEdit.setValidator(QIntValidator()) mainLayout.addWidget(unitsPerEmLabel, 6, 4) @@ -137,15 +137,6 @@ class GeneralTab(QWidget): font.info.openTypeNameLicense = self.licenseEdit.text() font.info.openTypeNameLicenseURL = self.licenseURLEdit.text() font.info.trademark = self.trademarkEdit.text() - """ - font.info.styleMapFamilyName = self.styleMapFamilyEdit.text() - sn = self.styleMapStyleDrop.currentIndex() - if sn == 1: font.info.styleMapStyleName = "regular" - elif sn == 2: font.info.styleMapStyleName = "italic" - elif sn == 3: font.info.styleMapStyleName = "bold" - elif sn == 4: font.info.styleMapStyleName = "bold italic" - else: font.info.styleMapStyleName = None - """ class NextTab(QWidget): def __init__(self, font, parent=None): @@ -183,15 +174,15 @@ class NextTab(QWidget): mainLayout.addWidget(self.styleMapStyleDrop, 0, 5) ascenderLabel = QLabel("Ascender:") - self.ascenderEdit = QLineEdit(str(font.info.ascender)) + self.ascenderEdit = QLineEdit(str(font.info.ascender or '')) self.ascenderEdit.setValidator(QIntValidator()) descenderLabel = QLabel("Descender:") - self.descenderEdit = QLineEdit(str(font.info.descender)) + self.descenderEdit = QLineEdit(str(font.info.descender or '')) self.descenderEdit.setValidator(QIntValidator()) italicAngleLabel = QLabel("Italic angle:") - self.italicAngleEdit = QLineEdit(str(font.info.italicAngle)) + self.italicAngleEdit = QLineEdit(str(font.info.italicAngle or '')) self.italicAngleEdit.setValidator(QDoubleValidator()) mainLayout.addWidget(ascenderLabel, 1, 0) @@ -202,11 +193,11 @@ class NextTab(QWidget): mainLayout.addWidget(self.italicAngleEdit, 1, 5) xHeightLabel = QLabel("x-height:") - self.xHeightEdit = QLineEdit(str(font.info.xHeight)) + self.xHeightEdit = QLineEdit(str(font.info.xHeight or '')) self.xHeightEdit.setValidator(QIntValidator()) capHeightLabel = QLabel("Cap height:") - self.capHeightEdit = QLineEdit(str(font.info.capHeight)) + self.capHeightEdit = QLineEdit(str(font.info.capHeight or '')) self.capHeightEdit.setValidator(QIntValidator()) mainLayout.addWidget(xHeightLabel, 2, 0) @@ -214,4 +205,13 @@ class NextTab(QWidget): mainLayout.addWidget(capHeightLabel, 2, 2) mainLayout.addWidget(self.capHeightEdit, 2, 3) - self.setLayout(mainLayout)
\ No newline at end of file + self.setLayout(mainLayout) + + def writeValues(self, font): + font.info.styleMapFamilyName = self.styleMapFamilyEdit.text() + sn = self.styleMapStyleDrop.currentIndex() + if sn == 1: font.info.styleMapStyleName = "regular" + elif sn == 2: font.info.styleMapStyleName = "italic" + elif sn == 3: font.info.styleMapStyleName = "bold" + elif sn == 4: font.info.styleMapStyleName = "bold italic" + else: font.info.styleMapStyleName = None
\ No newline at end of file diff --git a/Lib/defconQt/representationFactories/__init__.py b/Lib/defconQt/representationFactories/__init__.py index e2135e4..c2ea89e 100644 --- a/Lib/defconQt/representationFactories/__init__.py +++ b/Lib/defconQt/representationFactories/__init__.py @@ -83,10 +83,17 @@ class OutlineInformationPen(AbstractPointPen): self._rawPointData = [] self._rawComponentData = [] self._bezierHandleData = [] + self.index = 0 def getData(self): - data = dict(startPoints=[], onCurvePoints=[], offCurvePoints=[], bezierHandles=[], anchors=[], components=self._rawComponentData) + data = dict(startPoints=[], onCurvePoints=[], offCurvePoints=[], bezierHandles=[], anchors=[], lastSubpathPoints=[], components=self._rawComponentData) + for contour in self._rawPointData: + if type(contour) is str: + print("Hill") + data["lastSubpathPoints"].append(self.index) + self.index += 1 + continue # anchor if len(contour) == 1 and contour[0]["name"] is not None: anchor = contour[0] @@ -96,8 +103,11 @@ class OutlineInformationPen(AbstractPointPen): haveFirst = False for pointIndex, point in enumerate(contour): if point["segmentType"] is None: - data["offCurvePoints"].append(point) + print("OffCurve") + data["offCurvePoints"].append((point, self.index, not haveFirst)) + self.index += 1 # look for handles + # TODO: calculate this when drawing back = contour[pointIndex - 1] forward = contour[(pointIndex + 1) % len(contour)] if back["segmentType"] in ("curve", "line"): @@ -111,7 +121,9 @@ class OutlineInformationPen(AbstractPointPen): if p1 != p2: data["bezierHandles"].append((p1, p2)) else: - data["onCurvePoints"].append(point) + data["onCurvePoints"].append((point, self.index, not haveFirst)) + print("OnCurve") + self.index += 1 # catch first point if not haveFirst: haveFirst = True @@ -137,7 +149,8 @@ class OutlineInformationPen(AbstractPointPen): self._rawPointData.append([]) def endPath(self): - pass + # TODO: appending a string may not be the most elegant thing to do + self._rawPointData.append("Subpath") def addPoint(self, pt, segmentType=None, smooth=False, name=None, **kwargs): d = dict(point=pt, segmentType=segmentType, smooth=smooth, name=name) diff --git a/Lib/defconQt/spacecenter.py b/Lib/defconQt/spacecenter.py index d518ace..087d319 100644 --- a/Lib/defconQt/spacecenter.py +++ b/Lib/defconQt/spacecenter.py @@ -8,13 +8,14 @@ defaultPointSize = 150 class MainSpaceWindow(QWidget): def __init__(self, font, string="Hello World", pointSize=defaultPointSize, parent=None): - super(MainSpaceWindow, self).__init__(parent) + super(MainSpaceWindow, self).__init__(parent, Qt.Window) self.font = font - self.string = string - self.toolbar = FontToolBar(self.font, pointSize, self.string, self) - self.canvas = GlyphsCanvas(self.font, self.string, pointSize, self) - self.table = SpaceTable(self.font, self.string, self) + self.glyphs = [] + self._subscribeToGlyphsText(string) + self.toolbar = FontToolBar(string, pointSize, self) + self.canvas = GlyphsCanvas(self.font, self.glyphs, pointSize, self) + self.table = SpaceTable(self.glyphs, self) layout = QVBoxLayout(self) layout.addWidget(self.toolbar) @@ -25,9 +26,9 @@ class MainSpaceWindow(QWidget): self.setLayout(layout) self.resize(600,500) self.toolbar.comboBox.currentIndexChanged[str].connect(self.canvas._pointSizeChanged) - self.toolbar.textField.textEdited.connect(self.canvas._textChanged) - self.toolbar.textField.textEdited.connect(self.table._textChanged) - self.table.cellChanged.connect(self.canvas._metricsChanged) + self.toolbar.textField.textEdited.connect(self._textChanged) + + self.font.info.addObserver(self, "_fontInfoChanged", "Info.Changed") self.setWindowTitle("%s%s%s%s" % ("Space center – ", self.font.info.familyName, " ", self.font.info.styleName)) @@ -36,18 +37,71 @@ class MainSpaceWindow(QWidget): self.menuBar().addMenu(fileMenu) fileMenu.addAction("&Save...", self.save, "Ctrl+S") - fileMenu.addAction("E&xit", QApplication.instance().quit, "Ctrl+Q") + fileMenu.addAction("E&xit", self.close, "Ctrl+Q") + + def close(self): + self.font.info.removeObserver(self, "Info.Changed") + self._unsubscribeFromGlyphs() + super(MainSpaceWindow, self).close() + + def _fontInfoChanged(self): + self.canvas.update() + + def _glyphChanged(self, event): + self.canvas.update() + self.table.blockSignals(True) + self.table.fillGlyphs() + self.table.blockSignals(False) + + def _textChanged(self, newText): + self.setGlyphs(newText) + self.canvas._glyphsChanged(self.glyphs) + self.table.blockSignals(True) + self.table._glyphsChanged(self.glyphs) + self.table.blockSignals(False) + + def _subscribeToGlyphsText(self, newText): + glyphs = [] + # TODO: lexer + for c in newText: + name = self.font.unicodeData.glyphNameForUnicode(ord(c)) + if name not in self.font: continue + glyphs.append(self.font[name]) + self.glyphs = glyphs + + handledGlyphs = set() + for glyph in self.glyphs: + if glyph in handledGlyphs: + continue + handledGlyphs.add(glyph) + glyph.addObserver(self, "_glyphChanged", "Glyph.Changed") + + def _unsubscribeFromGlyphs(self): + handledGlyphs = set() + for glyph in self.glyphs: + if glyph in handledGlyphs: + continue + handledGlyphs.add(glyph) + glyph.removeObserver(self, "Glyph.Changed") + #self.glyphs = None + + def setGlyphs(self, string): + # unsubscribe from the old glyphs + self._unsubscribeFromGlyphs() + # subscribe to the new glyphs + self._subscribeToGlyphsText(string) + # set the records into the view + self.canvas._glyphsChanged(self.glyphs) + self.table._glyphsChanged(self.glyphs) pointSizes = [50, 75, 100, 125, 150, 200, 250, 300, 350, 400, 450, 500] class FontToolBar(QToolBar): - def __init__(self, font, pointSize, string, parent=None): + def __init__(self, string, pointSize, parent=None): super(FontToolBar, self).__init__(parent) self.textField = QLineEdit(string) self.comboBox = QComboBox() self.comboBox.setEditable(True) - # Should I allow this? What does robofont do here? - #self.comboBox.setInsertPolicy(QComboBox.NoInsert) for p in pointSizes: self.comboBox.addItem(str(p)) self.comboBox.lineEdit().setText(str(pointSize)) @@ -56,21 +110,20 @@ class FontToolBar(QToolBar): self.addWidget(self.comboBox) class GlyphsCanvas(QWidget): - def __init__(self, font, string, pointSize=defaultPointSize, parent=None): + def __init__(self, font, glyphs, pointSize=defaultPointSize, parent=None): super(GlyphsCanvas, self).__init__(parent) - self.font = font - self.string = string - + self.descender = font.info.descender + if self.descender is None: self.descender = 250 + self.upm = font.info.unitsPerEm + if self.upm is None or not self.upm > 0: self.upm = 1000 + self.glyphs = glyphs self.ptSize = pointSize self.calculateScale() self.padding = 10 def calculateScale(self): - if self.font.info.unitsPerEm is None: return - upm = self.font.info.unitsPerEm - if not upm > 0: upm = 1000 - scale = self.ptSize / float(self.font.info.unitsPerEm) + scale = self.ptSize / float(self.upm) if scale < .01: scale = 0.01 self.scale = scale @@ -79,22 +132,8 @@ class GlyphsCanvas(QWidget): self.calculateScale() self.update() - def _textChanged(self, newText): - self.string = newText - self.update() - - def _metricsChanged(self, row, col): - item = int(self.parent().table.item(row, col).text()) - # TODO: update width on the QTableWidget when sidebearing changes. - # stop passing signals and use defcon notificationHandler instead - # -1 because the first col contains descriptive text - glyph = self.font.unicodeData.glyphNameForUnicode(ord(self.string[col-1])) - if row == 1: - self.font[glyph].width = item - elif row == 2: - self.font[glyph].leftMargin = item - elif row == 3: - self.font[glyph].rightMargin = item + def _glyphsChanged(self, newGlyphs): + self.glyphs = newGlyphs self.update() # if we have a cell clicked in and we click on the canvas, @@ -103,30 +142,40 @@ class GlyphsCanvas(QWidget): # QTableWidget.scrollToItem() def mousePressEvent(self, event): self.setFocus(Qt.MouseFocusReason) + + def wheelEvent(self, event): + # TODO: should it snap to predefined pointSizes? is the scaling factor okay? + # see how rf behaves + decay = event.angleDelta().y() / 120.0 + newPointSize = self.ptSize + int(decay) * 10 + self._pointSizeChanged(newPointSize) + + comboBox = self.parent().toolbar.comboBox + comboBox.blockSignals(True) + comboBox.setEditText(str(newPointSize)) + comboBox.blockSignals(False) def paintEvent(self, event): painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing) painter.fillRect(0, 0, self.width(), self.height(), Qt.white) # TODO: should padding be added for the right boundary as well? I'd say no but not sure - painter.translate(self.padding, self.padding+self.ptSize+self.font.info.descender*self.scale) + painter.translate(self.padding, self.padding+self.ptSize+self.descender*self.scale) cur_width = 0 - for c in self.string: - glyph = self.font.unicodeData.glyphNameForUnicode(ord(c)) - if glyph not in self.font: continue + for glyph in self.glyphs: # line wrapping - if cur_width + self.font[glyph].width*self.scale + self.padding > self.width(): + if cur_width + glyph.width*self.scale + self.padding > self.width(): painter.translate(-cur_width, self.ptSize) - cur_width = self.font[glyph].width*self.scale + cur_width = glyph.width*self.scale else: - cur_width += self.font[glyph].width*self.scale - glyphPath = self.font[glyph].getRepresentation("defconQt.QPainterPath") + cur_width += glyph.width*self.scale + glyphPath = glyph.getRepresentation("defconQt.QPainterPath") painter.save() painter.scale(self.scale, -self.scale) painter.fillPath(glyphPath, Qt.black) painter.restore() - painter.translate(self.font[glyph].width*self.scale, 0) + painter.translate(glyph.width*self.scale, 0) ''' painter.setPen( @@ -140,10 +189,13 @@ class GlyphsCanvas(QWidget): ''' class SpaceTable(QTableWidget): - def __init__(self, font, string="", parent=None): - self.font = font - self.string = string - super(SpaceTable, self).__init__(4, len(self.string), parent) + def __init__(self, glyphs, parent=None): + self.glyphs = glyphs + super(SpaceTable, self).__init__(4, len(glyphs)+1, parent) + # XXX: dunno why but without updating col count + # scrollbar reports incorrect height... + # fillGlyphs() will change this value back + self.setColumnCount(len(self.glyphs)+2) data = [None, "Width", "Left", "Right"] for index, item in enumerate(data): cell = QTableWidgetItem(item) @@ -158,18 +210,36 @@ class SpaceTable(QTableWidget): # always show a scrollbar to fix layout self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOn) - self.resizeRowsToContents() self.setSizePolicy(QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed)) self.fillGlyphs() + self.resizeRowsToContents() + self.cellChanged.connect(self._cellEdited) # edit cell on single click, not double self.setEditTriggers(QAbstractItemView.CurrentChanged) - # TODO: insvestigate changing cell color as in robofont + # TODO: investigate changing cell color as in robofont # http://stackoverflow.com/a/13926342/2037879 - def _textChanged(self, newText): - self.string = newText + def _glyphsChanged(self, newGlyphs): + self.glyphs = newGlyphs # TODO: we don't need to reallocate cells, split alloc and fill self.fillGlyphs() + + def _cellEdited(self, row, col): + if row == 0 or col == 0: return + item = self.item(row, col).text() + # Glyphs that do not have outlines leave empty cells, can't call + # int() on that + if not item: return + item = int(item) + # -1 because the first col contains descriptive text + glyph = self.glyphs[col-1] + if row == 1: + glyph.width = item + elif row == 2: + glyph.leftMargin = item + elif row == 3: + glyph.rightMargin = item + # defcon callbacks do the update def sizeHint(self): # http://stackoverflow.com/a/7216486/2037879 @@ -191,20 +261,14 @@ class SpaceTable(QTableWidget): #item.setTextAlignment(Qt.AlignCenter) return item - self.setColumnCount(len(self.string)+1) - dropped = 0 - for index, char in enumerate(self.string): - glyph = self.font.unicodeData.glyphNameForUnicode(ord(char)) - i = index-dropped+1 - if glyph not in self.font: dropped += 1; continue + self.setColumnCount(len(self.glyphs)+1) + for index, glyph in enumerate(self.glyphs): # TODO: should glyph name edit really be permitted here? - # TODO: also find glyphs by /name or should be abstracted by input area or main object? - self.setItem(0, i, glyphTableWidgetItem(self.font[glyph].name, True)) - self.setItem(1, i, glyphTableWidgetItem(self.font[glyph].width)) - self.setItem(2, i, glyphTableWidgetItem(self.font[glyph].leftMargin)) - self.setItem(3, i, glyphTableWidgetItem(self.font[glyph].rightMargin)) - self.setColumnWidth(i, self._cellWidth) - self.setColumnCount(len(self.string)+1-dropped) + self.setItem(0, index+1, glyphTableWidgetItem(glyph.name, True)) + self.setItem(1, index+1, glyphTableWidgetItem(glyph.width)) + self.setItem(2, index+1, glyphTableWidgetItem(glyph.leftMargin)) + self.setItem(3, index+1, glyphTableWidgetItem(glyph.rightMargin)) + self.setColumnWidth(index+1, self._cellWidth) def wheelEvent(self, event): cur = self.horizontalScrollBar().value() @@ -212,7 +276,6 @@ class SpaceTable(QTableWidget): event.accept() if __name__ == '__main__': - import sys # registerallfactories app = QApplication(sys.argv) diff --git a/Lib/defconQt/svgViewer.py b/Lib/defconQt/svgViewer.py index 5e7b80a..c4d7d7f 100644 --- a/Lib/defconQt/svgViewer.py +++ b/Lib/defconQt/svgViewer.py @@ -2,7 +2,7 @@ from PyQt5.QtCore import QFile, QRectF, QSize, Qt from PyQt5.QtGui import QBrush, QColor, QImage, QPainter, QPainterPath, QPixmap, QPen from PyQt5.QtWidgets import (QActionGroup, QApplication, QFileDialog, QGraphicsItem, QGraphicsRectItem, QGraphicsScene, QGraphicsView, - QMainWindow, QMenu, QMessageBox, QWidget) + QMainWindow, QMenu, QMessageBox, QStyle, QStyleOptionGraphicsItem, QWidget) from PyQt5.QtOpenGL import QGL, QGLFormat, QGLWidget from PyQt5.QtSvg import QGraphicsSvgItem @@ -71,6 +71,74 @@ class MainGfxWindow(QMainWindow): elif action == self.imageAction: self.view.setRenderer(SvgView.Image) +# TODO: make QAbstractShapeItem as derive ellipse and rect, or just do path +class OnCurvePointItem(QGraphicsRectItem): + def __init__(self, x, y, width, height, pointX, pointY, pointIndex, otherPointIndex=None, + startPointObject=None, pen=None, brush=None, parent=None): + super(OnCurvePointItem, self).__init__(x, y, width, height, parent) + self.setFlag(QGraphicsItem.ItemIsMovable) + self.setFlag(QGraphicsItem.ItemIsSelectable) + # TODO: stop doing this and go back to mouse events + self.setFlag(QGraphicsItem.ItemSendsGeometryChanges) + if pen is not None: self._pen = pen; self.setPen(pen) + if brush is not None: self.setBrush(brush) + + self._pointX = pointX + self._pointY = pointY + self._pointIndex = pointIndex + # For the start point we must handle two instrs: the moveTo which is the initial start + # point of the path and the last lineTo/curveTo which is the closing segment + self._otherPointIndex = otherPointIndex + self._startPointObject = startPointObject + + """ + def mouseMoveEvent(self, event): + pos = event.pos() + print(pos) + super(OnCurvePointItem, self).mouseMoveEvent(event) + path = self.scene()._outlineItem.path() + path.setElementPositionAt(0, pos.x(), pos.y()) + self.scene()._outlineItem.setPath(path) + #self.scene().update() + """ + + def itemChange(self, change, value): + if change == QGraphicsItem.ItemPositionHasChanged: + path = self.scene()._outlineItem.path() + """ + for i in range(path.elementCount()): + elem = path.elementAt(i) + if elem.isCurveTo(): kind = "curve" + elif elem.isLineTo(): kind = "line" + else: kind = "move" + print("{}: {} {}".format(kind, elem.x, elem.y)) + print() + """ + # TODO: if we're snapped to int round self.pos to int + newX = self._pointX+self.pos().x() + newY = self._pointY+self.pos().y() + path.setElementPositionAt(self._pointIndex, newX, newY) + if self._otherPointIndex is not None: + path.setElementPositionAt(self._otherPointIndex, newX, newY) + # TODO: the angle ought to be recalculated + # maybe make it disappear on move and recalc when releasing + # what does rf do here? + self._startPointObject.setPos(self.pos()) + self.scene()._outlineItem.setPath(path) + return QGraphicsItem.itemChange(self, change, value) + + # http://www.qtfr.org/viewtopic.php?pid=21045#p21045 + def paint(self, painter, option, widget): + newOption = QStyleOptionGraphicsItem(option) + newOption.state = QStyle.State_None + super(OnCurvePointItem, self).paint(painter, newOption, widget) + if (option.state & QStyle.State_Selected): + pen = self.pen() + pen.setColor(Qt.red) + #pen.setWidth + self.setPen(pen) + else: + self.setPen(self._pen) class SvgView(QGraphicsView): Native, OpenGL, Image = range(3) @@ -102,11 +170,9 @@ class SvgView(QGraphicsView): self.setScene(QGraphicsScene(self)) #self.scene().setSceneRect(0, self._font.info.ascender, self._glyph.width, self._font.info.unitsPerEm) - # XXX: this should allow us to move the view but doesn't happen... self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse) self.setResizeAnchor(QGraphicsView.AnchorUnderMouse) - # TODO: only set this for moving tool, also set bounding box... - self.setDragMode(QGraphicsView.ScrollHandDrag) + self.setDragMode(QGraphicsView.RubberBandDrag) # This rewinds view when scrolling, needed for check-board background #self.setViewportUpdateMode(QGraphicsView.FullViewportUpdate) @@ -114,11 +180,12 @@ class SvgView(QGraphicsView): self.translate(0, self.height()*(1+self._font.info.descender/self._font.info.unitsPerEm)) self.scale(1, -1) self.addBackground() - self.addBlues() + #self.addBlues() self.addOutlines() self.addPoints() # Prepare background check-board pattern. + """ tilePixmap = QPixmap(64, 64) tilePixmap.fill(Qt.white) tilePainter = QPainter(tilePixmap) @@ -126,6 +193,7 @@ class SvgView(QGraphicsView): tilePainter.fillRect(0, 0, 32, 32, color) tilePainter.fillRect(32, 32, 32, 32, color) tilePainter.end() + """ #self.setBackgroundBrush(QBrush(tilePixmap)) @@ -143,10 +211,10 @@ class SvgView(QGraphicsView): return width, height def _calcScale(self): - if self.parent() is None: - return + #if self.viewport() is None: + # return if self._pointSize is None: - visibleHeight = self.parent().height() + visibleHeight = self.viewport().height() fitHeight = visibleHeight glyphWidth, glyphHeight = self._getGlyphWidthHeight() glyphHeight += self._noPointSizePadding * 2 @@ -348,7 +416,7 @@ class SvgView(QGraphicsView): s = self.scene() # outlines path = self._glyph.getRepresentation("defconQt.NoComponentsQPainterPath") - s.addPath(path, brush=QBrush(self._fillColor)) + self.scene()._outlineItem = s.addPath(path, brush=QBrush(self._fillColor)) # components path = self._glyph.getRepresentation("defconQt.OnlyComponentsQPainterPath") s.addPath(path, brush=QBrush(self._componentFillColor)) @@ -381,65 +449,94 @@ class SvgView(QGraphicsView): coordinateSize = 0 # use the data from the outline representation outlineData = self._glyph.getRepresentation("defconQt.OutlineInformation") - points = [] + points = [] # TODO: remove this unless we need it # useful for text drawing, add it + startObjects = [] # start point if self._showOnCurvePoints and outlineData["startPoints"]: startWidth = startHeight = self.roundPosition(startPointSize)# * self._inverseScale) startHalf = startWidth / 2.0 - path = QPainterPath() for point, angle in outlineData["startPoints"]: x, y = point + # TODO: do we really need to special-case with Qt? if angle is not None: + path = QPainterPath() path.moveTo(x, y) path.arcTo(x-startHalf, y-startHalf, 2*startHalf, 2*startHalf, angle-90, -180) - path.closeSubpath() + item = s.addPath(path, QPen(Qt.NoPen), QBrush(self._startPointColor)) + startObjects.append(item) + #path.closeSubpath() else: - path.addEllipse(x-startHalf, y-startHalf, startWidth, startHeight) - s.addPath(path, QPen(Qt.NoPen), brush=QBrush(self._startPointColor)) + item = s.addEllipse(x-startHalf, y-startHalf, startWidth, startHeight, + QPen(Qt.NoPen), QBrush(self._startPointColor)) + startObjects.append(item) + #s.addPath(path, QPen(Qt.NoPen), brush=QBrush(self._startPointColor)) # off curve if self._showOffCurvePoints and outlineData["offCurvePoints"]: # lines - path = QPainterPath() for point1, point2 in outlineData["bezierHandles"]: + path = QPainterPath() path.moveTo(*point1) path.lineTo(*point2) - s.addPath(path, QPen(self._bezierHandleColor, 1.0)) + s.addPath(path, QPen(self._bezierHandleColor, 1.0)) # points offWidth = offHeight = self.roundPosition(offCurvePointSize)# * self._inverseScale) offHalf = offWidth / 2.0 - path = QPainterPath() - for point in outlineData["offCurvePoints"]: + #path = QPainterPath() + for point, index, isFirst in outlineData["offCurvePoints"]: x, y = point["point"] points.append((x, y)) x = self.roundPosition(x - offHalf) y = self.roundPosition(y - offHalf) - path.addEllipse(x, y, offWidth, offHeight) - if self._drawStroke: - s.addPath(path, QPen(self._pointStrokeColor, 3.0)) - s.addPath(path, QPen(Qt.NoPen), brush=QBrush(self._backgroundColor)) - s.addPath(path, QPen(self._offCurvePointColor, 1.0)) + item = s.addEllipse(x, y, offWidth, offHeight, + QPen(self._offCurvePointColor, 1.0), QBrush(self._backgroundColor)) + item.setFlag(QGraphicsItem.ItemIsMovable) + #if self._drawStroke: + # s.addPath(path, QPen(self._pointStrokeColor, 3.0)) + #s.addPath(path, QPen(Qt.NoPen), brush=) + #s.addPath(path, ) # on curve if self._showOnCurvePoints and outlineData["onCurvePoints"]: width = height = self.roundPosition(onCurvePointSize)# * self._inverseScale) half = width / 2.0 smoothWidth = smoothHeight = self.roundPosition(onCurveSmoothPointSize)# * self._inverseScale) smoothHalf = smoothWidth / 2.0 - path = QPainterPath() - for point in outlineData["onCurvePoints"]: + #path = QPainterPath() + for point, index, isFirst in outlineData["onCurvePoints"]: x, y = point["point"] points.append((x, y)) if point["smooth"]: x = self.roundPosition(x - smoothHalf) y = self.roundPosition(y - smoothHalf) - path.addEllipse(x, y, smoothWidth, smoothHeight) + item = s.addEllipse(x, y, smoothWidth, smoothHeight, + QPen(self._pointStrokeColor, 1.5), QBrush(self._onCurvePointColor)) + item.setFlag(QGraphicsItem.ItemIsMovable) + item.setFlag(QGraphicsItem.ItemIsSelectable) else: - x = self.roundPosition(x - half) - y = self.roundPosition(y - half) - path.addRect(x, y, width, height) - if self._drawStroke: - s.addPath(path, QPen(self._pointStrokeColor, 3.0)) - s.addPath(path, QPen(Qt.NoPen), brush=QBrush(self._onCurvePointColor)) - + rx = self.roundPosition(x - half) + ry = self.roundPosition(y - half) + + lastPointInSubpath = None + startObject = None + if isFirst: + for lastPointIndex in outlineData["lastSubpathPoints"]: + if lastPointIndex > index: + lastPointInSubpath = lastPointIndex + break + startObject = startObjects.pop(0) + + item = OnCurvePointItem(rx, ry, width, height, x, y, index, lastPointInSubpath, + startObject, QPen(self._pointStrokeColor, 1.5), QBrush(self._onCurvePointColor)) + s.addItem(item) + #item = s.addRect(x, y, width, height, + # QPen(self._pointStrokeColor, 1.5), QBrush(self._onCurvePointColor)) + #item.setFlag(QGraphicsItem.ItemIsMovable) + #item.setFlag(QGraphicsItem.ItemIsSelectable) + #if self._drawStroke: + # s.addPath(path, ) + #myRect = s.addPath(path, QPen(Qt.NoPen), brush=) + #myRect.setFlag(QGraphicsItem.ItemIsSelectable) + #myRect.setFlag(QGraphicsItem.ItemIsMovable + def roundPosition(self, value): value = value * self._scale value = round(value) - .5 @@ -468,17 +565,37 @@ class SvgView(QGraphicsView): def setViewOutline(self, enable): if self.outlineItem: self.outlineItem.setVisible(enable) + + def mousePressEvent(self, event): + if (event.button() == Qt.MidButton): + if self.dragMode() == QGraphicsView.RubberBandDrag: + self.setDragMode(QGraphicsView.ScrollHandDrag) + else: + self.setDragMode(QGraphicsView.RubberBandDrag) + super(SvgView, self).mousePressEvent(event) + + # Lock/release handdrag does not seem to work… + ''' + def mouseReleaseEvent(self, event): + if (event.button() == Qt.MidButton): + self.setDragMode(QGraphicsView.RubberBandDrag) + super(SvgView, self).mouseReleaseEvent(event) + ''' def paintEvent(self, event): #self.scene().clear() - super(SvgView, self).paintEvent(event) + ''' painter = QPainter(self.viewport()) - self.drawBackground(painter) + painter.setRenderHint(QPainter.Antialiasing) + painter.translate(0, self.height()*(1+self._font.info.descender/self._font.info.unitsPerEm)) + painter.scale(1, -1) self.drawBlues(painter) + self.drawBackground(painter) self.drawOutlines(painter) self.drawPoints(painter) ''' + super(SvgView, self).paintEvent(event) ''' if self.renderer == SvgView.Image: @@ -505,6 +622,7 @@ class SvgView(QGraphicsView): #self._calcScale() #self._setFrame() self.scale(factor, factor) + self._calcScale() self.update() #newPos = self.mapToScene(event.pos()) #delta = newPos - oldPos diff --git a/Lib/defconQt/syntaxhighlighter.py b/Lib/defconQt/syntaxhighlighter.py index 98d42e3..26972e4 100644 --- a/Lib/defconQt/syntaxhighlighter.py +++ b/Lib/defconQt/syntaxhighlighter.py @@ -56,6 +56,7 @@ class TextEditor(QPlainTextEdit): self.setFont(font) self.highlighter = Highlighter(self.document()) + # TODO: get rid of jitter on opening self.lineNumbers = LineNumberArea(self) self.blockCountChanged.connect(self.updateLineNumberAreaWidth) self.updateRequest.connect(self.updateLineNumberArea) |
