Skip to content

Instantly share code, notes, and snippets.

@alexlib
Last active April 24, 2022 06:09
Show Gist options
  • Save alexlib/34838c9717e321ab91b36a265f079c62 to your computer and use it in GitHub Desktop.
Save alexlib/34838c9717e321ab91b36a265f079c62 to your computer and use it in GitHub Desktop.
Bug tracking of OpenPTV + PyPTV on Windows 10 with Python 3.10

Recreating the bug reported in the forum

https://groups.google.com/d/msgid/openptv/43837409-aac5-407c-a8bf-37e96a95f599n%40googlegroups.com?utm_medium=email&utm_source=footer

Installation procedure:

conda create -n pyptv_py310win python=3.10 -y
conda activate pyptv_py310win
python -m pip install --upgrade pip
pip install numpy
pip install pyptv --index-url https://pypi.fury.io/pyptv --extra-index-url https://pypi.org/simple

Run tests

     pyptv .\repos\test_cavity\

Fixing the first bug

(pyptv_py310win) PS C:\Users\alex> pyptv .\repos\test_cavity\
Traceback (most recent call last):
  File "C:\Users\alex\miniconda3\envs\pyptv_py310win\lib\runpy.py", line 196, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "C:\Users\alex\miniconda3\envs\pyptv_py310win\lib\runpy.py", line 86, in _run_code
    exec(code, run_globals)
  File "C:\Users\alex\miniconda3\envs\pyptv_py310win\Scripts\pyptv.exe\__main__.py", line 4, in <module>
  File "C:\Users\alex\miniconda3\envs\pyptv_py310win\lib\site-packages\pyptv\pyptv_gui.py", line 47, in <module>
    from pyptv.calibration_gui import CalibrationGUI
  File "C:\Users\alex\miniconda3\envs\pyptv_py310win\lib\site-packages\pyptv\calibration_gui.py", line 14, in <module>
    from chaco.api import Plot, ArrayPlotData, gray, \
  File "C:\Users\alex\miniconda3\envs\pyptv_py310win\lib\site-packages\chaco\api.py", line 343, in <module>
    from chaco.overlays.api import (
  File "C:\Users\alex\miniconda3\envs\pyptv_py310win\lib\site-packages\chaco\overlays\api.py", line 63, in <module>
    from chaco.overlays.layers.api import (
  File "C:\Users\alex\miniconda3\envs\pyptv_py310win\lib\site-packages\chaco\overlays\layers\api.py", line 11, in <module>
    from .status_layer import ErrorLayer, StatusLayer, WarningLayer
  File "C:\Users\alex\miniconda3\envs\pyptv_py310win\lib\site-packages\chaco\overlays\layers\status_layer.py", line 17, in <module>
    from enable.savage.svg.document import SVGDocument
  File "C:\Users\alex\miniconda3\envs\pyptv_py310win\lib\site-packages\enable\savage\svg\document.py", line 27, in <module>
    from . import css
  File "C:\Users\alex\miniconda3\envs\pyptv_py310win\lib\site-packages\enable\savage\svg\css\__init__.py", line 10, in <module>
    from .transform import transformList
  File "C:\Users\alex\miniconda3\envs\pyptv_py310win\lib\site-packages\enable\savage\svg\css\transform.py", line 18, in <module>
    from enable.savage.svg.pathdata import number, maybeComma
  File "C:\Users\alex\miniconda3\envs\pyptv_py310win\lib\site-packages\enable\savage\svg\pathdata.py", line 117, in <module>
    lineTo = Group(Command("L") + Arguments(coordinatePairSequence))
  File "C:\Users\alex\miniconda3\envs\pyptv_py310win\lib\site-packages\enable\savage\svg\pathdata.py", line 29, in Command
    return CaselessPreservingLiteral(char)
  File "C:\Users\alex\miniconda3\envs\pyptv_py310win\lib\site-packages\enable\savage\svg\pathdata.py", line 43, in __init__
    self.name = "'%s'" % matchString
AttributeError: can't set attribute 'name'

Edit "C:\Users\alex\miniconda3\envs\pyptv_py310win\lib\site-packages\enable\savage\svg\pathdata.py" and replace self.name by self.customName or replace it with the attached file

Fixing the reported bug:

Traceback (most recent call last):
  File "C:\Users\alex\miniconda3\envs\pyptv_py310win\lib\site-packages\pyface\ui\qt4\code_editor\gutters.py", line 134, in paintEvent
    painter.drawText(
TypeError: arguments did not match any overloaded call:
  drawText(self, Union[QPointF, QPoint], str): argument 1 has unexpected type 'int'
  drawText(self, QRectF, int, str): argument 1 has unexpected type 'int'
  drawText(self, QRect, int, str): argument 1 has unexpected type 'int'
  drawText(self, QRectF, str, option: QTextOption = QTextOption()): argument 1 has unexpected type 'int'
  drawText(self, QPoint, str): argument 1 has unexpected type 'int'
  drawText(self, int, int, int, int, int, str): argument 2 has unexpected type 'float'
  drawText(self, int, int, str): argument 2 has unexpected type 'float'

Edit C:\Users\alex\miniconda3\envs\pyptv_py310win\lib\site-packages\pyface\ui\qt4\code_editor\gutters.py and change the lines 135

                painter.drawText(
                    int(0),
                    int(top),
                    int(self.width() - 2),
                    self.fontMetrics().height(),
                    QtCore.Qt.AlignmentFlag.AlignRight,
                    str(blocknum + 1),
                )

or replace gutters.py with the attached file

(base) PS C:\Users\alex> conda activate pyptv_py310win
(pyptv_py310win) PS C:\Users\alex> python -m pip install --upgrade pip
Requirement already satisfied: pip in c:\users\alex\miniconda3\envs\pyptv_py310win\lib\site-packages (22.0.4)
(pyptv_py310win) PS C:\Users\alex> pip install numpy
Collecting numpy
Using cached numpy-1.22.3-cp310-cp310-win_amd64.whl (14.7 MB)
Installing collected packages: numpy
Successfully installed numpy-1.22.3
(pyptv_py310win) PS C:\Users\alex> pip install pyptv --index-url https://pypi.fury.io/pyptv --extra-index-url https://pypi.org/simple
Looking in indexes: https://pypi.fury.io/pyptv, https://pypi.org/simple
Collecting pyptv
Downloading https://pypi.fury.io/pyptv/-/ver_1ErqUI/pyptv-0.1.7-py3-none-any.whl (52 kB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 52.5/52.5 KB 135.0 kB/s eta 0:00:00
Collecting six
Using cached six-1.16.0-py2.py3-none-any.whl (11 kB)
Collecting scikit-image
Using cached scikit_image-0.19.2-cp310-cp310-win_amd64.whl (12.6 MB)
Collecting enable
Downloading https://pypi.fury.io/pyptv/-/ver_QW8R2/enable-5.2.1-cp310-cp310-win_amd64.whl (2.1 MB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 2.1/2.1 MB 1.0 MB/s eta 0:00:00
Collecting optv
Downloading https://pypi.fury.io/pyptv/-/ver_1dZBaK/optv-0.2.6-cp310-cp310-win_amd64.whl (1.1 MB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1.1/1.1 MB 1.2 MB/s eta 0:00:00
Collecting chaco
Downloading https://pypi.fury.io/pyptv/-/ver_1MJ6AH/chaco-5.0.0-cp310-cp310-win_amd64.whl (963 kB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 963.3/963.3 KB 5.1 MB/s eta 0:00:00
Collecting imagecodecs
Using cached imagecodecs-2022.2.22-cp310-cp310-win_amd64.whl (14.4 MB)
Requirement already satisfied: numpy in c:\users\alex\miniconda3\envs\pyptv_py310win\lib\site-packages (from pyptv) (1.22.3)
Collecting PyQt5
Using cached PyQt5-5.15.6-cp36-abi3-win_amd64.whl (6.7 MB)
Collecting Pygments
Using cached Pygments-2.11.2-py3-none-any.whl (1.1 MB)
Collecting traitsui
Using cached traitsui-7.3.1-py3-none-any.whl (1.5 MB)
Collecting traits>=6.2.0
Using cached traits-6.3.2-cp310-cp310-win_amd64.whl (5.0 MB)
Collecting pyface>=7.2.0
Using cached pyface-7.4.1-py3-none-any.whl (1.3 MB)
Collecting fonttools
Using cached fonttools-4.32.0-py3-none-any.whl (900 kB)
Collecting pillow
Using cached Pillow-9.1.0-cp310-cp310-win_amd64.whl (3.3 MB)
Collecting pyyaml>=5.4
Using cached PyYAML-6.0-cp310-cp310-win_amd64.whl (151 kB)
Collecting PyQt5-sip<13,>=12.8
Using cached PyQt5_sip-12.10.1-cp310-cp310-win_amd64.whl (77 kB)
Collecting PyQt5-Qt5>=5.15.2
Using cached PyQt5_Qt5-5.15.2-py3-none-win_amd64.whl (50.1 MB)
Collecting networkx>=2.2
Using cached networkx-2.8-py3-none-any.whl (2.0 MB)
Collecting PyWavelets>=1.1.1
Using cached PyWavelets-1.3.0-cp310-cp310-win_amd64.whl (4.2 MB)
Collecting imageio>=2.4.1
Downloading imageio-2.17.0-py3-none-any.whl (3.4 MB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 3.4/3.4 MB 4.4 MB/s eta 0:00:00
Collecting packaging>=20.0
Using cached packaging-21.3-py3-none-any.whl (40 kB)
Collecting scipy>=1.4.1
Using cached scipy-1.8.0-cp310-cp310-win_amd64.whl (37.0 MB)
Collecting tifffile>=2019.7.26
Using cached tifffile-2022.4.8-py3-none-any.whl (180 kB)
Collecting pyparsing!=3.0.5,>=2.0.2
Using cached pyparsing-3.0.8-py3-none-any.whl (98 kB)
Installing collected packages: PyQt5-Qt5, traits, tifffile, six, scipy, pyyaml, PyWavelets, PyQt5-sip, pyparsing, Pygments, pillow, networkx, imagecodecs, fonttools, PyQt5, pyface, packaging, optv, imageio, traitsui, scikit-image, enable, chaco, pyptv
# (C) Copyright 2005-2021 Enthought, Inc., Austin, TX
# All rights reserved.
#
# This software is provided without warranty under the terms of the BSD
# license included in LICENSE.txt and may be redistributed only under
# the conditions described in the aforementioned license. The license
# is also available online at http://www.enthought.com/licenses/BSD.txt
#
# Thanks for using Enthought open source!
""" SVG path data parser
Usage::
steps = svg.parseString(pathdata)
for command, arguments in steps:
pass
"""
from pyparsing import (
CaselessLiteral, Combine, Group, Literal, OneOrMore, Optional,
ParseException, Word, ZeroOrMore, nums, oneOf
)
# ParserElement.enablePackrat()
def Command(char):
""" Case insensitive but case preserving"""
return CaselessPreservingLiteral(char)
def Arguments(token):
return Group(token)
class CaselessPreservingLiteral(CaselessLiteral):
""" Like CaselessLiteral, but returns the match as found
instead of as defined.
"""
def __init__(self, matchString):
super().__init__(matchString.upper())
self.customName = "'%s'" % matchString
self.errmsg = "Expected " + self.name
def parseImpl(self, instring, loc, doActions=True):
test = instring[loc:loc + self.matchLen]
if test.upper() == self.match:
return loc + self.matchLen, test
# ~ raise ParseException( instring, loc, self.errmsg )
raise ParseException(instring, loc, self.errmsg, self)
def Sequence(token):
""" A sequence of the token"""
return OneOrMore(token + maybeComma)
digit_sequence = Word(nums)
sign = oneOf("+ -")
def convertToFloat(s, loc, toks):
try:
return float(toks[0])
except Exception:
raise ParseException(loc, "invalid float format %s" % toks[0])
exponent = CaselessLiteral("e") + Optional(sign) + Word(nums)
# note that almost all these fields are optional,
# and this can match almost anything. We rely on Pythons built-in
# float() function to clear out invalid values - loosely matching like this
# speeds up parsing quite a lot
floatingPointConstant = Combine(
Optional(sign)
+ Optional(Word(nums))
+ Optional(Literal(".") + Optional(Word(nums)))
+ Optional(exponent)
)
floatingPointConstant.setParseAction(convertToFloat)
number = floatingPointConstant
# same as FP constant but don't allow a - sign
nonnegativeNumber = Combine(
Optional(Word(nums))
+ Optional(Literal(".") + Optional(Word(nums)))
+ Optional(exponent)
)
nonnegativeNumber.setParseAction(convertToFloat)
coordinate = number
# comma or whitespace can seperate values all over the place in SVG
maybeComma = Optional(Literal(",")).suppress()
coordinateSequence = Sequence(coordinate)
coordinatePair = (coordinate + maybeComma + coordinate).setParseAction(
lambda t: tuple(t)
)
coordinatePairSequence = Sequence(coordinatePair)
coordinatePairPair = coordinatePair + maybeComma + coordinatePair
coordinatePairPairSequence = Sequence(Group(coordinatePairPair))
coordinatePairTriple = (
coordinatePair + maybeComma + coordinatePair + maybeComma + coordinatePair
)
coordinatePairTripleSequence = Sequence(Group(coordinatePairTriple))
# commands
lineTo = Group(Command("L") + Arguments(coordinatePairSequence))
moveTo = Group(Command("M") + Arguments(coordinatePairSequence))
closePath = Group(Command("Z")).setParseAction(lambda t: ("Z", (None,)))
flag = oneOf("1 0").setParseAction(lambda t: bool(int((t[0]))))
arcRadius = (
nonnegativeNumber + maybeComma + nonnegativeNumber # rx, ry
).setParseAction(lambda t: tuple(t))
arcFlags = (flag + maybeComma + flag).setParseAction(lambda t: tuple(t))
ellipticalArcArgument = Group(
arcRadius + maybeComma # rx, ry
+ number + maybeComma # rotation
+ arcFlags # large-arc-flag, sweep-flag
+ coordinatePair # (x,y)
)
ellipticalArc = Group(
Command("A") + Arguments(Sequence(ellipticalArcArgument))
)
smoothQuadraticBezierCurveto = Group(
Command("T") + Arguments(coordinatePairSequence)
)
quadraticBezierCurveto = Group(
Command("Q") + Arguments(coordinatePairPairSequence)
)
smoothCurve = Group(Command("S") + Arguments(coordinatePairPairSequence))
curve = Group(Command("C") + Arguments(coordinatePairTripleSequence))
horizontalLine = Group(Command("H") + Arguments(coordinateSequence))
verticalLine = Group(Command("V") + Arguments(coordinateSequence))
drawToCommand = (
lineTo
| moveTo
| closePath
| ellipticalArc
| smoothQuadraticBezierCurveto
| quadraticBezierCurveto
| smoothCurve
| curve
| horizontalLine
| verticalLine
)
# ~ number.debug = True
moveToDrawToCommands = moveTo + ZeroOrMore(drawToCommand)
svg = ZeroOrMore(moveToDrawToCommands)
svg.keepTabs = True
def profile():
import cProfile
p = cProfile.Profile()
p.enable()
ptest()
ptest()
ptest()
p.disable()
p.print_stats()
bpath = """M204.33 139.83 C196.33 133.33 206.68 132.82 206.58 132.58 C192.33 97.08 169.35
81.41 167.58 80.58 C162.12 78.02 159.48 78.26 160.45 76.97 C161.41 75.68 167.72 79.72 168.58
80.33 C193.83 98.33 207.58 132.33 207.58 132.33 C207.58 132.33 209.33 133.33 209.58 132.58
C219.58 103.08 239.58 87.58 246.33 81.33 C253.08 75.08 256.63 74.47 247.33 81.58 C218.58 103.58
210.34 132.23 210.83 132.33 C222.33 134.83 211.33 140.33 211.83 139.83 C214.85 136.81 214.83 145.83 214.83
145.83 C214.83 145.83 231.83 110.83 298.33 66.33 C302.43 63.59 445.83 -14.67 395.83 80.83 C393.24 85.79 375.83
105.83 375.83 105.83 C375.83 105.83 377.33 114.33 371.33 121.33 C370.3 122.53 367.83 134.33 361.83 140.83 C360.14 142.67
361.81 139.25 361.83 140.83 C362.33 170.83 337.76 170.17 339.33 170.33 C348.83 171.33 350.19 183.66 350.33 183.83 C355.83
190.33 353.83 191.83 355.83 194.83 C366.63 211.02 355.24 210.05 356.83 212.83 C360.83 219.83 355.99 222.72 357.33 224.83
C360.83 230.33 354.75 233.84 354.83 235.33 C355.33 243.83 349.67 240.73 349.83 244.33 C350.33 255.33 346.33 250.83 343.83 254.83
C336.33 266.83 333.46 262.38 332.83 263.83 C329.83 270.83 325.81 269.15 324.33 270.83 C320.83 274.83 317.33 274.83 315.83 276.33
C308.83 283.33 304.86 278.39 303.83 278.83 C287.83 285.83 280.33 280.17 277.83 280.33 C270.33 280.83 271.48 279.67 269.33 277.83
C237.83 250.83 219.33 211.83 215.83 206.83 C214.4 204.79 211.35 193.12 212.33 195.83 C214.33 201.33 213.33 250.33 207.83 250.33
C202.33 250.33 201.83 204.33 205.33 195.83 C206.43 193.16 204.4 203.72 201.79 206.83 C196.33 213.33 179.5 250.83 147.59 277.83
C145.42 279.67 146.58 280.83 138.98 280.33 C136.46 280.17 128.85 285.83 112.65 278.83 C111.61 278.39 107.58 283.33 100.49 276.33
C98.97 274.83 95.43 274.83 91.88 270.83 C90.39 269.15 86.31 270.83 83.27 263.83 C82.64 262.38 79.73 266.83 72.13 254.83 C69.6 250.83
65.54 255.33 66.05 244.33 C66.22 240.73 60.48 243.83 60.99 235.33 C61.08 233.84 54.91 230.33 58.45 224.83 C59.81 222.72 54.91 219.83
58.96 212.83 C60.57 210.05 49.04 211.02 59.97 194.83 C62 191.83 59.97 190.33 65.54 183.83 C65.69 183.66 67.06 171.33 76.69 170.33
C78.28 170.17 53.39 170.83 53.9 140.83 C53.92 139.25 55.61 142.67 53.9 140.83 C47.82 134.33 45.32 122.53 44.27 121.33 C38.19 114.33
39.71 105.83 39.71 105.83 C39.71 105.83 22.08 85.79 19.46 80.83 C-31.19 -14.67 114.07 63.59 118.22 66.33 C185.58 110.83 202 145.83
202 145.83 C202 145.83 202.36 143.28 203 141.83 C203.64 140.39 204.56 140.02 204.33 139.83 z"""
def ptest():
svg.parseString(bpath)
if __name__ == "__main__":
profile()
# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX
# All rights reserved.
#
# This software is provided without warranty under the terms of the BSD
# license included in LICENSE.txt and may be redistributed only under
# the conditions described in the aforementioned license. The license
# is also available online at http://www.enthought.com/licenses/BSD.txt
#
# Thanks for using Enthought open source!
import math
from pyface.qt import QtCore, QtGui
class GutterWidget(QtGui.QWidget):
min_width = 5
def sizeHint(self):
return QtCore.QSize(self.min_width, 0)
def paintEvent(self, event):
""" Paint the line numbers.
"""
painter = QtGui.QPainter(self)
painter.fillRect(event.rect(), self.pallette().window())
def wheelEvent(self, event):
""" Delegate mouse wheel events to parent for seamless scrolling.
"""
self.parent().wheelEvent(event)
class StatusGutterWidget(GutterWidget):
""" Draws status markers
"""
def __init__(self, *args, **kw):
super().__init__(*args, **kw)
self.error_lines = []
self.warn_lines = []
self.info_lines = []
def sizeHint(self):
return QtCore.QSize(10, 0)
def paintEvent(self, event):
""" Paint the line numbers.
"""
painter = QtGui.QPainter(self)
painter.fillRect(event.rect(), self.palette().window())
cw = self.parent()
pixels_per_block = self.height() / float(cw.blockCount())
for line in self.info_lines:
painter.fillRect(
QtCore.QRect(0, line * pixels_per_block, self.width(), 3),
QtCore.Qt.GlobalColor.green,
)
for line in self.warn_lines:
painter.fillRect(
QtCore.QRect(0, line * pixels_per_block, self.width(), 3),
QtCore.Qt.GlobalColor.yellow,
)
for line in self.error_lines:
painter.fillRect(
QtCore.QRect(0, line * pixels_per_block, self.width(), 3),
QtCore.Qt.GlobalColor.red,
)
class LineNumberWidget(GutterWidget):
""" Draw line numbers.
"""
min_char_width = 4
def fontMetrics(self):
# QWidget's fontMetrics method does not provide an up to date
# font metrics, just one corresponding to the initial font
return QtGui.QFontMetrics(self.font)
def set_font(self, font):
self.font = font
def digits_width(self):
nlines = max(1, self.parent().blockCount())
ndigits = max(
self.min_char_width, int(math.floor(math.log10(nlines) + 1))
)
# QFontMetrics.width() is deprecated and Qt docs suggest using
# horizontalAdvance() instead, but is only available since Qt 5.11
if QtCore.__version_info__ >= (5, 11):
width = max(
self.fontMetrics().horizontalAdvance("0" * ndigits) + 3,
self.min_width
)
else:
width = max(
self.fontMetrics().width("0" * ndigits) + 3, self.min_width
)
return width
def sizeHint(self):
return QtCore.QSize(self.digits_width(), 0)
def paintEvent(self, event):
""" Paint the line numbers.
"""
painter = QtGui.QPainter(self)
painter.setFont(self.font)
painter.fillRect(event.rect(), self.palette().window())
cw = self.parent()
block = cw.firstVisibleBlock()
blocknum = block.blockNumber()
top = (
cw.blockBoundingGeometry(block)
.translated(cw.contentOffset())
.top()
)
bottom = top + int(cw.blockBoundingRect(block).height())
painter.setBrush(self.palette().windowText())
while block.isValid() and top <= event.rect().bottom():
if block.isVisible() and bottom >= event.rect().top():
painter.drawText(
int(0),
int(top),
int(self.width() - 2),
self.fontMetrics().height(),
QtCore.Qt.AlignmentFlag.AlignRight,
str(blocknum + 1),
)
block = block.next()
top = bottom
bottom = top + int(cw.blockBoundingRect(block).height())
blocknum += 1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment