Skip to content

Commit 64a9401

Browse files
Merge branch 'develop'
2 parents d4c9737 + 1260252 commit 64a9401

12 files changed

Lines changed: 155 additions & 93 deletions

File tree

.env.template

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,10 @@
1-
PYTHONPATH=.
1+
# Python interpreter (explicit path, e.g. WinPython). Auto-detected if empty.
2+
PYTHON=
3+
# WinPython base directory (legacy, prefer PYTHON instead)
4+
# WINPYDIRBASE=
5+
# Virtual environment directory (e.g. .venv39). Auto-discovered if empty.
6+
VENV_DIR=
7+
# Python path for development (sibling packages)
8+
PYTHONPATH=.
9+
# Locale (e.g. fr)
10+
LANG=

.github/workflows/test-PyQt5.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ jobs:
2222
strategy:
2323
fail-fast: false
2424
matrix:
25-
python-version: ["3.9", "3.11", "3.13"]
25+
python-version: ["3.9", "3.11", "3.13", "3.14"]
2626

2727
steps:
2828
- uses: actions/checkout@v4

.github/workflows/test-PyQt6.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ jobs:
2222
strategy:
2323
fail-fast: false
2424
matrix:
25-
python-version: ["3.9", "3.11", "3.13"]
25+
python-version: ["3.9", "3.11", "3.13", "3.14"]
2626

2727
steps:
2828
- uses: actions/checkout@v4

.github/workflows/test-PySide6.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ jobs:
2222
strategy:
2323
fail-fast: false
2424
matrix:
25-
python-version: ["3.9", "3.11", "3.13"]
25+
python-version: ["3.9", "3.11", "3.13", "3.14"]
2626

2727
steps:
2828
- uses: actions/checkout@v4

.gitignore

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ doctmp/
77

88
# Visual Studio Code
99
.env
10+
.venv*
11+
.*venv*
12+
13+
#AI instructions
14+
*.ai
15+
*.ai*
1016

1117
# Created by https://www.gitignore.io/api/python
1218

@@ -37,6 +43,16 @@ var/
3743
.installed.cfg
3844
*.egg
3945

46+
# Environments
47+
.env*
48+
.envrc
49+
.venv*
50+
env*/
51+
venv*/
52+
ENV/
53+
env.bak/
54+
venv.bak/
55+
4056
# PyInstaller
4157
# Usually these files are written by a python script from a template
4258
# before PyInstaller builds the exe, so as to inject date/other infos into it.

.vscode/tasks.json

