enthought / enable

Enable: low-level drawing and interaction
Other
91 stars 45 forks source link

AttributeError: type object 'Unimplemented' has no attribute 'add_path' #1002

Open capn-freako opened 1 year ago

capn-freako commented 1 year ago

Since updating as follows:

my Traits/UI GUI application yields the following error upon launch, from the enable/savage/svg/backends/kiva/renderer.py file:

AttributeError: type object 'Unimplemented' has no attribute 'add_path'

Is this a known issue?

Any ideas about what's going on here?

Thanks! -db

Here's the full back-trace:

Traceback (most recent call last):
  File "C:\Users\capnf\anaconda3\envs\pybert-dev\lib\runpy.py", line 194, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "C:\Users\capnf\anaconda3\envs\pybert-dev\lib\runpy.py", line 87, in _run_code
    exec(code, run_globals)
  File "C:\Users\capnf\Documents\GitHub\PyBERT\pybert\__main__.py", line 2, in <module>
    from pybert.pybert      import PyBERT
  File "C:\Users\capnf\Documents\GitHub\PyBERT\pybert\pybert.py", line 29, in <module>
    from chaco.api import ArrayPlotData, GridPlotContainer
  File "C:\Users\capnf\anaconda3\envs\pybert-dev\lib\site-packages\chaco\api.py", line 343, in <module>
    from chaco.overlays.api import (
  File "C:\Users\capnf\anaconda3\envs\pybert-dev\Lib\site-packages\shiboken2\files.dir\shibokensupport\feature.py", line 139, in _import
    return original_import(name, *args, **kwargs)
  File "C:\Users\capnf\anaconda3\envs\pybert-dev\lib\site-packages\chaco\overlays\api.py", line 63, in <module>
    from chaco.overlays.layers.api import (
  File "C:\Users\capnf\anaconda3\envs\pybert-dev\Lib\site-packages\shiboken2\files.dir\shibokensupport\feature.py", line 139, in _import
    return original_import(name, *args, **kwargs)
  File "C:\Users\capnf\anaconda3\envs\pybert-dev\lib\site-packages\chaco\overlays\layers\api.py", line 11, in <module>
    from .status_layer import ErrorLayer, StatusLayer, WarningLayer
  File "C:\Users\capnf\anaconda3\envs\pybert-dev\Lib\site-packages\shiboken2\files.dir\shibokensupport\feature.py", line 139, in _import
    return original_import(name, *args, **kwargs)
  File "C:\Users\capnf\anaconda3\envs\pybert-dev\lib\site-packages\chaco\overlays\layers\status_layer.py", line 18, in <module>
    from enable.savage.svg.backends.kiva.renderer import Renderer as KivaRenderer
  File "C:\Users\capnf\anaconda3\envs\pybert-dev\Lib\site-packages\shiboken2\files.dir\shibokensupport\feature.py", line 139, in _import
    return original_import(name, *args, **kwargs)
  File "C:\Users\capnf\anaconda3\envs\pybert-dev\lib\site-packages\enable\savage\svg\backends\kiva\renderer.py", line 36, in <module>
    class CompiledPath(KivaCompiledPath):
  File "C:\Users\capnf\anaconda3\envs\pybert-dev\lib\site-packages\enable\savage\svg\backends\kiva\renderer.py", line 38, in CompiledPath
    AddPath = KivaCompiledPath.add_path
AttributeError: type object 'Unimplemented' has no attribute 'add_path'
corranwebster commented 1 year ago

Are you on WxPython or one of the Qt backends? Do you know which Kiva backend you are using (the default is "image" but sometimes you may get another one).

I have not seen this particular issue before.

My suspicion is that:

So my suspicions are that there is something wrong with the build, something unusual with the backend or environment being used, or a regression has crept in where KivaCompiledPath is not being imported correctly.

capn-freako commented 1 year ago

The original case was with:

from traits.etsconfig.api import ETSConfig
ETSConfig.toolkit = 'qt.celiagg'

Changing to:

ETSConfig.toolkit = 'qt.qpainter'

results in a successful launch, but none of my plots are visible and the program crashes after a few minutes. Here's that backtrace:

Traceback (most recent call last):
  File "C:\Users\capnf\anaconda3\envs\pybert-dev\lib\site-packages\enable\qt4\base_window.py", line 255, in paintEvent
    self.handler.paintEvent(event)
  File "C:\Users\capnf\anaconda3\envs\pybert-dev\lib\site-packages\enable\qt4\base_window.py", line 90, in paintEvent
    self._enable_window._paint(event)
  File "C:\Users\capnf\anaconda3\envs\pybert-dev\lib\site-packages\enable\abstract_window.py", line 536, in _paint
    self.component.draw(gc, view_bounds=(0, 0, size[0], size[1]))
  File "C:\Users\capnf\anaconda3\envs\pybert-dev\lib\site-packages\enable\component.py", line 410, in draw
    self._draw(gc, view_bounds, mode)
  File "C:\Users\capnf\anaconda3\envs\pybert-dev\lib\site-packages\enable\component.py", line 791, in _draw
    self._dispatch_draw(layer, gc, view_bounds, mode)
  File "C:\Users\capnf\anaconda3\envs\pybert-dev\lib\site-packages\enable\container.py", line 270, in _dispatch_draw
    component._dispatch_draw(layer, gc, new_bounds, mode)
  File "C:\Users\capnf\anaconda3\envs\pybert-dev\lib\site-packages\enable\container.py", line 255, in _dispatch_draw
    my_handler(gc, view_bounds, mode)
  File "C:\Users\capnf\anaconda3\envs\pybert-dev\lib\site-packages\enable\container.py", line 293, in _draw_container_underlay
    self._draw_underlay(gc, view_bounds, mode)
  File "C:\Users\capnf\anaconda3\envs\pybert-dev\lib\site-packages\enable\component.py", line 902, in _draw_underlay
    underlay.overlay(self, gc, view_bounds, mode)
  File "C:\Users\capnf\anaconda3\envs\pybert-dev\lib\site-packages\chaco\grid.py", line 412, in overlay
    gc.clip_to_rect(
  File "C:\Users\capnf\anaconda3\envs\pybert-dev\lib\site-packages\kiva\qpainter.py", line 482, in clip_to_rect
    self.gc.setClipRect(
AttributeError: PySide2.QtGui.QPainter.setClipRect(): unsupported keyword 'operation'
QBackingStore::endPaint() called with active painter; did you forget to destroy it or call QPainter::end() on it?
QBackingStore::endPaint() called with active painter; did you forget to destroy it or call QPainter::end() on it?
QBackingStore::endPaint() called with active painter; did you forget to destroy it or call QPainter::end() on it?
QBackingStore::endPaint() called with active painter; did you forget to destroy it or call QPainter::end() on it?
QBackingStore::endPaint() called with active painter; did you forget to destroy it or call QPainter::end() on it?
Segmentation fault

Changing to:

ETSConfig.toolkit = 'qt.image'

yields normal program behavior, but that includes plot titles and axis labels with unreadable small font sizes on my high DPI display Windows machine, which is what started me on this quest to begin with. :(

Changing to:

ETSConfig.toolkit = 'wx'

results in a crash at launch w/ this backtrace:

Traceback (most recent call last):
  File "C:\Users\capnf\anaconda3\envs\pybert-dev\lib\runpy.py", line 194, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "C:\Users\capnf\anaconda3\envs\pybert-dev\lib\runpy.py", line 87, in _run_code
    exec(code, run_globals)
  File "C:\Users\capnf\Documents\GitHub\PyBERT\pybert\__main__.py", line 2, in <module>
    from pybert.pybert      import PyBERT
  File "C:\Users\capnf\Documents\GitHub\PyBERT\pybert\pybert.py", line 31, in <module>
    from chaco.api import ArrayPlotData, GridPlotContainer
  File "C:\Users\capnf\anaconda3\envs\pybert-dev\lib\site-packages\chaco\api.py", line 314, in <module>
    from .plots.horizon_plot import BandedMapper
  File "C:\Users\capnf\anaconda3\envs\pybert-dev\lib\site-packages\chaco\plots\horizon_plot.py", line 14, in <module>
    from enable.api import transparent_color_trait
  File "C:\Users\capnf\anaconda3\envs\pybert-dev\lib\site-packages\enable\api.py", line 184, in <module>
    from .base import (
  File "C:\Users\capnf\anaconda3\envs\pybert-dev\lib\site-packages\enable\base.py", line 41, in <module>
    from .colors import color_table, transparent_color
  File "C:\Users\capnf\anaconda3\envs\pybert-dev\lib\site-packages\enable\colors.py", line 13, in <module>
    from pyface.toolkit import toolkit
  File "C:\Users\capnf\anaconda3\envs\pybert-dev\lib\site-packages\pyface\toolkit.py", line 23, in <module>
    toolkit = toolkit_object = find_toolkit("pyface.toolkits")
  File "C:\Users\capnf\anaconda3\envs\pybert-dev\lib\site-packages\pyface\base_toolkit.py", line 263, in find_toolkit
    return import_toolkit(ETSConfig.toolkit, entry_point)
  File "C:\Users\capnf\anaconda3\envs\pybert-dev\lib\site-packages\pyface\base_toolkit.py", line 229, in import_toolkit
    raise RuntimeError(msg)
RuntimeError: No pyface.toolkits plugin could be loaded for wx

(Did I get the syntax correct?)

capn-freako commented 1 year ago

By the way, should this work?:

ETSConfig.toolkit = 'qt5.celiagg'

It doesn't and I'm wondering if that's a clue. Here's the backtrace it yields:

Traceback (most recent call last):
  File "C:\Users\capnf\anaconda3\envs\pybert-dev\lib\runpy.py", line 194, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "C:\Users\capnf\anaconda3\envs\pybert-dev\lib\runpy.py", line 87, in _run_code
    exec(code, run_globals)
  File "C:\Users\capnf\Documents\GitHub\PyBERT\pybert\__main__.py", line 2, in <module>
    from pybert.pybert      import PyBERT
  File "C:\Users\capnf\Documents\GitHub\PyBERT\pybert\pybert.py", line 32, in <module>
    from chaco.api import ArrayPlotData, GridPlotContainer
  File "C:\Users\capnf\anaconda3\envs\pybert-dev\lib\site-packages\chaco\api.py", line 314, in <module>
    from .plots.horizon_plot import BandedMapper
  File "C:\Users\capnf\anaconda3\envs\pybert-dev\lib\site-packages\chaco\plots\horizon_plot.py", line 14, in <module>
    from enable.api import transparent_color_trait
  File "C:\Users\capnf\anaconda3\envs\pybert-dev\lib\site-packages\enable\api.py", line 184, in <module>
    from .base import (
  File "C:\Users\capnf\anaconda3\envs\pybert-dev\lib\site-packages\enable\base.py", line 41, in <module>
    from .colors import color_table, transparent_color
  File "C:\Users\capnf\anaconda3\envs\pybert-dev\lib\site-packages\enable\colors.py", line 13, in <module>
    from pyface.toolkit import toolkit
  File "C:\Users\capnf\anaconda3\envs\pybert-dev\lib\site-packages\pyface\toolkit.py", line 23, in <module>
    toolkit = toolkit_object = find_toolkit("pyface.toolkits")
  File "C:\Users\capnf\anaconda3\envs\pybert-dev\lib\site-packages\pyface\base_toolkit.py", line 263, in find_toolkit
    return import_toolkit(ETSConfig.toolkit, entry_point)
  File "C:\Users\capnf\anaconda3\envs\pybert-dev\lib\site-packages\pyface\base_toolkit.py", line 207, in import_toolkit
    raise RuntimeError(msg)
RuntimeError: No pyface.toolkits plugin found for toolkit qt5
capn-freako commented 1 year ago

Does this all look correct?

$ conda list qt
# packages in environment at C:\Users\capnf\anaconda3\envs\pybert-dev:
#
# Name                    Version                   Build  Channel
pyqt                      5.15.7           py38hd77b12b_0
pyqt5-sip                 12.11.0          py38hd77b12b_0
qt-main                   5.15.6               hf0cf448_0    conda-forge
qt-webengine              5.15.9               hb9a9bb5_4
qtconsole                 5.3.2            py38haa95532_0
qtpy                      2.2.0            py38haa95532_0
qtwebkit                  5.212                h0db62b3_6    conda-forge

$ conda list pyface
# packages in environment at C:\Users\capnf\anaconda3\envs\pybert-dev:
#
# Name                    Version                   Build  Channel
pyface                    7.4.2              pyhd8ed1ab_0    conda-forge
corranwebster commented 1 year ago

Thanks for the feedback. I think that the celiagg backend is the issue for the original problem - somehow it looks like its compiled path method isn't being provided correctly to Enable or Savage, so that feels like there is a bug somewhere. either in the celiagg backend or in the dynamic import logic. It's possible that this is related to #959 but the fact that you are getting an Unimplemented object implies a toolkit import issue is more likely.

It might be worth a try with qt4.celiagg - we are moving away from the old qt4 to just bare qt but there might be a few places where the old qt4 may linger and still be required (we aren't going to support qt5 or qt6 since we use the same codebase for all qt versions, so qt5.celiagg won't work).

Your environment looks OK - you seem to be on the most recent versions of ETS. We just released a bugfix release of TraitsUI but I don't think it should affect this issue. Chaco is in dire need of a release, hopefully we will get to that this quarter.

In terms of the original problem, if you are having issues with high-dpi displays, in recent versions of enable you can use Item(..., editor=ComponentEditor(..., high_resolution=False)) to render at 72-ish DPI if you have control over the editor. That may be something else you can try as a work-around.

It might also be worth digging in a bit at the font size problem for the image backend - at this point there is a known issue with polygon rendering at high resolution on the image backend (the lines are half the width they should be) but I thought we had resolved the font rendering issues. Can you run the benchmark in your environment (python -m enable.gcbench -o <output_dir> or something similar should work) and upload the high-res output of show_text_font_styles and show_text from the image backend either here or in a new issue if they look dramatically different from the lo-res versions.

For the other errors:

capn-freako commented 1 year ago

Can you run the benchmark in your environment (python -m enable.gcbench -o <output_dir> or something similar should work) and upload the high-res output of show_text_font_styles and show_text from the image backend either here or in a new issue if they look dramatically different from the lo-res versions.

I don't see an "image" backend in the output. Is it the "kiva.agg" backend?

$ python -m enable.gcbench -o enable.gcbench
Benchmarking backend: kiva.agg
        Benchmark draw_image
        Benchmark draw_image_2x
        Benchmark draw_marker_at_points
        Benchmark draw_marker_at_points_2x
        Benchmark draw_path
        Benchmark draw_path_2x
        Benchmark draw_path_at_points
        Benchmark draw_path_at_points_2x
        Benchmark draw_path_linear_gradient
        Benchmark draw_path_linear_gradient_2x
        Benchmark draw_rect
        Benchmark draw_rect_2x
        Benchmark show_text
        Benchmark show_text_2x
        Benchmark show_text_radial_gradient
        Benchmark show_text_radial_gradient_2x
Benchmarking backend: blend2d ... Not available
Benchmarking backend: cairo ... Not available
Benchmarking backend: celiagg ... Not available
Benchmarking backend: opengl ... Not available
Benchmarking backend: qpainter
        Benchmark draw_image
        Benchmark draw_image_2x
        Benchmark draw_marker_at_points ... Failed
        Benchmark draw_marker_at_points_2x ... Failed
        Benchmark draw_path
        Benchmark draw_path_2x
        Benchmark draw_path_at_points
        Benchmark draw_path_at_points_2x
        Benchmark draw_path_linear_gradient
        Benchmark draw_path_linear_gradient_2x
        Benchmark draw_rect
        Benchmark draw_rect_2x
        Benchmark show_text
        Benchmark show_text_2x
        Benchmark show_text_radial_gradient ... Failed
        Benchmark show_text_radial_gradient_2x ... Failed
Benchmarking backend: quartz ... Not available
Benchmarking backend: pdf ... Not available
Benchmarking backend: ps
        Benchmark draw_image
        Benchmark draw_marker_at_points ... Failed
        Benchmark draw_path
        Benchmark draw_path_at_points ... Failed
        Benchmark draw_path_linear_gradient
        Benchmark draw_rect
        Benchmark show_text ... Failed
        Benchmark show_text_radial_gradient ... Failed
Benchmarking backend: svg
        Benchmark draw_image
        Benchmark draw_marker_at_points ... Failed
        Benchmark draw_path
        Benchmark draw_path_at_points ... Failed
        Benchmark draw_path_linear_gradient
        Benchmark draw_rect
        Benchmark show_text
        Benchmark show_text_radial_gradient
capn-freako commented 1 year ago

Here are all outputs containing the word "text" in their filenames. kiva agg show_text kiva agg show_text_2x kiva agg show_text_radial_gradient kiva agg show_text_radial_gradient_2x qpainter show_text qpainter show_text_2x svg show_text svg show_text_radial_gradient

capn-freako commented 1 year ago
  • the qpainter backend failure looks like a genuine bug

I just noticed this, in the first few lines of that particular backtrace:

Traceback (most recent call last):
  File "C:\Users\capnf\anaconda3\envs\pybert-dev\lib\site-packages\enable\qt4\base_window.py", line 255, in paintEvent
    self.handler.paintEvent(event)
  File "C:\Users\capnf\anaconda3\envs\pybert-dev\lib\site-packages\enable\qt4\base_window.py", line 90, in paintEvent
    self._enable_window._paint(event)

It looks like my (locally built) Enable package has "hard coded" the selection of Qt4, as opposed to Qt5. Am I reading that correctly? If so, it's strange, for two reasons:

  1. I've been in a concerted effort to push this whole thing towards Qt5, in hopes that that might solve my small font problem.
  2. I don't think I have Qt4 installed:
$ conda list qt
# packages in environment at C:\Users\capnf\anaconda3\envs\pybert-dev:
#
# Name                    Version                   Build  Channel
pyqt                      5.15.7           py38hd77b12b_0
pyqt5-sip                 12.11.0          py38hd77b12b_0
qt-main                   5.15.6               hf0cf448_0    conda-forge
qt-webengine              5.15.9               hb9a9bb5_4
qtconsole                 5.3.2            py38haa95532_0
qtpy                      2.2.0            py38haa95532_0
qtwebkit                  5.212                h0db62b3_6    conda-forge

It all seems to be version 5.

Here's my build YAML for Enable; see any problems?

{% set name = "enable" %}
{% set version = "5.3.1" %}

package:
  name: "{{ name|lower }}"
  version: "{{ version }}"

source:
  git_url: https://github.com/enthought/{{name}}.git
  git_rev: {{version}}

build:
  number: 1
  script: "{{ PYTHON }} -m pip install . --no-deps --ignore-installed -vv "

requirements:
  build:
    - setuptools
    - git
    - cmake
    - swig =3
    # - {{compiler('c')}}
    # - {{ compiler('cxx') }}
    # - {{ cdt('xorg-x11-devel') }}  # [linux]
    # - vs2017_win-64
    - vs2019_win-64
  host:
    - fonttools
    - numpy
    - pillow
    - pip
    - pyface >=7.4.2
    - pyparsing
    - python
    - six
    - traitsui
    - Cython
  run:
    - fonttools
    - numpy
    - pillow
    - pyface >=7.4.2
    - pyparsing
    - python
    - six
    - traitsui

test:
  imports:
    - enable
    - enable.drawing
    - enable.gadgets
    - enable.layout
    - enable.null
    - enable.primitives
    # - enable.pyglet_backend
    - enable.qt4
    - enable.savage
    - enable.savage.compliance
    - enable.savage.svg
    - enable.savage.svg.backends
    - enable.savage.svg.backends.kiva
    - enable.savage.svg.backends.null
    - enable.savage.svg.backends.wx
    - enable.savage.svg.css
    - enable.savage.svg.tests
    - enable.savage.svg.tests.css
    - enable.savage.trait_defs
    - enable.savage.trait_defs.ui
    - enable.savage.trait_defs.ui.qt4
    - enable.savage.trait_defs.ui.wx
    - enable.tests
    - enable.tests.primitives
    - enable.tests.qt4
    - enable.tests.tools
    # - enable.tests.tools.apptools
    - enable.tests.wx
    - enable.tools
    - enable.tools.apptools
    - enable.tools.pyface
    - enable.tools.toolbars
    - enable.trait_defs
    - enable.trait_defs.ui
    - enable.trait_defs.ui.qt4
    - enable.trait_defs.ui.wx
    # - enable.vtk_backend
    - enable.wx
    - kiva
    - kiva.agg
    - kiva.agg.tests
    - kiva.fonttools
    - kiva.fonttools.tests
    - kiva.quartz
    - kiva.tests
    - kiva.tests.agg
    - kiva.trait_defs
    - kiva.trait_defs.ui
    - kiva.trait_defs.ui.wx

about:
  home: "https://github.com/enthought/enable/"
  license: "BSD"
  license_family: "BSD"
  license_file: ""
  summary: "low-level drawing and interaction"
  doc_url: ""
  dev_url: ""

extra:
  recipe-maintainers:
    - capn-freako
capn-freako commented 1 year ago

In terms of the original problem, if you are having issues with high-dpi displays, in recent versions of enable you can use Item(..., editor=ComponentEditor(..., high_resolution=False)) to render at 72-ish DPI if you have control over the editor. That may be something else you can try as a work-around.

I tried this, but it didn't change the size of my plot axis labels/numbers. Here's an example with the application full-screened:

image

corranwebster commented 1 year ago

You can safely ignore the qt4 in path names - this is a historical artefact from earlier iterations of toolkit selection where the toolkit name mapped to directories. The qt4 modules in fact contain code that runs on PyQt5, PySide2 and PyQt6 - we will likely rename them in the next major release of Pyface and TraitsUI, but it would be a potentially breaking change.

The actual mapping between toolkit names and modules is now handled by more standard setuptools entrypoints.

corranwebster commented 1 year ago

Sorry, yes, it is the "kiva.agg" backend. And some of the benchmarks are only in the main, not in the 5.3 release branch, so apologies for any confusion - this is probably good enough.

The output of the benchmark looked normal-ish (there's one that is clearly messed up, it would be good to know which one it was, but I suspect it's not the agg/image backend). You can get more info by opening the HTML file in the output directory.

I am now suspicious that this could be an issue with font selection on Windows. Do you know if the text is still small when you use a regular DPI display?

capn-freako commented 1 year ago

You can get more info by opening the HTML file in the output directory.

The messed up one is qpainter.

capn-freako commented 1 year ago

Do you know if the text is still small when you use a regular DPI display?

It is not. Things look normal on a "regular" (i.e. - not high DPI) resolution display.