Lines changed: 73 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,18 @@
4949
"statusbar": {
5050
"hide": true,
5151
},
52+
"cwd": "${workspaceFolder}",
53+
"statusbar": {
54+
"hide": true,
55+
},
5256
},
5357
"group": {
5458
"kind": "build",
5559
"isDefault": true,
60+
"isDefault": true,
5661
},
5762
"presentation": {
63+
"clear": true,
5864
"clear": true,
5965
"echo": true,
6066
"focus": false,
@@ -81,6 +87,7 @@
8187
"focus": false,
8288
"panel": "dedicated",
8389
"reveal": "always",
90+
"reveal": "always",
8491
"showReuseMessage": true,
8592
},
8693
"type": "shell",
@@ -94,6 +101,7 @@
94101
"-m",
95102
"pylint",
96103
"qwt",
104+
// "--disable=R0801,C0103,C0114,C0115,C0116,W0612,W0613",
97105
"--disable=fixme,C,R,W",
98106
],
99107
"options": {
@@ -132,12 +140,14 @@
132140
"group": {
133141
"kind": "build",
134142
"isDefault": true,
143+
"isDefault": true,
135144
},
136145
"presentation": {
146+
"clear": true,
137147
"echo": true,
138-
"reveal": "always",
139148
"focus": false,
140149
"panel": "dedicated",
150+
"reveal": "always",
141151
"showReuseMessage": true,
142152
"clear": true,
143153
},
@@ -160,7 +170,7 @@
160170
"options": {
161171
"cwd": "${workspaceFolder}",
162172
"env": {
163-
"COVERAGE_PROCESS_START": "${workspaceFolder}/.coveragerc",
173+
"UNATTENDED": "1",
164174
},
165175
"statusbar": {
166176
"hide": true,
@@ -189,9 +199,6 @@
189199
},
190200
"options": {
191201
"cwd": "${workspaceFolder}",
192-
"env": {
193-
"COVERAGE_PROCESS_START": "${workspaceFolder}/.coveragerc",
194-
},
195202
},
196203
"presentation": {
197204
"panel": "dedicated",
@@ -203,56 +210,80 @@
203210
],
204211
},
205212
{
206-
"label": "📷 Take test screenshots",
213+
"label": "Take screenshots",
207214
"type": "shell",
208215
"command": "${command:python.interpreterPath}",
209216
"args": [
210217
"scripts/run_with_env.py",
211218
"${command:python.interpreterPath}",
212-
"qwt/tests/__init__.py",
213-
"--mode",
214-
"screenshots",
219+
"scripts/take_screenshots.py",
215220
],
216221
"options": {
217222
"cwd": "${workspaceFolder}",
223+
"env": {
224+
"UNATTENDED": "1",
225+
},
226+
},
227+
"group": {
228+
"kind": "build",
229+
"isDefault": true,
218230
},
219231
"presentation": {
220232
"clear": true,
233+
"echo": true,
234+
"focus": false,
221235
"panel": "dedicated",
236+
"reveal": "always",
237+
"showReuseMessage": true,
238+
"clear": true,
222239
},
223-
"problemMatcher": [],
224240
},
225241
{
226-
"label": "📷 Take doc screenshots",
227-
"type": "shell",
242+
"label": "🔦 Pylint",
228243
"command": "${command:python.interpreterPath}",
229244
"args": [
230245
"scripts/run_with_env.py",
231246
"${command:python.interpreterPath}",
232-
"doc/plot_example.py",
247+
"-m",
248+
"pylint",
249+
"qwt",
250+
"--disable=fixme,C,R,W",
233251
],
234252
"options": {
235253
"cwd": "${workspaceFolder}",
236254
},
255+
"group": {
256+
"kind": "build",
257+
"isDefault": true,
258+
},
237259
"presentation": {
260+
"clear": true,
261+
"echo": true,
262+
"focus": false,
238263
"panel": "dedicated",
264+
"reveal": "always",
265+
"showReuseMessage": true,
239266
},
240-
"problemMatcher": [],
267+
"type": "shell",
241268
},
242269
{
243-
"label": "📷 Take symbol screenshots",
270+
"label": "Build documentation",
244271
"type": "shell",
245-
"command": "${command:python.interpreterPath}",
246-
"args": [
247-
"scripts/run_with_env.py",
248-
"${command:python.interpreterPath}",
249-
"doc/symbol_path_example.py",
250-
],
272+
"windows": {
273+
"command": "${command:python.interpreterPath} scripts/run_with_env.py ${command:python.interpreterPath} -m sphinx -b html doc build/doc; if ($?) { start build\\doc\\index.html }"
274+
},
275+
"linux": {
276+
"command": "${command:python.interpreterPath} scripts/run_with_env.py ${command:python.interpreterPath} -m sphinx -b html doc build/doc && xdg-open build/doc/index.html"
277+
},
278+
"osx": {
279+
"command": "${command:python.interpreterPath} scripts/run_with_env.py ${command:python.interpreterPath} -m sphinx -b html doc build/doc && open build/doc/index.html"
280+
},
251281
"options": {
252282
"cwd": "${workspaceFolder}",
253-
},
254-
"presentation": {
255-
"panel": "dedicated",
283+
"cwd": "${workspaceFolder}",
284+
"env": {
285+
"UNATTENDED": "1"
286+
}
256287
},
257288
"problemMatcher": [],
258289
},
@@ -304,21 +335,22 @@
304335
{
305336
"label": "📚 Build documentation",
306337
"type": "shell",
307-
"command": "${command:python.interpreterPath}",
308-
"args": [
309-
"scripts/run_with_env.py",
310-
"${command:python.interpreterPath}",
311-
"-m",
312-
"sphinx",
313-
"build",
314-
"doc",
315-
"${workspaceFolder}/build/doc",
316-
"-b",
317-
"html",
318-
],
338+
"windows": {
339+
"command": "if (Test-Path MANIFEST) { Remove-Item MANIFEST }; ${command:python.interpreterPath} scripts/run_with_env.py ${command:python.interpreterPath} -m build; if (Test-Path PythonQwt.egg-info) { Remove-Item -Recurse -Force PythonQwt.egg-info }"
340+
},
341+
"linux": {
342+
"command": "rm -f MANIFEST && ${command:python.interpreterPath} scripts/run_with_env.py ${command:python.interpreterPath} -m build && rm -rf PythonQwt.egg-info"
343+
},
344+
"osx": {
345+
"command": "rm -f MANIFEST && ${command:python.interpreterPath} scripts/run_with_env.py ${command:python.interpreterPath} -m build && rm -rf PythonQwt.egg-info"
346+
},
319347
"options": {
320348
"cwd": "${workspaceFolder}",
349+
"env": {
350+
"UNATTENDED": "1"
351+
}
321352
},
353+
"problemMatcher": [],
322354
"group": {
323355
"kind": "build",
324356
"isDefault": true,
@@ -345,14 +377,12 @@
345377
"command": "open build/doc/index.html",
346378
},
347379
"options": {
348-
"cwd": "${workspaceFolder}",
380+
"cwd": "scripts",
381+
"env": {
382+
"PYTHON": "${command:python.interpreterPath}",
383+
"UNATTENDED": "1"
384+
}
349385
},
350-
"problemMatcher": [],
351-
},
352-
{
353-
"label": "📦 Build package",
354-
"type": "shell",
355-
"command": "${command:python.interpreterPath}",
356386
"args": [
357387
"scripts/run_with_env.py",
358388
"${command:python.interpreterPath}",

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
# PythonQwt Releases
22

3+
## Version 0.16.1
4+
5+
### Bug fixes
6+
7+
- Fixed [Issue #107](https://github.com/PlotPyStack/PythonQwt/issues/107): corrected a Windows crash/access violation that could occur in long-running sessions creating and rendering large amounts of data, due to GDI handle exhaustion caused by object lifetime and cache growth
8+
- Preserved the rendering performance improvements introduced in 0.16.0 while restoring safer Qt object ownership for internal text/scale-draw private data and adding defensive limits to font-metrics caches
9+
10+
311
## Version 0.16.0
412

513
### Performance

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ version = { attr = "qwt.__version__" }
5959
addopts = "qwt"
6060

6161
[tool.ruff]
62-
exclude = [".git", ".vscode", "build", "dist"]
62+
exclude = [".git", ".vscode", "build", "dist","venv*",".venv*"]
6363
line-length = 88 # Same as Black.
6464
indent-width = 4 # Same as Black.
6565
target-version = "py39" # Assume Python 3.9.

qwt/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@
6363
from qwt.text import QwtText # noqa: F401
6464
from qwt.toqimage import array_to_qimage as toQImage # noqa: F401
6565

66-
__version__ = "0.16.0"
66+
__version__ = "0.16.1"
6767
QWT_VERSION_STR = "6.1.5"
6868

6969

qwt/scale_draw.py

Lines changed: 7 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
from qtpy.QtCore import (
2626
QLineF,
27+
QObject,
2728
QPoint,
2829
QPointF,
2930
QRect,
@@ -50,22 +51,11 @@
5051
_ALIGN_BOTTOM = int(Qt.AlignBottom)
5152

5253

53-
class QwtAbstractScaleDraw_PrivateData(object):
54-
# See QwtText_PrivateData: ``QObject`` inheritance is unused and the
55-
# base class' ``__init__`` is a measurable cost in tick-heavy renders.
56-
__slots__ = (
57-
"spacing",
58-
"penWidth",
59-
"minExtent",
60-
"components",
61-
"tick_length",
62-
"tick_lighter_factor",
63-
"map",
64-
"scaleDiv",
65-
"labelCache",
66-
)
54+
class QwtAbstractScaleDraw_PrivateData(QObject):
55+
# QObject base class restored for Qt parent/child ownership semantics.
6756

6857
def __init__(self):
58+
QObject.__init__(self)
6959
self.spacing = 4
7060
self.penWidth = 0
7161
self.minExtent = 0.0
@@ -492,20 +482,11 @@ def invalidateCache(self):
492482
self.__data.labelCache.clear()
493483

494484

495-
class QwtScaleDraw_PrivateData(object):
496-
# See QwtText_PrivateData: ``QObject`` inheritance is unused and the
497-
# base class' ``__init__`` is a measurable cost in tick-heavy renders.
498-
__slots__ = (
499-
"len",
500-
"alignment",
501-
"orientation",
502-
"labelAlignment",
503-
"labelRotation",
504-
"labelAutoSize",
505-
"pos",
506-
)
485+
class QwtScaleDraw_PrivateData(QObject):
486+
# QObject base class restored for Qt parent/child ownership semantics.
507487

508488
def __init__(self):
489+
QObject.__init__(self)
509490
self.len = 0
510491
self.alignment = QwtScaleDraw.BottomScale
511492
# Cached orientation - kept in sync by ``QwtScaleDraw.setAlignment``

0 commit comments

Comments
 (0)