Compare commits
403 Commits
pyqtgraph-
...
master
Author | SHA1 | Date |
---|---|---|
Kyle Sunden | 6fa4a0a3eb | |
Kyle Sunden | 0f3804e32c | |
Ogi Moore | af8c766de3 | |
Ogi Moore | 58325ee91b | |
Ogi Moore | 936108ec60 | |
Ogi Moore | 9b67e51230 | |
Ogi Moore | db74522003 | |
Kyle Sunden | 9ae38e6d21 | |
Kyle Sunden | 34c6981b0f | |
Ogi Moore | b8ca314597 | |
Kyle Sunden | ae484ba411 | |
Ogi Moore | fd6985565c | |
KIU Shueng Chuan | 2da769d6a5 | |
Ogi Moore | fb809e5260 | |
KIU Shueng Chuan | 4aeb0ce1dd | |
Martin Chase | a6bb2c6edd | |
Ogi Moore | e8b77a11e5 | |
Ogi Moore | e04ecdf554 | |
Nils Nemitz | 0cc3580687 | |
Ogi Moore | bc542ae1c4 | |
Ogi Moore | 5917e5e666 | |
Ogi Moore | 95c302fd0d | |
KIU Shueng Chuan | 4b59734629 | |
ntjess | 8d3e6cbd22 | |
Ogi Moore | e9d3b6ddd2 | |
Dane Owens | 1132c138a4 | |
Ogi Moore | b075035b0d | |
Jan Kotanski | 4334cd14de | |
Ogi Moore | 126b7e0925 | |
magnium | 6f4b6b1b88 | |
ntjess | babd037cf7 | |
Petras Jokubauskas | 745b04baa5 | |
Ogi Moore | 2a66460afc | |
Ogi Moore | 446a2c324c | |
Ogi Moore | d54da71138 | |
Ogi Moore | 13ab4de209 | |
Ogi Moore | c3dba791d1 | |
KIU Shueng Chuan | b5475cad6b | |
KIU Shueng Chuan | 6763b14fb9 | |
Ogi Moore | 98d7bcf560 | |
KIU Shueng Chuan | 6f1ea29fe4 | |
Ogi Moore | 175f84ecbc | |
KIU Shueng Chuan | 11b4a1bb47 | |
Ogi Moore | e752336b55 | |
KIU Shueng Chuan | 0cd926ff8e | |
KIU Shueng Chuan | e541c9ba1e | |
KIU Shueng Chuan | 44e32f00c7 | |
KIU Shueng Chuan | 679c1002c1 | |
KIU Shueng Chuan | 7e31a5e4ee | |
KIU Shueng Chuan | fa826bd4cb | |
Ogi Moore | 0d8eba36a6 | |
Ogi Moore | 690a416d5d | |
Ogi Moore | a537bc2b23 | |
KIU Shueng Chuan | 287ec9fbea | |
Nathan Jessurun | 503507ba50 | |
Nathan Jessurun | 3d15ca1038 | |
Nathan Jessurun | 6f44d27f2d | |
Ogi Moore | f16125c378 | |
Tim Gates | a2078f8a87 | |
Ogi Moore | 239e15a6f8 | |
KIU Shueng Chuan | 178e693a4d | |
KIU Shueng Chuan | 371facb17f | |
KIU Shueng Chuan | 482b92d700 | |
KIU Shueng Chuan | efa662415e | |
ntjess | 4bf1866c2a | |
Ogi Moore | fb2e684f45 | |
ntjess | e18d1fb1f2 | |
Ogi Moore | d0961cc320 | |
njessurun | c0eb3267d2 | |
Ogi Moore | b0bcec1ff2 | |
njessurun | ef99d3fbf6 | |
Nathan Jessurun | 84b491fb9b | |
Kyle Sunden | a472f8c5de | |
Ogi Moore | 1ddbfc8321 | |
KIU Shueng Chuan | aebc66dcaa | |
Kyle Sunden | 6a59b7e5b5 | |
Ogi Moore | 2d90e54441 | |
KIU Shueng Chuan | 04673ac98b | |
KIU Shueng Chuan | f64290be9e | |
KIU Shueng Chuan | 9355ecf469 | |
KIU Shueng Chuan | 75654b8495 | |
KIU Shueng Chuan | 9913f7c1e7 | |
Ogi Moore | 7aa5d0cbf4 | |
KIU Shueng Chuan | 643dd87800 | |
Ogi Moore | fe10b5e8dc | |
Ogi Moore | 0cfc9cd440 | |
Ogi Moore | d3755520d0 | |
KIU Shueng Chuan | 0afd8adcd8 | |
Martin Schulz | 96cccd96ec | |
Nils Nemitz | 5084d9b537 | |
Martin Chase | eb8965c2f4 | |
KIU Shueng Chuan | 1a34c8857f | |
Nils Nemitz | 7009084e4c | |
ntjess | 81823768c0 | |
Nils Nemitz | 8f96c78715 | |
Ogi Moore | d396d33799 | |
Martin Chase | 1d40d50b89 | |
KIU Shueng Chuan | 1184e3fcce | |
KIU Shueng Chuan | 394b0dd75c | |
KIU Shueng Chuan | 8a6640c419 | |
Ogi Moore | 66ec0996f4 | |
Ogi Moore | 699ed578f4 | |
Kenneth Lyons | 0ae6b753fc | |
Ogi Moore | 4e293dd17d | |
Nils Nemitz | 8b4db67ae9 | |
KIU Shueng Chuan | d9726dbcc1 | |
Ogi Moore | d5990bf32c | |
Dennis Goeries | cdb427ff2f | |
Ogi Moore | ed66eef203 | |
Ogi Moore | 58aac09387 | |
Kenneth Lyons | ba7129a719 | |
KIU Shueng Chuan | cbc9b4d310 | |
Ogi Moore | ddab4180e9 | |
KIU Shueng Chuan | 6f49ede5c1 | |
Ogi Moore | 76f3612245 | |
Artturin | 2de5cd78da | |
Ogi Moore | ddf73c2ecf | |
KIU Shueng Chuan | fa77dae941 | |
KIU Shueng Chuan | 5283eeb71b | |
KIU Shueng Chuan | aca627ac8c | |
KIU Shueng Chuan | 31e10fdc1d | |
KIU Shueng Chuan | f85a1015ad | |
KIU Shueng Chuan | e10dbfd9e1 | |
KIU Shueng Chuan | 6ca81fdddb | |
KIU Shueng Chuan | 7a17cda956 | |
KIU Shueng Chuan | dfd5b5dc1b | |
KIU Shueng Chuan | 5b2674c9d5 | |
KIU Shueng Chuan | f43f795950 | |
KIU Shueng Chuan | 9bf6c01f58 | |
KIU Shueng Chuan | bc52a2afe0 | |
KIU Shueng Chuan | fab505a431 | |
KIU Shueng Chuan | 1814ff535d | |
KIU Shueng Chuan | f698ccc06e | |
KIU Shueng Chuan | ce4c6d95ed | |
KIU Shueng Chuan | b5fc3d2a7e | |
KIU Shueng Chuan | 1b00d3448a | |
KIU Shueng Chuan | 5d7dd101f2 | |
KIU Shueng Chuan | 025ca08574 | |
KIU Shueng Chuan | ee9b1565bd | |
KIU Shueng Chuan | e158034c07 | |
KIU Shueng Chuan | 31f9d0024a | |
Martin Chase | bcb629495c | |
Ogi Moore | d28e1c8075 | |
Ogi Moore | b79171dc3a | |
Ogi Moore | 910142aa6f | |
KIU Shueng Chuan | 7442ab1e52 | |
Ogi Moore | b9e079b10c | |
Scott Talbert | db8180d88e | |
Ogi Moore | 1ce9da36d3 | |
Kenneth Lyons | bb94290d8e | |
KIU Shueng Chuan | e17428b018 | |
Ogi Moore | 6b4385ce0d | |
Jennifer Manriquez | 0c074ea005 | |
Kyle Sunden | fda6071ae5 | |
Kyle Sunden | 53fc415813 | |
Kyle Sunden | 49a97351e7 | |
Ogi Moore | 0c38ff2754 | |
Kyle Sunden | 89958dbaab | |
Kyle Sunden | e12ac50a05 | |
Etienne Dumur | 804f6b6649 | |
Ogi Moore | d82a47b864 | |
Nils Nemitz | 523b31e97f | |
Ogi Moore | 4b7dfdef88 | |
Ogi Moore | e83b91e9af | |
ntjess | 820c1604dd | |
Kenneth Lyons | 7ebae20c5d | |
KIU Shueng Chuan | e9ee11f010 | |
Ogi Moore | 02909999dc | |
KIU Shueng Chuan | 6fc02711a8 | |
KIU Shueng Chuan | e76328ab0e | |
KIU Shueng Chuan | f9f5d46589 | |
KIU Shueng Chuan | 96f7ce1325 | |
KIU Shueng Chuan | b843214f66 | |
KIU Shueng Chuan | 1a29cf7579 | |
Etienne Dumur | f5563501ed | |
Etienne Dumur | da39e8f460 | |
Etienne Dumur | 08e460ad34 | |
ntjess | 4a921ddf71 | |
Nils Nemitz | e79dacf805 | |
Ogi Moore | 5e8f200aa0 | |
Nils Nemitz | c07b95812c | |
Ogi Moore | 93fa8664cb | |
KIU Shueng Chuan | 5d55f3facf | |
Ogi Moore | 82da8f122e | |
Ogi Moore | 418a181691 | |
KIU Shueng Chuan | 3a1b74df6f | |
KIU Shueng Chuan | 11bf824c0a | |
KIU Shueng Chuan | 427128ece1 | |
KIU Shueng Chuan | 81baa182c5 | |
Ogi Moore | 3fbb4f6b02 | |
Ogi Moore | 27ad41a10c | |
Martin Chase | f764e2d3ff | |
Nils Nemitz | 775f1d629c | |
Ogi Moore | addb92f592 | |
Ogi Moore | 98e9ea9b79 | |
KIU Shueng Chuan | 30f4af5913 | |
Nils Nemitz | 362fbaa53a | |
Nils Nemitz | f79d0dfa14 | |
Ogi Moore | ba517aba0e | |
Jennifer Manriquez | 2899143e84 | |
Ogi Moore | 663bb75da4 | |
Ogi Moore | 30bc13831d | |
Ogi Moore | ee602ac243 | |
Ogi Moore | 61dedcb4a5 | |
KIU Shueng Chuan | 5dcea9bdac | |
Ogi Moore | c376c6fdcc | |
KIU Shueng Chuan | f63d1e4206 | |
KIU Shueng Chuan | 7c6d9fe6d5 | |
Ogi Moore | fff2bc82c8 | |
KIU Shueng Chuan | 89f6c7da81 | |
KIU Shueng Chuan | abeae0b7fa | |
KIU Shueng Chuan | 6bfb516768 | |
KIU Shueng Chuan | a1845cddbc | |
KIU Shueng Chuan | 6839ec937a | |
KIU Shueng Chuan | ad3e3a6b8b | |
KIU Shueng Chuan | 380ec2e0b2 | |
Ogi Moore | cb4af3ac97 | |
KIU Shueng Chuan | 98a020d1bb | |
KIU Shueng Chuan | afe47def28 | |
Ogi Moore | e2b0a5ffae | |
Ogi Moore | e892ad37aa | |
Ogi Moore | 7e8d34ecd8 | |
Martin Chase | 2fb04b754c | |
KIU Shueng Chuan | 6a2bfa5c84 | |
KIU Shueng Chuan | b3e8f332fb | |
KIU Shueng Chuan | 765f9648cd | |
Ogi Moore | 6f0ffcbf8f | |
Ogi Moore | d974544053 | |
Ogi Moore | f225724f26 | |
Ogi Moore | 3c352dd1a9 | |
Ogi Moore | 2f1f297f39 | |
Ogi Moore | cc081af528 | |
Ogi Moore | 195a1a6fa3 | |
Ogi Moore | d455da9aec | |
Ogi Moore | 29bdf9949e | |
Nils Nemitz | 7d41e8a878 | |
Nils Nemitz | f002d70adc | |
Ogi Moore | 823988d51c | |
KIU Shueng Chuan | 1715d5a6fa | |
Nils Nemitz | 61f067bf7c | |
Ogi Moore | 3a3d05b16f | |
bsobhani | d863907163 | |
shikishima-TasakiLab | f13002b251 | |
KIU Shueng Chuan | ecc3563f6d | |
KIU Shueng Chuan | 8997cfa07c | |
KIU Shueng Chuan | c8e6920443 | |
Ogi Moore | e206ea5ae9 | |
Ogi Moore | 145ab2e9e3 | |
Ogi Moore | 5c54577c26 | |
Ogi Moore | 55e42aabfd | |
Ogi Moore | 649757eb31 | |
Ogi Moore | 31b4210f4e | |
Ogi Moore | bd11e5e401 | |
njessurun | c0ebf8a432 | |
Ogi Moore | c0b9f3e26e | |
Ogi Moore | 45cf6fa232 | |
Nils Nemitz | 39f705fd7f | |
Ogi Moore | 0da1958ff1 | |
3DAlgoLab | 28b1499f09 | |
Ogi Moore | 4264219144 | |
Ogi Moore | f01f3d473f | |
Ogi Moore | c7675ca8bb | |
Ogi Moore | d72be799d7 | |
Ogi Moore | d859cec95a | |
Ogi Moore | ceeacf4e5f | |
Ogi Moore | ab792a73a6 | |
Ogi Moore | 4224fe5664 | |
Ogi Moore | 1d55fde41a | |
Ogi Moore | d519630d14 | |
Ogi Moore | 648b8c7df4 | |
Ogi Moore | ee951331be | |
Ogi Moore | e7a30b9324 | |
Ogi Moore | 0160de22fb | |
Ogi Moore | a6971c768d | |
KIU Shueng Chuan | cabcf6cd29 | |
KIU Shueng Chuan | 8f4104a9ab | |
Ogi Moore | 270fc59cab | |
Ogi Moore | 78e678c1b9 | |
Luke Campagnola | e29f86578f | |
Martin Chase | 1735effea7 | |
Ogi Moore | 62165bbe45 | |
Ogi Moore | c6f1d2df94 | |
Ogi Moore | 07ba7be2a4 | |
Ogi Moore | d62d3b182f | |
Ogi Moore | aaae0b9e1c | |
Ogi Moore | 7860d641ab | |
Ogi Moore | f08d239578 | |
KIU Shueng Chuan | d220e8a6e9 | |
Ogi Moore | 7bc5f215c0 | |
Fernando Bordignon | a4de137535 | |
Ogi Moore | ed1653f3c5 | |
Wyatt Ubellacker | 7e5c90a7a4 | |
KIU Shueng Chuan | 7499c3b375 | |
KIU Shueng Chuan | 9426ef0391 | |
KIU Shueng Chuan | 03ddf92839 | |
Martin Chase | ac84f45787 | |
Kenneth Lyons | bcb6a5d66a | |
Martin Chase | a91953e93d | |
Mark Schloeman | 1c1212e5e9 | |
Mark Schloeman | c9ea25eed6 | |
Ogi Moore | 9566e2ba36 | |
Ogi Moore | 9624b2a049 | |
Ogi Moore | d3e0d041de | |
Martin Chase | 253055003b | |
KIU Shueng Chuan | dfddb39ce0 | |
KIU Shueng Chuan | 0fb0209e43 | |
KIU Shueng Chuan | 0754602c3c | |
KIU Shueng Chuan | 21296cd4f3 | |
KIU Shueng Chuan | 61badc88b3 | |
Ogi Moore | 1905f83047 | |
KIU Shueng Chuan | 381147450d | |
KIU Shueng Chuan | 210203d628 | |
Ogi Moore | eed220e874 | |
KIU Shueng Chuan | 3eb69e6d9b | |
KIU Shueng Chuan | 2dad9862cb | |
KIU Shueng Chuan | 3b32e27083 | |
KIU Shueng Chuan | 70c123a95c | |
KIU Shueng Chuan | 60661f586f | |
Ogi Moore | 96e83b7d43 | |
KIU Shueng Chuan | 0503f96121 | |
KIU Shueng Chuan | 09ce81655d | |
Nils Nemitz | bfc63bb730 | |
Martin Chase | 2fb7cdafbd | |
Martin Chase | fa1be1e5bf | |
Kenneth Lyons | cafe079910 | |
Ogi Moore | 7dd033c03b | |
shahmustafa54 | e1b1410260 | |
Ogi Moore | cf70cf4395 | |
Ogi Moore | 2fd5337215 | |
Ogi Moore | cb48ce0548 | |
Ogi Moore | 6163a60198 | |
Martin Chase | 85e2574230 | |
Fernando Bordignon | 7dc4823cc6 | |
pijyoi | 4d6a8e4998 | |
Nils Nemitz | a7bc2b9a63 | |
Martin Chase | 4ee1fe4388 | |
Ogi Moore | a534132c62 | |
Ogi Moore | fe66f3fd12 | |
KIU Shueng Chuan | 306e9c2498 | |
Ogi Moore | 2cbc86bee1 | |
Ogi Moore | 2d8d1d6d32 | |
Ogi Moore | 7c48233bfa | |
Ogi Moore | 38dccf8642 | |
Ogi Moore | 00a0ddb0d4 | |
Ogi Moore | 4d388ee633 | |
Ogi Moore | cf95964678 | |
Ogi Moore | d9a3774f6c | |
Ogi Moore | 6a708dc203 | |
Ogi Moore | 03ea368454 | |
Ogi Moore | 329423f525 | |
Ogi Moore | e1415cb3a8 | |
Ogi Moore | 6a386e723b | |
Ogi Moore | 3ea92736b8 | |
Ogi Moore | 314121192a | |
Ogi Moore | b0769f4be9 | |
Ogi Moore | 1138c67d45 | |
KIU Shueng Chuan | e3372fddc5 | |
Ogi Moore | c4a1cf11a1 | |
Ogi Moore | 85c726e49a | |
Ogi Moore | b01e0e0895 | |
Ogi Moore | f60fa3b534 | |
Ogi Moore | f8cefa6284 | |
Ogi Moore | b0a3849960 | |
KIU Shueng Chuan | fbc86cd2c5 | |
Ogi Moore | a9161e0794 | |
Ogi Moore | 9a77c223f0 | |
KIU Shueng Chuan | 51871cd01e | |
KIU Shueng Chuan | f4ed46773f | |
Ogi Moore | ec4d613037 | |
Martin Chase | d531a808e1 | |
KIU Shueng Chuan | 0781f9392b | |
Nils Nemitz | a6f9a2be12 | |
Ogi Moore | c62e51f4d8 | |
Nils Nemitz | 515ae5f6e3 | |
Ogi Moore | 2b2b8d34c2 | |
Ogi Moore | 1326ebe2f4 | |
Ogi Moore | 41eadd678b | |
Ogi Moore | 7740de4a26 | |
Ogi Moore | a231c9ffb0 | |
Ogi Moore | 342fbb053f | |
Ogi Moore | 52de9554bb | |
Ogi Moore | e735d2d9b8 | |
Ogi Moore | 435c54d20b | |
Ogi Moore | 5c67f03b0e | |
Ogi Moore | 314e4eb480 | |
Martin Chase | 6fed6d42b3 | |
Martin Chase | a465f93d9b | |
Ogi Moore | dfa225f56f | |
Christopher Mullins | 10c1e83cd7 | |
Ogi Moore | 6f74d56266 | |
Ogi Moore | d2e1f99b5e | |
KIU Shueng Chuan | 8cb53d321b | |
Ogi Moore | fe6ad52262 | |
Ogi Moore | 5a08650853 | |
Ogi Moore | bb90ef1ec9 | |
Ogi Moore | a3443571f2 | |
KIU Shueng Chuan | 2aed5c36d5 | |
KIU Shueng Chuan | 64702981d4 | |
KIU Shueng Chuan | 4699bbad6b | |
Nathan Jessurun | e890d832e0 | |
Ogi Moore | 10d924818c | |
KIU Shueng Chuan | 3be8cafff4 | |
Martin Chase | 0f94c17d86 |
46
.flake8
46
.flake8
|
@ -3,47 +3,5 @@ exclude = .git,.tox,__pycache__,doc,old,build,dist
|
|||
show_source = True
|
||||
statistics = True
|
||||
verbose = 2
|
||||
select =
|
||||
E101,
|
||||
E112,
|
||||
E122,
|
||||
E125,
|
||||
E133,
|
||||
E223,
|
||||
E224,
|
||||
E242,
|
||||
E273,
|
||||
E274,
|
||||
E901,
|
||||
E902,
|
||||
W191,
|
||||
W601,
|
||||
W602,
|
||||
W603,
|
||||
W604,
|
||||
E124,
|
||||
E231,
|
||||
E211,
|
||||
E261,
|
||||
E271,
|
||||
E272,
|
||||
E304,
|
||||
F401,
|
||||
F402,
|
||||
F403,
|
||||
F404,
|
||||
E501,
|
||||
E502,
|
||||
E702,
|
||||
E703,
|
||||
E711,
|
||||
E712,
|
||||
E721,
|
||||
F811,
|
||||
F812,
|
||||
F821,
|
||||
F822,
|
||||
F823,
|
||||
F831,
|
||||
F841,
|
||||
W292
|
||||
max-line-length = 88
|
||||
extend-ignore = E203, W503
|
|
@ -16,35 +16,30 @@ jobs:
|
|||
- python-version: "3.7"
|
||||
qt-lib: "pyqt"
|
||||
qt-version: "PyQt5~=5.12.0"
|
||||
numpy-version: "~=1.17.0"
|
||||
numpy-version: "~=1.18.0"
|
||||
- python-version: "3.7"
|
||||
qt-lib: "pyside"
|
||||
qt-version: "PySide2~=5.12.0"
|
||||
numpy-version: "~=1.17.0"
|
||||
numpy-version: "~=1.18.0"
|
||||
- python-version: "3.8"
|
||||
qt-lib: "pyqt"
|
||||
qt-version: "PyQt5~=5.15.0"
|
||||
numpy-version: "~=1.19.0"
|
||||
numpy-version: "~=1.21.0"
|
||||
- python-version: "3.8"
|
||||
qt-lib: "pyside"
|
||||
qt-version: "PySide2~=5.15.0"
|
||||
numpy-version: "~=1.19.0"
|
||||
numpy-version: "~=1.21.0"
|
||||
- python-version: "3.9"
|
||||
qt-lib: "pyqt"
|
||||
qt-version: "PyQt6"
|
||||
numpy-version: "~=1.19.0"
|
||||
numpy-version: "~=1.21.0"
|
||||
- python-version: "3.9"
|
||||
qt-lib: "pyside"
|
||||
qt-version: "PySide6"
|
||||
numpy-version: "~=1.19.0"
|
||||
numpy-version: "~=1.21.0"
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Checkout test-data
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: pyqtgraph/test-data
|
||||
path: .pyqtgraph/test-data
|
||||
- name: Setup Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
|
@ -69,10 +64,10 @@ jobs:
|
|||
shell: cmd
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
pip install --upgrade pip
|
||||
pip install ${{ matrix.qt-version }} numpy${{ matrix.numpy-version }} scipy pyopengl h5py matplotlib
|
||||
pip install .
|
||||
pip install pytest pytest-cov pytest-xdist coverage
|
||||
python -m pip install --upgrade pip setuptools wheel
|
||||
python -m pip install ${{ matrix.qt-version }} numpy${{ matrix.numpy-version }} scipy pyopengl h5py matplotlib numba
|
||||
python -m pip install --use-feature=in-tree-build .
|
||||
python -m pip install pytest
|
||||
- name: "Install Linux VirtualDisplay"
|
||||
if: runner.os == 'Linux'
|
||||
run: |
|
||||
|
@ -80,14 +75,14 @@ jobs:
|
|||
sudo apt-get install -y libxkbcommon-x11-0 x11-utils
|
||||
sudo apt-get install --no-install-recommends -y libyaml-dev libegl1-mesa libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 libxcb-xinerama0
|
||||
sudo apt-get install -y libopengl0
|
||||
pip install pytest-xvfb
|
||||
python -m pip install pytest-xvfb
|
||||
- name: 'Debug Info'
|
||||
run: |
|
||||
echo python location: `which python`
|
||||
echo python version: `python --version`
|
||||
echo pytest location: `which pytest`
|
||||
echo installed packages
|
||||
pip list
|
||||
python -m pip list
|
||||
echo pyqtgraph system info
|
||||
python -c "import pyqtgraph as pg; pg.systemInfo()"
|
||||
shell: bash
|
||||
|
@ -106,10 +101,8 @@ jobs:
|
|||
- name: Run Tests
|
||||
run: |
|
||||
mkdir $SCREENSHOT_DIR
|
||||
pytest . -v \
|
||||
-n auto \
|
||||
--junitxml pytest.xml \
|
||||
--cov pyqtgraph --cov-report=xml --cov-report=html
|
||||
pytest tests -v
|
||||
pytest examples -v
|
||||
shell: bash
|
||||
- name: Upload Screenshots
|
||||
uses: actions/upload-artifact@v2
|
||||
|
@ -120,29 +113,9 @@ jobs:
|
|||
env:
|
||||
SCREENSHOT_DIR: ./screenshots
|
||||
|
||||
build-docs:
|
||||
name: build docs
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Setup Python 3.9
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.9'
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
cd doc
|
||||
python -m pip install -r requirements.txt
|
||||
- name: Build Documentation
|
||||
run: |
|
||||
cd doc
|
||||
make html SPHINXOPTS='-W --keep-going -v'
|
||||
|
||||
build-wheel:
|
||||
name: build wheel
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Setup Python 3.9
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
# This workflows will upload a Python Package using setup.py/twine when a release is created
|
||||
# For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries
|
||||
|
||||
name: Upload Python Package
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [created]
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.x'
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install wheel twine setuptools
|
||||
- name: Build and publish
|
||||
env:
|
||||
TWINE_USERNAME: __token__
|
||||
# The PYPI_PASSWORD must be a pypi token with the "pypi-" prefix with sufficient permissions to upload this package
|
||||
# https://pypi.org/help/#apitoken
|
||||
TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
|
||||
run: |
|
||||
python setup.py sdist bdist_wheel
|
||||
twine upload dist/*
|
|
@ -108,3 +108,4 @@ rtr.cvs
|
|||
.tags*
|
||||
|
||||
.asv/
|
||||
asv.conf.json
|
||||
|
|
139
CHANGELOG
139
CHANGELOG
|
@ -1,3 +1,142 @@
|
|||
pyqtgraph-0.12.3
|
||||
|
||||
Highlights:
|
||||
- PlotCurveItem render speed is now substantially faster
|
||||
- #1868/#1873 Example app now has filter text input
|
||||
- #1910 PlotSpeedTest now has parameter tree control panel
|
||||
|
||||
New Features:
|
||||
- #1844 More parameter item types (File, Calendar, ProgressBar, Font, Pen, Slider)
|
||||
- #1865 Matplotlib colormaps viridis, plasma, magma and inferno are now included in pyqtgraph
|
||||
- #1911 Extend Colormap with HSL cycles and subset generation
|
||||
- #1932 Make anti-aliasing optional for paintGL in PlotCurveItem
|
||||
- #1944 Expand use of QColor functions/methods, including setNamedColor
|
||||
- #1952 Add checklist parameter item
|
||||
- #1998 ThreadTrace can now save to a file
|
||||
|
||||
Performance Enhancement:
|
||||
- #1927 Reduce ColorMap inefficiencies
|
||||
- #1956 use QByteArray as backing store in arrayToQPath
|
||||
- #1965 perform arrayToQPath in chunks
|
||||
|
||||
Bug Fixes:
|
||||
- #1845 Fix zoom behavior with showGrid by separating mouse events stolen by AxisItem
|
||||
- #1860 RemoteGraphicsView and RemoteSpeedTest now work under windows venv environments
|
||||
- #1865 Fixed matplotlib colormap importer code
|
||||
- #1869 Fix ColorBarItem tick position on export
|
||||
- #1871 Allow adding items to GLViewWidget before showing plot
|
||||
- #1875 Fix calls in mouse methods in GLViewWidgets due to missing event.localPos() in PyQt6
|
||||
- #1876 Fix for improper placement of ROI handle positions in some cases
|
||||
- #1889/#2003 Fix call to drawText in GLTextItem and GLGradientLegendItem on Python 3.10
|
||||
- #1897/#1902 Re-enable "experimental" feature with fix for PlotCurveItem with OpenGL on Windows
|
||||
- #1907 Fix GLVolumeItem example for arm64 platforms
|
||||
- #1909 Check if AxisItem.label is None before and exit early in resizeEvent
|
||||
- #1920 arrayToQPath can handle empty paths
|
||||
- #1936 QPolygonF creation can now handle empty arrays
|
||||
- #1968 Fix output of clip_array in colormap.modulatedBarData not being assigned
|
||||
- #1973 Fix PlotItem.updateDecimate unhiding intentionally hidden curves
|
||||
- #1974 Fix ImageView levelMode with levelMode == 'rgba'
|
||||
- #1987 Fix HistogramLUTItem itemChanged with use of autoLevel
|
||||
- #2009 Fix ROI curves hidding in ImageView
|
||||
|
||||
API/Behavior Changes:
|
||||
- #1992 Reverted to traditional log10 mode for PlotDataItem
|
||||
- #1840 Allow border=False in GraphicsLayout
|
||||
- #1846 Reduced pollution to pg.namespace
|
||||
- #1853 ColorMap.getColors and getStops behavior changes
|
||||
- #1864 Draw GradientLegend in ViewBox coordinates
|
||||
- #1885 Raise TypeError instead of general Exception if functions.eq is unable to determine equality
|
||||
- #1903 Cleanup GLViewWidget
|
||||
- #1908 More readable parameters for ColorBarItem
|
||||
- #1914 Emit deprecation warning for use of pyqtgraph.ptime
|
||||
- #1928 Restore previous signature of TargetItem.setPos
|
||||
- #1940 fix log mode by reverting to previous formulation
|
||||
- #1954 Deprecate use of values opt for list parameter type
|
||||
- #1995 ColorButton now takes optional padding argument instead of hardcoded value of 6
|
||||
|
||||
Other:
|
||||
- #1862/#1901 MetaArray now under deprecation warning, to be removed in a future version
|
||||
- #1892 Add GLPainterItem Example
|
||||
- #1844 Debugged elusive intermitted CI segfault
|
||||
- #1870/#1891 Updated README.md
|
||||
- #1895 Update CONTRIBUTING.md
|
||||
- #1913 Bump sphinx and theme versions
|
||||
- #1919 Re-organize paramtypes
|
||||
- #1935 Remove some unused imports
|
||||
- #1939 Remove usage of python2_3.py
|
||||
- #1941 Remove str casting of QTextEdit.toPlainText output
|
||||
- #1942 Add EOF newline to files missing it
|
||||
- #1943 Remove python2 code paths
|
||||
- #1951 Fix typos in docs
|
||||
- #1957 Bump minimum numpy version to 1.18
|
||||
- #1968 Fix ImageView calling deprecated QGraphicsItem.scale()
|
||||
- #1985 delegate float LUTs to makeARGB with warning
|
||||
- #2014 Replace couple absolute imports with relative imports
|
||||
|
||||
pyqtgraph-0.12.2
|
||||
|
||||
Highlights
|
||||
- Qt6 6.0 support has been removed in favor of Qt6 6.1+
|
||||
- More numba utilization, specifically for makeARGB
|
||||
- Substantial ImageItem performance improvements have been made thanks to @pijyoi and @outofculture
|
||||
- Significant performance improvements made to ScatterPlotItem and LinePlots
|
||||
- More ColorMap features/support (more are coming!)
|
||||
|
||||
New Features
|
||||
- #1318 Added TargetItem
|
||||
- #1707 Added Qt 6.1 support
|
||||
- #1729 Allow gradient position to be configured on a histogram
|
||||
- #1742 Better support for plotting with gradients
|
||||
- #1776 Add GLTextItem
|
||||
- #1797 Add ColorMap linearization (using CIELab calculations), colorDistance functionality
|
||||
- #1865 Include viridis, magma, plasma, inferno, and cividis colormaps
|
||||
- #1868/#1873 Example app now has a filter text box to search for relevant examples
|
||||
|
||||
Performance enhancement:
|
||||
- #1738, #1695, 1786, #1768, 1794 - ImageItem/makeARGB performance improvements
|
||||
- #1724 Use math module for scalar values math instead of numpy functions
|
||||
- #1796 Greatly speed up line plots with use-case of connect='all'
|
||||
- #1817 Speed up some cases of connect='finite' (few discontinuities)
|
||||
- #1829 Use QPainter.drawPixmapFragments for ScatterPlotItem
|
||||
|
||||
Bug Fixes:
|
||||
- #1700 Fixed ROI getArrayRegion
|
||||
- #1748 Fixed bug when plotting boolean arrays in PlotDataItem
|
||||
- #1791 Callable LUTs being used on the ImageItem substrates
|
||||
- #1783 Fix memory leak in GLMeshItem
|
||||
- #1802 Updated cx_freeze example and added workaround for template files
|
||||
- #1804 Fix mouseClick handling for Qt6 on ROIs
|
||||
- #1799 Force cameraPosition() to return a Vector in GLViewWidget
|
||||
- #1809 Sanitize ShowGrid Alpha Input PlotItem
|
||||
- #1816 Fix bug with Parameter value failing with numpy array-like values
|
||||
- #1827 Fix BusyCursor to use internal stack provided by setOverrideCursor/restoreOverrideCursor
|
||||
- #1833 Fix ScatterPlot render issues for PyQt6 6.1.0
|
||||
- #1843 Fix zoom only applied to y-axis with show grid
|
||||
- #1860 Fix pyqtgraph multiprocessing on Windows inside a venv environment
|
||||
- #1869 Fix color bar ticks not being drawn correctly during export
|
||||
- #1865 Fix matplotlib colormap import code
|
||||
- #1876 Fix LineROI handle positions being way off-base in some circumstances
|
||||
- #1871 Allow adding items to GLViewWidget before calling GLViewWidget.show()
|
||||
- #1864 Draw GradientLegend in ViewBox coordinate system with correct orientation
|
||||
- #1875 Fixed mouse events in GLViewWidget for PyQt6 bindings
|
||||
|
||||
API/Behavior Changes:
|
||||
- #519 Expose clickable property in PlotDataItem
|
||||
- #1772 Keep ColorMap values for RGBA as uint8
|
||||
- #1736 RemoteGraphicsView is now hidpi aware
|
||||
- #1779 Have SpinBox use fallback minStep in dec mode
|
||||
- #1706 Colors defined with hex string values must start with a #
|
||||
- #1819 Added method to disable autoscaling for HistogramLUTItem
|
||||
- #1638 Expose number of subsamples in ImageItem auto-level determination
|
||||
- #1824 Remove little-endian assumption for image export
|
||||
|
||||
Other
|
||||
- #1807 Merge pyqtgraph/test-data repo into main repo, move test files to tests directory
|
||||
- #1862 Scheduled deprecation for MetaArray module
|
||||
- #1846 Cleaned up pg namespace
|
||||
|
||||
|
||||
|
||||
pyqtgraph-0.12.1
|
||||
|
||||
New Features
|
||||
|
|
|
@ -0,0 +1,135 @@
|
|||
# Code of Conduct
|
||||
|
||||
Diversity is one of our huge strengths, but it can also lead to communication issues. To support a welcoming environment
|
||||
for all, regardless of individual differences, we have a few ground rules that we ask people to adhere to when they
|
||||
participate in this community. These rules apply equally to founders, organizers, contributors, users and affiliates —
|
||||
in short, to all participants.
|
||||
|
||||
This isn’t an exhaustive list of things that you must do, or can’t do. Rather, take it in the spirit in which it’s
|
||||
intended. It’s a guide to make it easier to enrich all of us and the technical communities in which we participate, and
|
||||
which we represent.
|
||||
|
||||
## Our Pledge
|
||||
|
||||
We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for
|
||||
everyone, regardless of background or identity. This includes, but is not limited to, members of any race, ethnicity,
|
||||
culture, national origin, color, immigration status, social and economic class, educational level, sex, sexual
|
||||
orientation, gender identity and expression, age, size, family status, political belief, religion, level of experience,
|
||||
and mental or physical ability.
|
||||
|
||||
We pledge to be respectful. Not all of us will agree all the time, but disagreement is no excuse for poor behavior and
|
||||
poor manners. We might all experience some frustration now and then, but we cannot allow that frustration to turn into a
|
||||
personal attack. It’s important to remember that a community where people feel uncomfortable or threatened is not a
|
||||
productive one. Members of the PyQtGraph community should be respectful when dealing with each other as well as with
|
||||
people outside the community.
|
||||
|
||||
We pledge to be welcoming, supportive, kind and professional. We pledge to act and interact in ways that contribute to
|
||||
an open, diverse, inclusive, and healthy community. We pledge not insult or put down other participants, individually or
|
||||
as a group. Harassment and other exclusionary behavior aren’t acceptable.
|
||||
|
||||
## Examples
|
||||
|
||||
Examples of behavior that contributes to a positive environment for our community include:
|
||||
|
||||
* Demonstrating empathy and kindness toward other people
|
||||
* Being respectful of differing opinions, viewpoints, and experiences
|
||||
* Giving and gracefully accepting constructive feedback
|
||||
* Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience
|
||||
* Focusing on what is best not just for us as individuals, but for the overall community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
* Violent threats or language directed against another person.
|
||||
* Discriminatory jokes and language.
|
||||
* Posting sexually explicit or violent material.
|
||||
* Posting (or threatening to post) other people’s personally identifying information ("doxing").
|
||||
* Personal insults, especially those using racist or sexist terms.
|
||||
* Unwelcome sexual attention.
|
||||
* Repeated harassment of others. In general, if someone asks you to stop, then stop.
|
||||
* Advocating for, or encouraging, any of the above behavior.
|
||||
|
||||
## Where does this code of conduct apply
|
||||
|
||||
This Code of Conduct applies within all online community spaces, official organized meetups, and when an individual is
|
||||
officially representing the community in public spaces, online or offline.
|
||||
|
||||
## What to do in case of violations
|
||||
|
||||
If you believe someone is violating the code of conduct, we ask that you report it by contacting any of the following
|
||||
individuals, being sure to include mention of your message being about a "PyQtGraph conduct violation":
|
||||
|
||||
* Ognyan Moore @j9ac9k <ognyan.moore@gmail.com>
|
||||
* Martin Chase @outofculture <outofculture@gmail.com>
|
||||
* Nathan Jessurun @ntjess <ntjessu@gmail.com>
|
||||
|
||||
All complaints will be reviewed and investigated promptly and fairly. All reports will be kept confidential. In some
|
||||
cases, we may determine that a public statement will need to be made. If that’s the case, the identities of all victims
|
||||
and reporters will remain confidential unless those individuals instruct us otherwise.
|
||||
|
||||
If you believe anyone is in physical danger, please notify appropriate law enforcement first. If you are unsure which
|
||||
law enforcement agency is appropriate, please include this in your report and we will attempt to notify them.
|
||||
|
||||
### What to include in the report
|
||||
|
||||
* Your contact information (so we can get in touch with you if we need to follow up)
|
||||
* Names of any individuals involved (real names, nicknames, or pseudonyms). If there were other witnesses besides you,
|
||||
please try to include them as well.
|
||||
* When and where the incident occurred. Please be as specific as possible.
|
||||
* Your account of what occurred. If there is a publicly available record (e.g., a mailing list archive or a public chat
|
||||
logger) please include a link. Our Slack is currently on a free plan that does not retain more than 10,000 messages of
|
||||
history, if an incident occurred on Slack please take a screenshot so it is not lost.
|
||||
* Any extra context you believe existed for the incident.
|
||||
* Whether you believe this incident is ongoing.
|
||||
* Any other information you believe we should have.
|
||||
|
||||
## Enforcement Responsibilities
|
||||
|
||||
Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take
|
||||
appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive,
|
||||
or harmful.
|
||||
|
||||
Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits,
|
||||
issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for
|
||||
moderation decisions when appropriate. This removal should be as transparent as possible, with information available in
|
||||
the same scope of context as the original moderated content, linking to an incident report, the acting moderators,
|
||||
and/or this document as appropriate.
|
||||
|
||||
All community leaders are obligated to respect the privacy and security of the reporter of any incident.
|
||||
|
||||
Upon receiving a report of misconduct, the staff must review the incident and determine the following:
|
||||
|
||||
* What happened.
|
||||
* Whether this event constitutes a code of conduct violation.
|
||||
* Who the bad actor(s) was.
|
||||
* Whether this is an ongoing situation.
|
||||
* If any previous disiplinary measures are relevent and in effect.
|
||||
* If there is a threat to anyone’s physical safety.
|
||||
* What impact this has had on the community.
|
||||
|
||||
After the conduct committee has a complete account of the events they need to make a decision as to how to resolve the
|
||||
case. Resolutions might include:
|
||||
|
||||
* Nothing (if we determine no violation occurred).
|
||||
* A private reprimand from the staff to the individual(s) involved, providing clarity around the nature of the
|
||||
violation, and an explanation of why the behavior was inappropriate.
|
||||
* A request for a public or private apology.
|
||||
* A public reprimand.
|
||||
* An imposed vacation (for example, asking someone to "take a week off" from Slack).
|
||||
* A permanent or temporary ban from some or all PyQtGraph spaces (Slack, github, mailing list or events) or from
|
||||
communicating, in public or in private, with some are all of the members of our community.
|
||||
* Contacting law enforcement.
|
||||
|
||||
A response must be sent within one week to the person who filed the report with either a resolution or an explanation of
|
||||
why the situation is not yet resolved.
|
||||
|
||||
A Code of Conduct transparency report will then be published
|
||||
to [the PyQtGraph users' list](https://groups.google.com/g/pyqtgraph) with anonymized information about any violations
|
||||
that might have occurred. This report should be handled with care not to divulge personally identifying information
|
||||
about victims, reporters, and violators, and should serve as a means to ensure that members will be comfortable
|
||||
reporting violations and that our community will be kept accountable for supporting and encouraging safe spaces.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from
|
||||
the [Contributor Covenant, version 2.0](https://www.contributor-covenant.org/version/2/0/code_of_conduct.html) and
|
||||
the [PuPPy Code of Conduct](https://www.pspython.com/pages/code-of-conduct/).
|
102
CONTRIBUTING.md
102
CONTRIBUTING.md
|
@ -1,72 +1,92 @@
|
|||
# Contributing to PyQtGraph
|
||||
|
||||
Contributions to pyqtgraph are welcome!
|
||||
Contributions to pyqtgraph are welcome! Be kind and respectful! See [our Code of Conduct](CODE_OF_CONDUCT.md) for details.
|
||||
|
||||
Please use the following guidelines when preparing changes:
|
||||
|
||||
## Submitting Code Changes
|
||||
## Development Environment Creation
|
||||
|
||||
* The preferred method for submitting changes is by github pull request against the "master" branch.
|
||||
* Pull requests should include only a focused and related set of changes. Mixed features and unrelated changes may be rejected.
|
||||
* For major changes, it is recommended to discuss your plans on the mailing list or in a github issue before putting in too much effort.
|
||||
* The following deprecations are being considered by the maintainers
|
||||
* `pyqtgraph.opengl` may be deprecated and replaced with `VisPy` functionality
|
||||
* After v0.11, pyqtgraph will adopt [NEP-29](https://numpy.org/neps/nep-0029-deprecation_policy.html) which will effectively mean that python2 support will be deprecated
|
||||
* Qt4 will be deprecated shortly, as well as Qt5<5.9 (and potentially <5.12)
|
||||
First thing to do is fork the repository, and clone your own fork to your local computer.
|
||||
|
||||
```bash
|
||||
git clone https://github.com/<username>/pyqtgraph.git
|
||||
cd pyqtgraph
|
||||
```
|
||||
|
||||
While there is nothing preventing users from using `conda` environments, as a general principle, we recommend using the `venv` module for creating an otherwise empty virtual environment. Furthermore, at this time, WSL is not supported (it can likely be made to work, but you're on your own if you go down this route).
|
||||
|
||||
```bash
|
||||
python3.9 -m venv .venv
|
||||
source .venv/bin/activate
|
||||
# on windows this would be .venv/Scripts/activate
|
||||
python -m pip install --upgrade wheel setuptools pip
|
||||
python -m pip install numpy scipy pyside6 -e .
|
||||
```
|
||||
|
||||
Before making changes to the code-base, create a different branch with a name that should be unique (this makes it easier for maintainers to examine the proposed changes locally).
|
||||
|
||||
```bash
|
||||
git switch -c my-new-feature
|
||||
```
|
||||
|
||||
When you're ready to submit the pull request, do so via the github, and the target of the pull request should be the `master` branch in the pyqtgraph repo.
|
||||
|
||||
Pull requests should include only a focused and related set of changes. Mixed features and unrelated changes may be rejected.
|
||||
|
||||
For major changes, it is recommended to discuss your plans on the mailing list or in a github issue/discussion before putting in too much effort.
|
||||
|
||||
PyQtGraph has adopted [NEP-29](https://numpy.org/neps/nep-0029-deprecation_policy.html) which governs the timeline for phasing out support for numpy and python versions.
|
||||
|
||||
## Documentation
|
||||
|
||||
* Writing proper documentation and unit tests is highly encouraged. PyQtGraph uses pytest style testing, so tests should usually be included in a tests/ directory adjacent to the relevant code.
|
||||
* Documentation is generated with sphinx; please check that docstring changes compile correctly
|
||||
* Writing proper documentation and unit tests is highly encouraged. PyQtGraph uses [`pytest`](https://docs.pytest.org/) for testing.
|
||||
* Documentation is generated with sphinx, and usage of [numpy-docstyle](https://numpydoc.readthedocs.io/en/latest/format.html) is encouraged (many places in the library do not use this docstring style at present, it's a gradual process to migrate).
|
||||
* The docs built for this PR can be previewed by clicking on the "Details" link for the read-the-docs entry in the checks section of the PR conversation page.
|
||||
|
||||
## Style guidelines
|
||||
|
||||
### Rules
|
||||
### Formatting ~~Rules~~ Suggestions
|
||||
|
||||
* PyQtGraph prefers PEP8 for most style issues, but this is not enforced rigorously as long as the code is clean and readable.
|
||||
* Use `python setup.py style` to see whether your code follows the mandatory style guidelines checked by flake8.
|
||||
* Exception 1: All variable names should use camelCase rather than underscore_separation. This is done for consistency with Qt
|
||||
* Exception 2: Function docstrings use ReStructuredText tables for describing arguments:
|
||||
|
||||
```text
|
||||
============== ========================================================
|
||||
**Arguments:**
|
||||
argName1 (type) Description of argument
|
||||
argName2 (type) Description of argument. Longer descriptions must
|
||||
be wrapped within the column guidelines defined by the
|
||||
"====" header and footer.
|
||||
============== ========================================================
|
||||
```
|
||||
|
||||
QObject subclasses that implement new signals should also describe
|
||||
these in a similar table.
|
||||
* Variable and Function/Methods that are intended to be part of the public API should be camelCase.
|
||||
* "Private" methods/variables should have a leading underscore (`_`) before the name.
|
||||
|
||||
### Pre-Commit
|
||||
|
||||
PyQtGraph developers are highly encouraged to (but not required) to use [`pre-commit`](https://pre-commit.com/). `pre-commit` does a number of checks when attempting to commit the code to ensure it conforms to various standards, such as `flake8`, utf-8 encoding pragma, line-ending fixers, and so on. If any of the checks fail, the commit will be rejected, and you will have the opportunity to make the necessary fixes before adding and committing a file again. This ensures that every commit made conforms to (most) of the styling standards that the library enforces; and you will most likely pass the code style checks by the CI.
|
||||
PyQtGraph developers are highly encouraged to (but not required) to use [`pre-commit`](https://pre-commit.com/). `pre-commit` does a number of checks when attempting to commit the code to being committed, such as ensuring no large files are accidentally added, address mixed-line-endings types and so on. Check the [pre-commit documentation](https://pre-commit.com) on how to setup.
|
||||
|
||||
To make use of `pre-commit`, have it available in your `$PATH` and run `pre-commit install` from the root directory of PyQtGraph.
|
||||
## Testing
|
||||
|
||||
## Testing Setting up a test environment
|
||||
|
||||
### Dependencies
|
||||
### Basic Setup
|
||||
|
||||
* tox
|
||||
* tox-conda
|
||||
* pytest
|
||||
* pytest-cov
|
||||
* pytest-xdist
|
||||
* Optional: pytest-xvfb
|
||||
* Optional: pytest-xvfb (used on linux with headless displays)
|
||||
|
||||
If you have `pytest<5` (used in python2), you may also want to install `pytest-faulthandler==1.6` plugin to output extra debugging information in case of test failures. This isn't necessary with `pytest>=5`
|
||||
To run the test suite, after installing the above dependencies run
|
||||
|
||||
```bash
|
||||
python -m pytest examples tests
|
||||
```
|
||||
|
||||
### Tox
|
||||
|
||||
As PyQtGraph supports a wide array of Qt-bindings, and python versions, we make use of `tox` to test against most of the configurations in our test matrix. As some of the qt-bindings are only installable via `conda`, `conda` needs to be in your `PATH`, and we utilize the `tox-conda` plugin.
|
||||
As PyQtGraph supports a wide array of Qt-bindings, and python versions, we make use of `tox` to test against as many supported configurations as feasible. With tox installed, simply run `tox` and it will run through all the configurations. This should be done if there is uncertainty regarding changes working on specific combinations of PyQt bindings and/or python versions.
|
||||
|
||||
* Tests for a module should ideally cover all code in that module, i.e., statement coverage should be at 100%.
|
||||
* To measure the test coverage, un `pytest --cov -n 4` to run the test suite with coverage on 4 cores.
|
||||
### Continuous Integration
|
||||
|
||||
### Continous Integration
|
||||
For our Continuous Integration, we utilize Github Actions. Tested configurations are visible on [README](README.md).
|
||||
|
||||
For our Continuous Integration, we utilize Azure Pipelines. Tested configurations are visible on [README](README.md). More information on coverage and test failures can be found on the respective tabs of the [build results page](https://dev.azure.com/pyqtgraph/pyqtgraph/_build?definitionId=1)
|
||||
### Benchmarks
|
||||
|
||||
( *Still under development* ) To ensure this library is performant, we use [Air Speed Velocity (asv)](https://asv.readthedocs.io/en/stable/) to run benchmarks. For developing on core functions and classes, be aware of any impact your changes have on their speed. To configure and run asv:
|
||||
|
||||
```bash
|
||||
pip install asv
|
||||
python setup.py asv_config
|
||||
asv run
|
||||
```
|
||||
|
||||
( TODO publish results )
|
||||
|
|
65
README.md
65
README.md
|
@ -23,29 +23,47 @@ heavy leverage of numpy for number crunching, Qt's GraphicsView framework for
|
|||
Requirements
|
||||
------------
|
||||
|
||||
pyqtgraph has adopted [NEP 29](https://numpy.org/neps/nep-0029-deprecation_policy.html).
|
||||
PyQtGraph has adopted [NEP 29](https://numpy.org/neps/nep-0029-deprecation_policy.html).
|
||||
|
||||
This project supports:
|
||||
|
||||
* All minor versions of Python released 42 months prior to the project, and at minimum the two latest minor versions.
|
||||
* All minor versions of numpy released in the 24 months prior to the project, and at minimum the last three minor versions.
|
||||
* All minor versions of Qt 5 and Qt 6 currently supported by upstream Qt
|
||||
* All Qt5 versions from 5.12-5.15, and Qt6 6.1
|
||||
|
||||
Currently this means:
|
||||
|
||||
* Python 3.7+
|
||||
* Qt 5.12-6.0
|
||||
* Required
|
||||
* PyQt5, PyQt6, PySide2 or PySide6
|
||||
* `numpy` 1.17+
|
||||
* Optional
|
||||
* `scipy` for image processing
|
||||
* `pyopengl` for 3D graphics
|
||||
* `pyopengl` on macOS Big Sur only works with python 3.9.1+
|
||||
* `hdf5` for large hdf5 binary format support
|
||||
* `colorcet` for supplemental colormaps
|
||||
* [`cupy`](https://docs.cupy.dev/en/stable/install.html) for CUDA-enhanced image processing
|
||||
* On Windows, CUDA toolkit must be >= 11.1
|
||||
* Qt 5.12-5.15, 6.1
|
||||
* [PyQt5](https://www.riverbankcomputing.com/software/pyqt/),
|
||||
[PyQt6](https://www.riverbankcomputing.com/software/pyqt/),
|
||||
[PySide2](https://wiki.qt.io/Qt_for_Python), or
|
||||
[PySide6](https://wiki.qt.io/Qt_for_Python)
|
||||
* [`numpy`](https://github.com/numpy/numpy) 1.18+
|
||||
|
||||
### Optional added functionalities
|
||||
|
||||
Through 3rd part libraries, additional functionality may be added to PyQtGraph, see the table below for a summary.
|
||||
|
||||
| Library | Added functionality |
|
||||
|----------------|-|
|
||||
| [`scipy`] | <ul><li> Image processing through [`ndimage`]</li><li> Data array filtering through [`signal`] </li><ul> |
|
||||
| [`pyopengl`] | <ul><li> 3D graphics </li><li> Faster image processing </li><li>Note: on macOS Big Sur only works with python 3.9.1+</li></ul> |
|
||||
| [`h5py`] | <ul><li> Export in hdf5 format </li></ul> |
|
||||
| [`colorcet`] | <ul><li> Add a collection of perceptually uniform colormaps </li></ul> |
|
||||
| [`matplotlib`] | <ul><li> Export of PlotItem in matplotlib figure </li><li> Add matplotlib collection of colormaps </li></ul> |
|
||||
| [`cupy`] | <ul><li> CUDA-enhanced image processing </li><li> Note: On Windows, CUDA toolkit must be >= 11.1 </li></ul> |
|
||||
| [`numba`] | <ul><li> Faster image processing </li></ul> |
|
||||
|
||||
[`scipy`]: https://github.com/scipy/scipy
|
||||
[`ndimage`]: https://docs.scipy.org/doc/scipy/reference/ndimage.html
|
||||
[`signal`]: https://docs.scipy.org/doc/scipy/reference/signal.html
|
||||
[`pyopengl`]: https://github.com/mcfletch/pyopengl
|
||||
[`h5py`]: https://github.com/h5py/h5py
|
||||
[`colorcet`]: https://github.com/holoviz/colorcet
|
||||
[`matplotlib`]: https://github.com/matplotlib/matplotlib
|
||||
[`numba`]: https://github.com/numba/numba
|
||||
[`cupy`]: https://docs.cupy.dev/en/stable/install.html
|
||||
|
||||
Qt Bindings Test Matrix
|
||||
-----------------------
|
||||
|
@ -55,11 +73,14 @@ The following table represents the python environments we test in our CI system.
|
|||
| Qt-Bindings | Python 3.7 | Python 3.8 | Python 3.9 |
|
||||
| :------------- | :----------------: | :----------------: | :----------------: |
|
||||
| PySide2-5.12 | :white_check_mark: | :x: | :x: |
|
||||
| PyQt5-5.12 | :white_check_mark: | :x: | :x: |
|
||||
| PySide2-5.15 | :x: | :white_check_mark: | :x: |
|
||||
| PyQt5-5.15 | :x: | :white_check_mark: | :x: |
|
||||
| PySide6-6.0 | :x: | :x: | :white_check_mark: |
|
||||
| PyQt6-6.0 | :x: | :x: | :white_check_mark: |
|
||||
| PyQt5-5.12 | :white_check_mark: | | :x: |
|
||||
| PySide2-5.15 | | :white_check_mark: | |
|
||||
| PyQt5-5.15 | | :white_check_mark: | |
|
||||
| PySide6-6.1 | | | :white_check_mark: |
|
||||
| PyQt6-6.1 | | | :white_check_mark: |
|
||||
|
||||
* :x: - Not compatible
|
||||
* :white_check_mark: - Tested
|
||||
|
||||
Support
|
||||
-------
|
||||
|
@ -70,14 +91,14 @@ Support
|
|||
Installation Methods
|
||||
--------------------
|
||||
|
||||
* From PyPI:
|
||||
* From PyPI:
|
||||
* Last released version: `pip install pyqtgraph`
|
||||
* Latest development version: `pip install git+https://github.com/pyqtgraph/pyqtgraph@master`
|
||||
* From conda
|
||||
* Last released version: `conda install -c conda-forge pyqtgraph`
|
||||
* To install system-wide from source distribution: `python setup.py install`
|
||||
* Many linux package repositories have release versions.
|
||||
* To use with a specific project, simply copy the pyqtgraph subdirectory
|
||||
* To use with a specific project, simply copy the PyQtGraph subdirectory
|
||||
anywhere that is importable from your project.
|
||||
|
||||
Documentation
|
||||
|
@ -85,7 +106,7 @@ Documentation
|
|||
|
||||
The official documentation lives at [pyqtgraph.readthedocs.io](https://pyqtgraph.readthedocs.io)
|
||||
|
||||
The easiest way to learn pyqtgraph is to browse through the examples; run `python -m pyqtgraph.examples` to launch the examples application.
|
||||
The easiest way to learn PyQtGraph is to browse through the examples; run `python -m pyqtgraph.examples` to launch the examples application.
|
||||
|
||||
Used By
|
||||
-------
|
||||
|
|
140
asv.conf.json
140
asv.conf.json
|
@ -1,140 +0,0 @@
|
|||
{
|
||||
// The version of the config file format. Do not change, unless
|
||||
// you know what you are doing.
|
||||
"version": 1,
|
||||
|
||||
// The name of the project being benchmarked
|
||||
"project": "pyqtgraph",
|
||||
|
||||
// The project's homepage
|
||||
"project_url": "http://pyqtgraph.org/",
|
||||
|
||||
// The URL or local path of the source code repository for the
|
||||
// project being benchmarked
|
||||
"repo": ".",
|
||||
|
||||
// List of branches to benchmark. If not provided, defaults to "master"
|
||||
// (for git) or "default" (for mercurial).
|
||||
"branches": ["master"], // for git
|
||||
// "branches": ["default"], // for mercurial
|
||||
|
||||
// The DVCS being used. If not set, it will be automatically
|
||||
// determined from "repo" by looking at the protocol in the URL
|
||||
// (if remote), or by looking for special directories, such as
|
||||
// ".git" (if local).
|
||||
// "dvcs": "git",
|
||||
|
||||
// The tool to use to create environments. May be "conda",
|
||||
// "virtualenv" or other value depending on the plugins in use.
|
||||
// If missing or the empty string, the tool will be automatically
|
||||
// determined by looking for tools on the PATH environment
|
||||
// variable.
|
||||
"environment_type": "conda",
|
||||
|
||||
// timeout in seconds for installing any dependencies in environment
|
||||
// defaults to 10 min
|
||||
//"install_timeout": 600,
|
||||
|
||||
// the base URL to show a commit for the project.
|
||||
"show_commit_url": "http://github.com/pyqtgraph/pyqtgraph/commit/",
|
||||
|
||||
// The Pythons you'd like to test against. If not provided, defaults
|
||||
// to the current version of Python used to run `asv`.
|
||||
"pythons": ["2.7", "3.8"],
|
||||
|
||||
// The matrix of dependencies to test. Each key is the name of a
|
||||
// package (in PyPI) and the values are version numbers. An empty
|
||||
// list or empty string indicates to just test against the default
|
||||
// (latest) version. null indicates that the package is to not be
|
||||
// installed. If the package to be tested is only available from
|
||||
// PyPi, and the 'environment_type' is conda, then you can preface
|
||||
// the package name by 'pip+', and the package will be installed via
|
||||
// pip (with all the conda available packages installed first,
|
||||
// followed by the pip installed packages).
|
||||
//
|
||||
"matrix": {
|
||||
"numpy": [],
|
||||
"numba": [],
|
||||
"pyqt": ["4", "5"],
|
||||
},
|
||||
|
||||
// Combinations of libraries/python versions can be excluded/included
|
||||
// from the set to test. Each entry is a dictionary containing additional
|
||||
// key-value pairs to include/exclude.
|
||||
//
|
||||
// An exclude entry excludes entries where all values match. The
|
||||
// values are regexps that should match the whole string.
|
||||
//
|
||||
// An include entry adds an environment. Only the packages listed
|
||||
// are installed. The 'python' key is required. The exclude rules
|
||||
// do not apply to includes.
|
||||
//
|
||||
// In addition to package names, the following keys are available:
|
||||
//
|
||||
// - python
|
||||
// Python version, as in the *pythons* variable above.
|
||||
// - environment_type
|
||||
// Environment type, as above.
|
||||
// - sys_platform
|
||||
// Platform, as in sys.platform. Possible values for the common
|
||||
// cases: 'linux2', 'win32', 'cygwin', 'darwin'.
|
||||
//
|
||||
"exclude": [
|
||||
{"python": "3.8", "pyqt": "4"},
|
||||
],
|
||||
//
|
||||
// "include": [
|
||||
// // additional env for python2.7
|
||||
// {"python": "2.7", "numpy": "1.8"},
|
||||
// // additional env if run on windows+conda
|
||||
// {"platform": "win32", "environment_type": "conda", "python": "2.7", "libpython": ""},
|
||||
// ],
|
||||
|
||||
// The directory (relative to the current directory) that benchmarks are
|
||||
// stored in. If not provided, defaults to "benchmarks"
|
||||
"benchmark_dir": "benchmarks",
|
||||
|
||||
// The directory (relative to the current directory) to cache the Python
|
||||
// environments in. If not provided, defaults to "env"
|
||||
"env_dir": ".asv/env",
|
||||
|
||||
// The directory (relative to the current directory) that raw benchmark
|
||||
// results are stored in. If not provided, defaults to "results".
|
||||
"results_dir": ".asv/results",
|
||||
|
||||
// The directory (relative to the current directory) that the html tree
|
||||
// should be written to. If not provided, defaults to "html".
|
||||
"html_dir": ".asv/html",
|
||||
|
||||
// The number of characters to retain in the commit hashes.
|
||||
// "hash_length": 8,
|
||||
|
||||
// `asv` will cache wheels of the recent builds in each
|
||||
// environment, making them faster to install next time. This is
|
||||
// number of builds to keep, per environment.
|
||||
"build_cache_size": 5
|
||||
|
||||
// The commits after which the regression search in `asv publish`
|
||||
// should start looking for regressions. Dictionary whose keys are
|
||||
// regexps matching to benchmark names, and values corresponding to
|
||||
// the commit (exclusive) after which to start looking for
|
||||
// regressions. The default is to start from the first commit
|
||||
// with results. If the commit is `null`, regression detection is
|
||||
// skipped for the matching benchmark.
|
||||
//
|
||||
// "regressions_first_commits": {
|
||||
// "some_benchmark": "352cdf", // Consider regressions only after this commit
|
||||
// "another_benchmark": null, // Skip regression detection altogether
|
||||
// }
|
||||
|
||||
// The thresholds for relative change in results, after which `asv
|
||||
// publish` starts reporting regressions. Dictionary of the same
|
||||
// form as in ``regressions_first_commits``, with values
|
||||
// indicating the thresholds. If multiple entries match, the
|
||||
// maximum is taken. If no entry matches, the default is 5%.
|
||||
//
|
||||
// "regressions_thresholds": {
|
||||
// "some_benchmark": 0.01, // Threshold of 1%
|
||||
// "another_benchmark": 0.5, // Threshold of 50%
|
||||
// }
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
import numpy as np
|
||||
import pyqtgraph as pg
|
||||
|
||||
rng = np.random.default_rng(12345)
|
||||
|
||||
class _TimeSuite:
|
||||
params = ([10_000, 100_000, 1_000_000], ['all', 'finite', 'pairs', 'array'])
|
||||
|
||||
def setup(self, nelems, connect):
|
||||
self.xdata = np.arange(nelems, dtype=np.float64)
|
||||
self.ydata = rng.standard_normal(nelems, dtype=np.float64)
|
||||
if connect == 'array':
|
||||
self.connect_array = np.ones(nelems, dtype=bool)
|
||||
if self.have_nonfinite:
|
||||
self.ydata[::5000] = np.nan
|
||||
|
||||
def time_test(self, nelems, connect):
|
||||
if connect == 'array':
|
||||
connect = self.connect_array
|
||||
pg.arrayToQPath(self.xdata, self.ydata, connect=connect)
|
||||
|
||||
class TimeSuiteAllFinite(_TimeSuite):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.have_nonfinite = False
|
||||
|
||||
class TimeSuiteWithNonFinite(_TimeSuite):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.have_nonfinite = True
|
|
@ -1,72 +0,0 @@
|
|||
import numpy as np
|
||||
|
||||
from pyqtgraph.functions import makeARGB
|
||||
|
||||
|
||||
class TimeSuite(object):
|
||||
def __init__(self):
|
||||
self.c_map = None
|
||||
self.float_data = None
|
||||
self.uint8_data = None
|
||||
self.uint8_lut = None
|
||||
self.uint16_data = None
|
||||
self.uint16_lut = None
|
||||
|
||||
def setup(self):
|
||||
size = (500, 500)
|
||||
|
||||
self.float_data = {
|
||||
'data': np.random.normal(size=size),
|
||||
'levels': [-4., 4.],
|
||||
}
|
||||
|
||||
self.uint16_data = {
|
||||
'data': np.random.randint(100, 4500, size=size).astype('uint16'),
|
||||
'levels': [250, 3000],
|
||||
}
|
||||
|
||||
self.uint8_data = {
|
||||
'data': np.random.randint(0, 255, size=size).astype('ubyte'),
|
||||
'levels': [20, 220],
|
||||
}
|
||||
|
||||
self.c_map = np.array([
|
||||
[-500., 255.],
|
||||
[-255., 255.],
|
||||
[0., 500.],
|
||||
])
|
||||
|
||||
self.uint8_lut = np.zeros((256, 4), dtype='ubyte')
|
||||
for i in range(3):
|
||||
self.uint8_lut[:, i] = np.clip(np.linspace(self.c_map[i][0], self.c_map[i][1], 256), 0, 255)
|
||||
self.uint8_lut[:, 3] = 255
|
||||
|
||||
self.uint16_lut = np.zeros((2 ** 16, 4), dtype='ubyte')
|
||||
for i in range(3):
|
||||
self.uint16_lut[:, i] = np.clip(np.linspace(self.c_map[i][0], self.c_map[i][1], 2 ** 16), 0, 255)
|
||||
self.uint16_lut[:, 3] = 255
|
||||
|
||||
|
||||
def make_test(dtype, use_levels, lut_name, func_name):
|
||||
def time_test(self):
|
||||
data = getattr(self, dtype + '_data')
|
||||
makeARGB(
|
||||
data['data'],
|
||||
lut=getattr(self, lut_name + '_lut', None),
|
||||
levels=use_levels and data['levels'],
|
||||
)
|
||||
|
||||
time_test.__name__ = func_name
|
||||
return time_test
|
||||
|
||||
|
||||
for dt in ['float', 'uint16', 'uint8']:
|
||||
for levels in [True, False]:
|
||||
for ln in [None, 'uint8', 'uint16']:
|
||||
name = f'time_makeARGB_{dt}_{"" if levels else "no"}levels_{ln or "no"}lut'
|
||||
setattr(TimeSuite, name, make_test(dt, levels, ln, name))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
ts = TimeSuite()
|
||||
ts.setup()
|
|
@ -0,0 +1,139 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import numpy as np
|
||||
|
||||
import pyqtgraph as pg
|
||||
|
||||
try:
|
||||
import cupy as cp
|
||||
|
||||
pg.setConfigOption("useCupy", True)
|
||||
except ImportError:
|
||||
cp = None
|
||||
|
||||
try:
|
||||
import numba
|
||||
except ImportError:
|
||||
numba = None
|
||||
|
||||
|
||||
def renderQImage(*args, **kwargs):
|
||||
imgitem = pg.ImageItem(axisOrder='row-major')
|
||||
if 'autoLevels' not in kwargs:
|
||||
kwargs['autoLevels'] = False
|
||||
imgitem.setImage(*args, **kwargs)
|
||||
imgitem.render()
|
||||
|
||||
|
||||
def prime_numba():
|
||||
shape = (64, 64)
|
||||
lut_small = np.random.randint(256, size=(256, 3), dtype=np.uint8)
|
||||
lut_big = np.random.randint(256, size=(512, 3), dtype=np.uint8)
|
||||
for lut in [lut_small, lut_big]:
|
||||
renderQImage(np.zeros(shape, dtype=np.uint8), levels=(20, 220), lut=lut)
|
||||
renderQImage(np.zeros(shape, dtype=np.uint16), levels=(250, 3000), lut=lut)
|
||||
renderQImage(np.zeros(shape, dtype=np.float32), levels=(-4.0, 4.0), lut=lut)
|
||||
|
||||
|
||||
class _TimeSuite(object):
|
||||
def __init__(self):
|
||||
super(_TimeSuite, self).__init__()
|
||||
self.size = None
|
||||
self.float_data = None
|
||||
self.uint8_data = None
|
||||
self.uint8_lut = None
|
||||
self.uint16_data = None
|
||||
self.uint16_lut = None
|
||||
self.cupy_uint16_lut = None
|
||||
self.cupy_uint8_lut = None
|
||||
|
||||
def setup(self):
|
||||
size = (self.size, self.size)
|
||||
self.float_data, self.uint16_data, self.uint8_data, self.uint16_lut, self.uint8_lut = self._create_data(
|
||||
size, np
|
||||
)
|
||||
if numba is not None:
|
||||
# ensure JIT compilation
|
||||
pg.setConfigOption("useNumba", True)
|
||||
prime_numba()
|
||||
pg.setConfigOption("useNumba", False)
|
||||
if cp:
|
||||
_d1, _d2, _d3, self.cupy_uint16_lut, self.cupy_uint8_lut = self._create_data(size, cp)
|
||||
renderQImage(cp.asarray(self.uint16_data["data"])) # prime the gpu
|
||||
|
||||
@property
|
||||
def numba_uint16_lut(self):
|
||||
return self.uint16_lut
|
||||
|
||||
@property
|
||||
def numba_uint8_lut(self):
|
||||
return self.uint8_lut
|
||||
|
||||
@property
|
||||
def numpy_uint16_lut(self):
|
||||
return self.uint16_lut
|
||||
|
||||
@property
|
||||
def numpy_uint8_lut(self):
|
||||
return self.uint8_lut
|
||||
|
||||
@staticmethod
|
||||
def _create_data(size, xp):
|
||||
float_data = {
|
||||
"data": xp.random.normal(size=size).astype("float32"),
|
||||
"levels": [-4.0, 4.0],
|
||||
}
|
||||
uint16_data = {
|
||||
"data": xp.random.randint(100, 4500, size=size).astype("uint16"),
|
||||
"levels": [250, 3000],
|
||||
}
|
||||
uint8_data = {
|
||||
"data": xp.random.randint(0, 255, size=size).astype("ubyte"),
|
||||
"levels": [20, 220],
|
||||
}
|
||||
c_map = xp.array([[-500.0, 255.0], [-255.0, 255.0], [0.0, 500.0]])
|
||||
uint8_lut = xp.zeros((256, 4), dtype="ubyte")
|
||||
for i in range(3):
|
||||
uint8_lut[:, i] = xp.clip(xp.linspace(c_map[i][0], c_map[i][1], 256), 0, 255)
|
||||
uint8_lut[:, 3] = 255
|
||||
uint16_lut = xp.zeros((2 ** 16, 4), dtype="ubyte")
|
||||
for i in range(3):
|
||||
uint16_lut[:, i] = xp.clip(xp.linspace(c_map[i][0], c_map[i][1], 2 ** 16), 0, 255)
|
||||
uint16_lut[:, 3] = 255
|
||||
return float_data, uint16_data, uint8_data, uint16_lut, uint8_lut
|
||||
|
||||
|
||||
def make_test(dtype, kind, use_levels, lut_name, func_name):
|
||||
def time_test(self):
|
||||
data = getattr(self, dtype + "_data")
|
||||
levels = data["levels"] if use_levels else None
|
||||
lut = getattr(self, f"{kind}_{lut_name}_lut", None) if lut_name is not None else None
|
||||
pg.setConfigOption("useNumba", kind == "numba")
|
||||
img_data = data["data"]
|
||||
if kind == "cupy":
|
||||
img_data = cp.asarray(img_data)
|
||||
renderQImage(img_data, lut=lut, levels=levels)
|
||||
|
||||
time_test.__name__ = func_name
|
||||
return time_test
|
||||
|
||||
|
||||
for option in ["cupy", "numba", "numpy"]:
|
||||
if option == "cupy" and cp is None:
|
||||
continue
|
||||
if option == "numba" and numba is None:
|
||||
continue
|
||||
for data_type in ["float", "uint16", "uint8"]:
|
||||
for lvls in [True, False]:
|
||||
if data_type == "float" and not lvls:
|
||||
continue
|
||||
for lutname in [None, "uint8", "uint16"]:
|
||||
name = (
|
||||
f'time_1x_renderImageItem_{option}_{data_type}_{"" if lvls else "no"}levels_{lutname or "no"}lut'
|
||||
)
|
||||
setattr(_TimeSuite, name, make_test(data_type, option, lvls, lutname, name))
|
||||
|
||||
|
||||
class Time4096Suite(_TimeSuite):
|
||||
def __init__(self):
|
||||
super(Time4096Suite, self).__init__()
|
||||
self.size = 4096
|
|
@ -1,5 +1,5 @@
|
|||
pyside2
|
||||
numpy
|
||||
pyopengl
|
||||
sphinx==3.4.3
|
||||
sphinx_rtd_theme==0.5.1
|
||||
sphinx==4.1.1
|
||||
sphinx_rtd_theme==0.5.2
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
GLGraphItem
|
||||
===========
|
||||
|
||||
.. autoclass:: pyqtgraph.opengl.GLGraphItem
|
||||
:members:
|
||||
|
||||
.. automethod:: pyqtgraph.opengl.GLGraphItem.__init__
|
|
@ -19,6 +19,7 @@ Contents:
|
|||
glviewwidget
|
||||
|
||||
glgriditem
|
||||
glgraphitem
|
||||
glsurfaceplotitem
|
||||
glvolumeitem
|
||||
glimageitem
|
||||
|
|
|
@ -1,8 +1,84 @@
|
|||
ColorMap
|
||||
========
|
||||
Color Maps
|
||||
==========
|
||||
|
||||
A color map defines a relationship between scalar data values and a range of colors. Color maps are
|
||||
commonly used to generate false color images, color scatter-plot points, and illustrate the height
|
||||
of surface plots.
|
||||
|
||||
PyQtGraph's :class:`~pyqtgraph.ColorMap` can conveniently be applied to images and interactively
|
||||
adjusted by using :class:`~pyqtgraph.ColorBarItem`.
|
||||
To provide interactively user-defined color mappings, see
|
||||
:class:`~pyqtgraph.GradientEditorItem` and :class:`~pyqtgraph.GradientWidget`, which wraps it.
|
||||
:class:`~pyqtgraph.GradientEditorItem` combines the editing with a histogram and controls for
|
||||
interactively adjusting image levels.
|
||||
|
||||
ColorMap can also be used a convenient source of colors from a consistent palette or to generate
|
||||
QPen and QBrush objects used to draw lines and fills that are colored according to their values
|
||||
along the horizontal or vertical axis.
|
||||
|
||||
|
||||
Sources for color maps
|
||||
----------------------
|
||||
|
||||
Color maps can be user defined by assigning a number of *stops* over the range of 0 to 1. A color
|
||||
is given for each stop, and the in-between values are generated by interpolation.
|
||||
|
||||
When map colors directly represent values, an improperly designed map can obscure detail over
|
||||
certain ranges of values, while creating false detail in others. PyQtGraph includes the
|
||||
perceptually uniform color maps provided by the
|
||||
`Colorcet project <https://colorcet.holoviz.org/>`_. Color maps can also be imported from the
|
||||
``colorcet`` library or from ``matplotlib``, if either of these is installed.
|
||||
|
||||
To see all available color maps, please run the `ColorMap` demonstration available in the suite of
|
||||
:ref:`examples`.
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
False color display of a 2D data set. Display levels are controlled by
|
||||
a :class:`ColorBarItem <pyqtgraph.ColorBarItem>`:
|
||||
|
||||
.. literalinclude:: images/gen_example_false_color_image.py
|
||||
:lines: 18-28
|
||||
:dedent: 8
|
||||
|
||||
Using QtGui.QPen and QtGui.QBrush to color plots according to the plotted value:
|
||||
|
||||
.. literalinclude:: images/gen_example_gradient_plot.py
|
||||
:lines: 16-33
|
||||
:dedent: 8
|
||||
|
||||
.. image::
|
||||
images/example_false_color_image.png
|
||||
:width: 49%
|
||||
:alt: Example of a false color image
|
||||
|
||||
.. image::
|
||||
images/example_gradient_plot.png
|
||||
:width: 49%
|
||||
:alt: Example of drawing and filling plots with gradients
|
||||
|
||||
The use of color maps is also demonstrated in the `ImageView`, `Color Gradient Plots` and `ColorBarItem`
|
||||
:ref:`examples`.
|
||||
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
.. autofunction:: pyqtgraph.colormap.listMaps
|
||||
|
||||
.. autofunction:: pyqtgraph.colormap.get
|
||||
|
||||
.. autofunction:: pyqtgraph.colormap.getFromMatplotlib
|
||||
|
||||
.. autofunction:: pyqtgraph.colormap.getFromColorcet
|
||||
|
||||
.. autofunction:: pyqtgraph.colormap.modulatedBarData
|
||||
|
||||
|
||||
|
||||
|
||||
.. autoclass:: pyqtgraph.ColorMap
|
||||
:members:
|
||||
|
||||
.. automethod:: pyqtgraph.ColorMap.__init__
|
||||
|
||||
|
|
|
@ -41,23 +41,33 @@ Export Formats
|
|||
Exporting from the API
|
||||
----------------------
|
||||
|
||||
To export a file programatically, follow this example::
|
||||
To export a file programatically, follow this example:
|
||||
|
||||
import pyqtgraph as pg
|
||||
import pyqtgraph.exporters
|
||||
.. code-block:: python
|
||||
|
||||
import pyqtgraph as pg
|
||||
import pyqtgraph.exporters
|
||||
|
||||
# generate something to export
|
||||
plt = pg.plot([1,5,2,4,3])
|
||||
# generate something to export
|
||||
plt = pg.plot([1,5,2,4,3])
|
||||
|
||||
# create an exporter instance, as an argument give it
|
||||
# the item you wish to export
|
||||
exporter = pg.exporters.ImageExporter(plt.plotItem)
|
||||
# create an exporter instance, as an argument give it
|
||||
# the item you wish to export
|
||||
exporter = pg.exporters.ImageExporter(plt.plotItem)
|
||||
|
||||
# set export parameters if needed
|
||||
exporter.parameters()['width'] = 100 # (note this also affects height parameter)
|
||||
# set export parameters if needed
|
||||
exporter.parameters()['width'] = 100 # (note this also affects height parameter)
|
||||
|
||||
# save to file
|
||||
exporter.export('fileName.png')
|
||||
# save to file
|
||||
exporter.export('fileName.png')
|
||||
|
||||
To export the overall layout of a GraphicsLayoutWidget `grl`, the exporter initialization is
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
exporter = pg.exporters.ImageExporter( grl.scene() )
|
||||
|
||||
instead.
|
||||
|
||||
|
||||
Exporting 3D Graphics
|
||||
|
@ -69,5 +79,3 @@ generate an image from a GLViewWidget by using QGLWidget.grabFrameBuffer or QGLW
|
|||
glview.grabFrameBuffer().save('fileName.png')
|
||||
|
||||
See the Qt documentation for more information.
|
||||
|
||||
|
||||
|
|
|
@ -30,11 +30,11 @@ Qt uses the classes QColor, QPen, and QBrush to determine how to draw lines and
|
|||
|
||||
.. autofunction:: pyqtgraph.intColor
|
||||
|
||||
.. autofunction:: pyqtgraph.colorTuple
|
||||
.. autofunction:: pyqtgraph.CIELabColor
|
||||
|
||||
.. autofunction:: pyqtgraph.colorStr
|
||||
.. autofunction:: pyqtgraph.colorCIELab
|
||||
|
||||
.. autofunction:: pyqtgraph.glColor
|
||||
.. autofunction:: pyqtgraph.colorDistance
|
||||
|
||||
|
||||
Data Slicing
|
||||
|
@ -102,3 +102,16 @@ Miscellaneous Functions
|
|||
.. autofunction:: pyqtgraph.systemInfo
|
||||
|
||||
.. autofunction:: pyqtgraph.exit
|
||||
|
||||
|
||||
Legacy Color Helper Functions
|
||||
-------------------------------
|
||||
|
||||
The following helper functions should no longer be used. The functionality that they implement is trivial and it is suggested that the user use the equivalent QColor methods directly.
|
||||
|
||||
|
||||
.. autofunction:: pyqtgraph.colorTuple
|
||||
|
||||
.. autofunction:: pyqtgraph.colorStr
|
||||
|
||||
.. autofunction:: pyqtgraph.glColor
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
ColorBarItem
|
||||
============
|
||||
|
||||
.. autoclass:: pyqtgraph.ColorBarItem
|
||||
:members:
|
||||
|
||||
.. automethod:: pyqtgraph.ColorBarItem.__init__
|
|
@ -1,8 +1,66 @@
|
|||
ImageItem
|
||||
=========
|
||||
|
||||
:class:`~pyqtgraph.ImageItem` displays images inside a :class:`~pyqtgraph.GraphicsView`, or a
|
||||
:class:`~pyqtgraph.ViewBox`, which may itself be part of a :class:`~pyqtgraph.PlotItem`. It is designed
|
||||
for rapid updates as needed for a video display. The supplied data is optionally scaled (see
|
||||
:func:`~pyqtgraph.ImageItem.setLevels`) and/or colored according to a
|
||||
lookup table (see :func:`~pyqtgraph.ImageItem.setLookupTable`.
|
||||
|
||||
Data is provided as a NumPy array with an ordering of either
|
||||
|
||||
* `col-major`, where the shape of the array represents (width, height) or
|
||||
* `row-major`, where the shape of the array represents (height, width).
|
||||
|
||||
While `col-major` is the default, `row-major` ordering typically has the best performance. In either ordering,
|
||||
a third dimension can be added to the array to hold individual
|
||||
``[R,G,B]`` or ``[R,G,B,A]`` components.
|
||||
|
||||
Notes
|
||||
-----
|
||||
|
||||
Data ordering can be set for each ImageItem, or in the :ref:`global configuration options <apiref_config>` by ::
|
||||
|
||||
pyqtgraph.setConfigOption('imageAxisOrder', 'row-major') # best performance
|
||||
|
||||
An image can be placed into a plot area of a given extent directly through the
|
||||
:func:`~pyqtgraph.ImageItem.setRect` method or the ``rect`` keyword. This is internally realized through
|
||||
assigning a ``QtGui.QTransform``. For other translation, scaling or rotations effects that
|
||||
persist for all later image data, the user can also directly define and assign such a
|
||||
transform, as shown in the example below.
|
||||
|
||||
ImageItem is frequently used in conjunction with :class:`~pyqtgraph.ColorBarItem` to provide
|
||||
a color map display and interactive level adjustments, or with
|
||||
:class:`~pyqtgraph.HistogramLUTItem` or :class:`~pyqtgraph.HistogramLUTWidget` for a full GUI
|
||||
to control the levels and lookup table used to display the image.
|
||||
|
||||
If performance is critial, the following points may be worth investigating:
|
||||
|
||||
* Use row-major ordering and C-contiguous image data.
|
||||
* Manually provide ``level`` information to avoid autoLevels sampling of the image.
|
||||
* Prefer `float32` to `float64` for floating point data, avoid NaN values.
|
||||
* Use lookup tables with <= 256 entries for false color images.
|
||||
* Avoid individual level adjustments RGB components.
|
||||
* Use the latest version of NumPy. Notably, SIMD code added in version 1.20 significantly improved performance on Linux platforms.
|
||||
* Enable Numba with ``pyqtgraph.setConfigOption('useNumba', True)``, although the JIT compilation will only accelerate repeated image display.
|
||||
|
||||
.. _ImageItem_examples:
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
.. literalinclude:: ../images/gen_example_imageitem_transform.py
|
||||
:lines: 19-28
|
||||
:dedent: 8
|
||||
|
||||
.. image::
|
||||
../images/example_imageitem_transform.png
|
||||
:width: 49%
|
||||
:alt: Example of transformed image display
|
||||
|
||||
|
||||
|
||||
.. autoclass:: pyqtgraph.ImageItem
|
||||
:members:
|
||||
|
||||
.. automethod:: pyqtgraph.ImageItem.__init__
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ Contents:
|
|||
plotdataitem
|
||||
plotitem
|
||||
imageitem
|
||||
colorbaritem
|
||||
pcolormeshitem
|
||||
graphitem
|
||||
viewbox
|
||||
|
@ -46,3 +47,4 @@ Contents:
|
|||
uigraphicsitem
|
||||
graphicswidgetanchor
|
||||
dateaxisitem
|
||||
targetitem
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
TargetItem
|
||||
==========
|
||||
|
||||
.. autoclass:: pyqtgraph.TargetItem
|
||||
:members:
|
||||
|
||||
.. automethod:: pyqtgraph.TargetItem.__init__
|
||||
|
||||
|
||||
TargetLabel
|
||||
==================
|
||||
|
||||
.. autoclass:: pyqtgraph.TargetLabel
|
||||
:members:
|
||||
|
||||
.. automethod:: pyqtgraph.TargetLabel.__init__
|
||||
|
|
@ -101,7 +101,7 @@ Embedding PyQtGraph as a sub-package of a larger project
|
|||
|
||||
When writing applications or python packages that make use of pyqtgraph, it is most common to install pyqtgraph system-wide (or within a virtualenv) and simply call `import pyqtgraph` from within your application. The main benefit to this is that pyqtgraph is configured independently of your application and thus you (or your users) are free to install newer versions of pyqtgraph without changing anything in your application. This is standard practice when developing with python.
|
||||
|
||||
Occasionally, a specific program needs to be kept in working order for an extended amount of time after development has been completed. This is often the case for single-purpose scientific applications. If we want to ensure that the software will still work ten years later, then it is preferrable to tie it to a very specific version of pyqtgraph and *avoid* importing the system-installed version, which may be much newer and potentially incompatible. This is especially true when the application requires site-specific modifications to the pyqtgraph package.
|
||||
Occasionally, a specific program needs to be kept in working order for an extended amount of time after development has been completed. This is often the case for single-purpose scientific applications. If we want to ensure that the software will still work ten years later, then it is preferable to tie it to a very specific version of pyqtgraph and *avoid* importing the system-installed version, which may be much newer and potentially incompatible. This is especially true when the application requires site-specific modifications to the pyqtgraph package.
|
||||
|
||||
To support such a separate local installation, all internal import statements in pyqtgraph are relative. That means that pyqtgraph never refers to itself internally as 'pyqtgraph'. This allows the package to be renamed or used as a sub-package without any naming conflicts with other versions of pyqtgraph on the system.
|
||||
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 35 KiB |
Binary file not shown.
After Width: | Height: | Size: 17 KiB |
Binary file not shown.
After Width: | Height: | Size: 2.6 KiB |
|
@ -0,0 +1,45 @@
|
|||
"""
|
||||
generates 'example_false_color_image.png'
|
||||
"""
|
||||
import numpy as np
|
||||
import pyqtgraph as pg
|
||||
import pyqtgraph.exporters as exp
|
||||
from pyqtgraph.Qt import QtCore, QtGui, QtWidgets, mkQApp
|
||||
|
||||
class MainWindow(pg.GraphicsLayoutWidget):
|
||||
""" example application main window """
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.resize(420,400)
|
||||
self.show()
|
||||
|
||||
plot = self.addPlot() # title="non-interactive")
|
||||
|
||||
# prepare demonstration data:
|
||||
data = np.fromfunction(lambda i, j: (1+0.3*np.sin(i)) * (i)**2 + (j)**2, (100, 100))
|
||||
noisy_data = data * (1 + 0.2 * np.random.random(data.shape) )
|
||||
|
||||
# Example: False color image with interactive level adjustment
|
||||
img = pg.ImageItem(image=noisy_data) # create monochrome image from demonstration data
|
||||
plot.addItem( img ) # add to PlotItem 'plot'
|
||||
cm = pg.colormap.get('CET-L9') # prepare a linear color map
|
||||
bar = pg.ColorBarItem( values= (0, 20_000), cmap=cm ) # prepare interactive color bar
|
||||
# Have ColorBarItem control colors of img and appear in 'plot':
|
||||
bar.setImageItem( img, insert_in=plot )
|
||||
|
||||
self.timer = pg.QtCore.QTimer( singleShot=True )
|
||||
self.timer.timeout.connect(self.export)
|
||||
self.timer.start(100)
|
||||
|
||||
def export(self):
|
||||
print('exporting')
|
||||
exporter = exp.ImageExporter(self.scene())
|
||||
exporter.parameters()['width'] = 420
|
||||
exporter.export('example_false_color_image.png')
|
||||
|
||||
mkQApp("False color image example")
|
||||
main_window = MainWindow()
|
||||
|
||||
## Start Qt event loop
|
||||
if __name__ == '__main__':
|
||||
pg.exec()
|
|
@ -0,0 +1,55 @@
|
|||
"""
|
||||
generates 'example_gradient_plot.png'
|
||||
"""
|
||||
import numpy as np
|
||||
import pyqtgraph as pg
|
||||
import pyqtgraph.exporters as exp
|
||||
from pyqtgraph.Qt import QtCore, QtGui, QtWidgets, mkQApp
|
||||
|
||||
class MainWindow(pg.GraphicsLayoutWidget):
|
||||
""" example application main window """
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.resize(420,400)
|
||||
self.show()
|
||||
|
||||
# Prepare demonstration data
|
||||
raw = np.linspace(0.0, 2.0, 400)
|
||||
y_data1 = ( (raw+0.1)%1 ) ** 4
|
||||
y_data2 = ( (raw+0.1)%1 ) ** 4 - ( (raw+0.6)%1 ) ** 4
|
||||
|
||||
# Example 1: Gradient pen
|
||||
cm = pg.colormap.get('CET-L17') # prepare a linear color map
|
||||
cm.reverse() # reverse it to put light colors at the top
|
||||
pen = cm.getPen( span=(0.0,1.0), width=5 ) # gradient from blue (y=0) to white (y=1)
|
||||
# plot a curve drawn with a pen colored according to y value:
|
||||
curve1 = pg.PlotDataItem( y=y_data1, pen=pen )
|
||||
|
||||
# Example 2: Gradient brush
|
||||
cm = pg.colormap.get('CET-D1') # prepare a diverging color map
|
||||
cm.setMappingMode('diverging') # set mapping mode
|
||||
brush = cm.getBrush( span=(-1., 1.) ) # gradient from blue at -1 to red at +1
|
||||
# plot a curve that is filled to zero with the gradient brush:
|
||||
curve2 = pg.PlotDataItem( y=y_data2, pen='w', brush=brush, fillLevel=0.0 )
|
||||
|
||||
for idx, curve in enumerate( (curve1, curve2) ):
|
||||
plot = self.addPlot(row=idx, col=0)
|
||||
plot.getAxis('left').setWidth(25)
|
||||
plot.addItem( curve )
|
||||
|
||||
self.timer = pg.QtCore.QTimer( singleShot=True )
|
||||
self.timer.timeout.connect(self.export)
|
||||
self.timer.start(100)
|
||||
|
||||
def export(self):
|
||||
print('exporting')
|
||||
exporter = exp.ImageExporter(self.scene())
|
||||
exporter.parameters()['width'] = 420
|
||||
exporter.export('example_gradient_plot.png')
|
||||
|
||||
mkQApp("Gradient plotting example")
|
||||
main_window = MainWindow()
|
||||
|
||||
## Start Qt event loop
|
||||
if __name__ == '__main__':
|
||||
pg.exec()
|
|
@ -0,0 +1,45 @@
|
|||
"""
|
||||
generates 'example_false_color_image.png'
|
||||
"""
|
||||
import numpy as np
|
||||
import pyqtgraph as pg
|
||||
import pyqtgraph.exporters as exp
|
||||
from pyqtgraph.Qt import QtGui, mkQApp
|
||||
|
||||
class MainWindow(pg.GraphicsLayoutWidget):
|
||||
""" example application main window """
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.resize(420,400)
|
||||
self.show()
|
||||
|
||||
plot = self.addPlot()
|
||||
# Example: Transformed display of ImageItem
|
||||
|
||||
tr = QtGui.QTransform() # prepare ImageItem transformation:
|
||||
tr.scale(6.0, 3.0) # scale horizontal and vertical axes
|
||||
tr.translate(-1.5, -1.5) # move 3x3 image to locate center at axis origin
|
||||
|
||||
img = pg.ImageItem( image=np.eye(3), levels=(0,1) ) # create example image
|
||||
img.setTransform(tr) # assign transform
|
||||
|
||||
plot.addItem( img ) # add ImageItem to PlotItem
|
||||
plot.showAxes(True) # frame it with a full set of axes
|
||||
plot.invertY(True) # vertical axis counts top to bottom
|
||||
|
||||
self.timer = pg.QtCore.QTimer( singleShot=True )
|
||||
self.timer.timeout.connect(self.export)
|
||||
self.timer.start(100)
|
||||
|
||||
def export(self):
|
||||
print('exporting')
|
||||
exporter = exp.ImageExporter(self.scene())
|
||||
exporter.parameters()['width'] = 420
|
||||
exporter.export('example_imageitem_transform.png')
|
||||
|
||||
mkQApp("ImageItem transform example")
|
||||
main_window = MainWindow()
|
||||
|
||||
## Start Qt event loop
|
||||
if __name__ == '__main__':
|
||||
pg.exec()
|
|
@ -3,6 +3,8 @@ Parameter
|
|||
|
||||
.. autofunction:: pyqtgraph.parametertree.registerParameterType
|
||||
|
||||
.. autofunction:: pyqtgraph.parametertree.registerParameterItemType
|
||||
|
||||
.. autoclass:: pyqtgraph.parametertree.Parameter
|
||||
:members:
|
||||
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
..
|
||||
This file is auto-generated from pyqtgraph/tools/rebuildPtreeRst.py. Do not modify by hand! Instead, rerun the
|
||||
generation script with `python pyqtgraph/tools/rebuildPtreeRst.py`.
|
||||
|
||||
Built-in Parameter Types
|
||||
========================
|
||||
|
||||
|
@ -6,7 +10,25 @@ Built-in Parameter Types
|
|||
Parameters
|
||||
----------
|
||||
|
||||
.. autoclass:: SimpleParameter
|
||||
.. autoclass:: ActionParameter
|
||||
:members:
|
||||
|
||||
.. autoclass:: CalendarParameter
|
||||
:members:
|
||||
|
||||
.. autoclass:: ChecklistParameter
|
||||
:members:
|
||||
|
||||
.. autoclass:: ColorMapParameter
|
||||
:members:
|
||||
|
||||
.. autoclass:: ColorParameter
|
||||
:members:
|
||||
|
||||
.. autoclass:: FileParameter
|
||||
:members:
|
||||
|
||||
.. autoclass:: FontParameter
|
||||
:members:
|
||||
|
||||
.. autoclass:: GroupParameter
|
||||
|
@ -15,16 +37,46 @@ Parameters
|
|||
.. autoclass:: ListParameter
|
||||
:members:
|
||||
|
||||
.. autoclass:: TextParameter
|
||||
.. autoclass:: PenParameter
|
||||
:members:
|
||||
|
||||
.. autoclass:: ActionParameter
|
||||
.. autoclass:: ProgressBarParameter
|
||||
:members:
|
||||
|
||||
.. autoclass:: SimpleParameter
|
||||
:members:
|
||||
|
||||
.. autoclass:: SliderParameter
|
||||
:members:
|
||||
|
||||
.. autoclass:: TextParameter
|
||||
:members:
|
||||
|
||||
ParameterItems
|
||||
--------------
|
||||
|
||||
.. autoclass:: WidgetParameterItem
|
||||
.. autoclass:: ActionParameterItem
|
||||
:members:
|
||||
|
||||
.. autoclass:: BoolParameterItem
|
||||
:members:
|
||||
|
||||
.. autoclass:: CalendarParameterItem
|
||||
:members:
|
||||
|
||||
.. autoclass:: ChecklistParameterItem
|
||||
:members:
|
||||
|
||||
.. autoclass:: ColorMapParameterItem
|
||||
:members:
|
||||
|
||||
.. autoclass:: ColorParameterItem
|
||||
:members:
|
||||
|
||||
.. autoclass:: FileParameterItem
|
||||
:members:
|
||||
|
||||
.. autoclass:: FontParameterItem
|
||||
:members:
|
||||
|
||||
.. autoclass:: GroupParameterItem
|
||||
|
@ -33,8 +85,20 @@ ParameterItems
|
|||
.. autoclass:: ListParameterItem
|
||||
:members:
|
||||
|
||||
.. autoclass:: TextParameterItem
|
||||
.. autoclass:: NumericParameterItem
|
||||
:members:
|
||||
|
||||
.. autoclass:: ActionParameterItem
|
||||
.. autoclass:: PenParameterItem
|
||||
:members:
|
||||
|
||||
.. autoclass:: ProgressBarParameterItem
|
||||
:members:
|
||||
|
||||
.. autoclass:: SliderParameterItem
|
||||
:members:
|
||||
|
||||
.. autoclass:: StrParameterItem
|
||||
:members:
|
||||
|
||||
.. autoclass:: TextParameterItem
|
||||
:members:
|
||||
|
|
|
@ -16,7 +16,7 @@ To select a 2D region from an image, pyqtgraph uses the :class:`ROI <pyqtgraph.R
|
|||
|
||||
To automatically extract a region of image data using an ROI and an ImageItem, use :func:`ROI.getArrayRegion <pyqtgraph.ROI.getArrayRegion>`. ROI classes use the :func:`affineSlice <pyqtgraph.affineSlice>` function to perform this extraction.
|
||||
|
||||
ROI can also be used as a control for moving/rotating/scaling items in a scene similar to most vetctor graphics editing applications.
|
||||
ROI can also be used as a control for moving/rotating/scaling items in a scene similar to most vector graphics editing applications.
|
||||
|
||||
See the ROITypes example for more information.
|
||||
|
||||
|
|
|
@ -51,4 +51,4 @@ anim = a.makeAnimation(loop=-1)
|
|||
anim.start()
|
||||
|
||||
if __name__ == '__main__':
|
||||
pg.mkQApp().exec_()
|
||||
pg.exec()
|
||||
|
|
|
@ -35,4 +35,4 @@ bg = BarGraph(x=x, y=y1*0.3+2, height=0.4+y1*0.2, width=0.8)
|
|||
win.addItem(bg)
|
||||
|
||||
if __name__ == '__main__':
|
||||
pg.mkQApp().exec_()
|
||||
pg.exec()
|
||||
|
|
|
@ -19,4 +19,4 @@ data = np.random.normal(size=(500,500))
|
|||
pg.image(data, title="Simplest possible image example")
|
||||
|
||||
if __name__ == '__main__':
|
||||
pg.mkQApp().exec_()
|
||||
pg.exec()
|
||||
|
|
|
@ -41,7 +41,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||
|
||||
cmap = pg.colormap.get('CET-L9')
|
||||
bar = pg.ColorBarItem(
|
||||
interactive=False, values= (0, 30_000), cmap=cmap,
|
||||
interactive=False, values= (0, 30_000), colorMap=cmap,
|
||||
label='vertical fixed color bar'
|
||||
)
|
||||
bar.setImageItem( i1, insert_in=p1 )
|
||||
|
@ -59,11 +59,11 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||
cmap = pg.colormap.get('CET-L4')
|
||||
bar = pg.ColorBarItem(
|
||||
values = (0, 30_000),
|
||||
cmap=cmap,
|
||||
colorMap=cmap,
|
||||
label='horizontal color bar',
|
||||
limits = (0, None),
|
||||
rounding=1000,
|
||||
orientation = 'horizontal',
|
||||
orientation = 'h',
|
||||
pen='#8888FF', hoverPen='#EEEEFF', hoverBrush='#EEEEFF80'
|
||||
)
|
||||
bar.setImageItem( i2, insert_in=p2 )
|
||||
|
@ -83,7 +83,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||
limits = (-30_000, 30_000), # start with full range...
|
||||
rounding=1000,
|
||||
width = 10,
|
||||
cmap=cmap )
|
||||
colorMap=cmap )
|
||||
bar.setImageItem( [i3, i4] )
|
||||
bar.setLevels( low=-5_000, high=15_000) # ... then adjust to retro sunset.
|
||||
|
||||
|
@ -97,4 +97,4 @@ main_window = MainWindow()
|
|||
|
||||
## Start Qt event loop
|
||||
if __name__ == '__main__':
|
||||
mkQApp().exec_()
|
||||
pg.exec()
|
||||
|
|
|
@ -27,4 +27,4 @@ btn.sigColorChanging.connect(change)
|
|||
btn.sigColorChanged.connect(done)
|
||||
|
||||
if __name__ == '__main__':
|
||||
pg.mkQApp().exec_()
|
||||
pg.exec()
|
||||
|
|
|
@ -0,0 +1,147 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
This example demonstrates plotting with color gradients.
|
||||
It also shows multiple plots with timed rolling updates
|
||||
"""
|
||||
# Add path to library (just for examples; you do not need this)
|
||||
import initExample
|
||||
|
||||
import numpy as np
|
||||
import time
|
||||
from pyqtgraph.Qt import QtCore, QtGui, QtWidgets, mkQApp
|
||||
import pyqtgraph as pg
|
||||
|
||||
class DataSource(object):
|
||||
""" source of buffered demonstration data """
|
||||
def __init__(self, sample_rate=200., signal_period=0.55, negative_period=None, max_length=300):
|
||||
""" prepare, but don't start yet """
|
||||
self.rate = sample_rate
|
||||
self.period = signal_period
|
||||
self.neg_period = negative_period
|
||||
self.start_time = 0.
|
||||
self.sample_idx = 0 # number of next sample to be taken
|
||||
|
||||
def start(self, timestamp):
|
||||
""" start acquiring simulated data """
|
||||
self.start_time = timestamp
|
||||
self.sample_idx = 0
|
||||
|
||||
def get_data(self, timestamp, max_length=6000):
|
||||
""" return all data acquired since last get_data call """
|
||||
next_idx = int( (timestamp - self.start_time) * self.rate )
|
||||
if next_idx - self.sample_idx > max_length:
|
||||
self.sample_idx = next_idx - max_length # catch up if needed
|
||||
# create some mildly intersting data:
|
||||
sample_phases = np.arange( self.sample_idx, next_idx, dtype=np.float64 )
|
||||
self.sample_idx = next_idx
|
||||
|
||||
sample_phase_pos = sample_phases / (self.period*self.rate)
|
||||
sample_phase_pos %= 1.0
|
||||
if self.neg_period is None:
|
||||
return sample_phase_pos**4
|
||||
sample_phase_neg = sample_phases / (self.neg_period*self.rate)
|
||||
sample_phase_neg %= 1.0
|
||||
return sample_phase_pos**4 - sample_phase_neg**4
|
||||
|
||||
class MainWindow(pg.GraphicsLayoutWidget):
|
||||
""" example application main window """
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.setWindowTitle('pyqtgraph example: gradient plots')
|
||||
self.resize(800,800)
|
||||
self.show()
|
||||
|
||||
layout = self # we are using a GraphicsLayoutWidget as main window for convenience
|
||||
cm = pg.colormap.get('CET-L17')
|
||||
cm.reverse()
|
||||
pen0 = cm.getPen( span=(0.0,1.0), width=5 )
|
||||
curve0 = pg.PlotDataItem(pen=pen0 )
|
||||
comment0 = 'Clipped color map applied to vertical axis'
|
||||
|
||||
cm = pg.colormap.get('CET-D1')
|
||||
cm.setMappingMode('diverging')
|
||||
brush = cm.getBrush( span=(-1., 1.), orientation='vertical' )
|
||||
curve1 = pg.PlotDataItem(pen='w', brush=brush, fillLevel=0.0 )
|
||||
comment1 = 'Diverging vertical color map used as brush'
|
||||
|
||||
cm = pg.colormap.get('CET-L17')
|
||||
cm.setMappingMode('mirror')
|
||||
pen2 = cm.getPen( span=(400.0,600.0), width=5, orientation='horizontal' )
|
||||
curve2 = pg.PlotDataItem(pen=pen2 )
|
||||
comment2 = 'Mirrored color map applied to horizontal axis'
|
||||
|
||||
cm = pg.colormap.get('CET-C2')
|
||||
cm.setMappingMode('repeat')
|
||||
pen3 = cm.getPen( span=(100, 200), width=5, orientation='horizontal' )
|
||||
curve3 = pg.PlotDataItem(pen=pen3 ) # vertical diverging fill
|
||||
comment3 = 'Repeated color map applied to horizontal axis'
|
||||
|
||||
curves = (curve0, curve1, curve2, curve3)
|
||||
comments = (comment0, comment1, comment2, comment3)
|
||||
|
||||
length = int( 3.0 * 200. ) # length of display in samples
|
||||
self.top_plot = None
|
||||
for idx, (curve, comment) in enumerate( zip(curves,comments) ):
|
||||
plot = layout.addPlot(row=idx+1, col=0)
|
||||
text = pg.TextItem( comment, anchor=(0,1) )
|
||||
text.setPos(0.,1.)
|
||||
if self.top_plot is None:
|
||||
self.top_plot = plot
|
||||
else:
|
||||
plot.setXLink( self.top_plot )
|
||||
plot.addItem( curve )
|
||||
plot.addItem( text )
|
||||
plot.setXRange( 0, length )
|
||||
if idx != 1: plot.setYRange( 0. , 1.1 )
|
||||
else : plot.setYRange( -1. , 1.2 ) # last plot include positive/negative data
|
||||
|
||||
self.traces = (
|
||||
{'crv': curve0, 'buf': np.zeros( length ), 'ptr':0, 'ds': DataSource( signal_period=0.55 ) },
|
||||
{'crv': curve1, 'buf': np.zeros( length ), 'ptr':0, 'ds': DataSource( signal_period=0.61, negative_period=0.55 ) },
|
||||
{'crv': curve2, 'buf': np.zeros( length ), 'ptr':0, 'ds': DataSource( signal_period=0.65 ) },
|
||||
{'crv': curve3, 'buf': np.zeros( length ), 'ptr':0, 'ds': DataSource( signal_period=0.52 ) },
|
||||
)
|
||||
self.timer = QtCore.QTimer(timerType=QtCore.Qt.TimerType.PreciseTimer)
|
||||
self.timer.timeout.connect(self.update)
|
||||
timestamp = time.perf_counter()
|
||||
for dic in self.traces:
|
||||
dic['ds'].start( timestamp )
|
||||
self.last_update = time.perf_counter()
|
||||
self.mean_dt = None
|
||||
self.timer.start(33)
|
||||
|
||||
def update(self):
|
||||
""" called by timer at 30 Hz """
|
||||
timestamp = time.perf_counter()
|
||||
# measure actual update rate:
|
||||
dt = timestamp - self.last_update
|
||||
if self.mean_dt is None:
|
||||
self.mean_dt = dt
|
||||
else:
|
||||
self.mean_dt = 0.95 * self.mean_dt + 0.05 * dt # average over fluctuating measurements
|
||||
self.top_plot.setTitle(
|
||||
'refresh: {:0.1f}ms -> {:0.1f} fps'.format( 1000*self.mean_dt, 1/self.mean_dt )
|
||||
)
|
||||
# handle rolling buffer:
|
||||
self.last_update = timestamp
|
||||
for dic in self.traces:
|
||||
new_data = dic['ds'].get_data( timestamp )
|
||||
idx_a = dic['ptr']
|
||||
idx_b = idx_a + len( new_data )
|
||||
len_buffer = dic['buf'].shape[0]
|
||||
if idx_b < len_buffer: # data does not cross buffer boundary
|
||||
dic['buf'][idx_a:idx_b] = new_data
|
||||
else: # part of the new data needs to roll over to beginning of buffer
|
||||
len_1 = len_buffer - idx_a # this many elements still fit
|
||||
dic['buf'][idx_a:idx_a+len_1] = new_data[:len_1] # first part of data at end
|
||||
idx_b = len(new_data) - len_1
|
||||
dic['buf'][0:idx_b] = new_data[len_1:] # second part of data at re-start
|
||||
dic['ptr'] = idx_b
|
||||
dic['crv'].setData( dic['buf'] )
|
||||
|
||||
mkQApp("Gradient plotting example")
|
||||
main_window = MainWindow()
|
||||
|
||||
## Start Qt event loop
|
||||
if __name__ == '__main__':
|
||||
pg.exec()
|
|
@ -30,4 +30,4 @@ c.show()
|
|||
c.setWindowTitle('pyqtgraph example: ConsoleWidget')
|
||||
|
||||
if __name__ == '__main__':
|
||||
pg.mkQApp().exec_()
|
||||
pg.exec()
|
||||
|
|
|
@ -51,7 +51,7 @@ class Graph(pg.GraphItem):
|
|||
|
||||
|
||||
def mouseDragEvent(self, ev):
|
||||
if ev.button() != QtCore.Qt.LeftButton:
|
||||
if ev.button() != QtCore.Qt.MouseButton.LeftButton:
|
||||
ev.ignore()
|
||||
return
|
||||
|
||||
|
@ -130,4 +130,4 @@ g.setData(pos=pos, adj=adj, pen=lines, size=1, symbol=symbols, pxMode=False, tex
|
|||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pg.mkQApp().exec_()
|
||||
pg.exec()
|
||||
|
|
|
@ -58,4 +58,4 @@ imv1.setLevels(-0.003, 0.003)
|
|||
update()
|
||||
|
||||
if __name__ == '__main__':
|
||||
pg.mkQApp().exec_()
|
||||
pg.exec()
|
||||
|
|
|
@ -43,4 +43,4 @@ tree.resize(600,600)
|
|||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pg.mkQApp().exec_()
|
||||
pg.exec()
|
||||
|
|
|
@ -27,4 +27,4 @@ w.setWindowTitle('pyqtgraph example: DateAxisItem')
|
|||
w.show()
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.exec_()
|
||||
pg.exec()
|
||||
|
|
|
@ -43,4 +43,4 @@ window.setWindowTitle('pyqtgraph example: DateAxisItem_QtDesigner')
|
|||
window.show()
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.exec_()
|
||||
pg.exec()
|
||||
|
|
|
@ -46,4 +46,4 @@ tree.resize(1000, 800)
|
|||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pg.mkQApp().exec_()
|
||||
pg.exec()
|
||||
|
|
|
@ -43,4 +43,4 @@ img.setDrawKernel(kern, mask=kern, center=(1,1), mode='add')
|
|||
img.setLevels([0, 10])
|
||||
|
||||
if __name__ == '__main__':
|
||||
pg.mkQApp().exec_()
|
||||
pg.exec()
|
||||
|
|
|
@ -27,4 +27,4 @@ plt.addItem(err)
|
|||
plt.plot(x, y, symbol='o', pen={'color': 0.8, 'width': 2})
|
||||
|
||||
if __name__ == '__main__':
|
||||
pg.mkQApp().exec_()
|
||||
pg.exec()
|
||||
|
|
|
@ -4,10 +4,12 @@ import re
|
|||
import sys
|
||||
import subprocess
|
||||
from argparse import Namespace
|
||||
|
||||
import pyqtgraph as pg
|
||||
from pyqtgraph.Qt import QtGui, QtCore, QT_LIB
|
||||
from pyqtgraph.Qt import QtWidgets, QtGui, QtCore, QT_LIB
|
||||
from collections import OrderedDict
|
||||
from .utils import examples
|
||||
from .utils import examples_
|
||||
from functools import lru_cache
|
||||
|
||||
path = os.path.abspath(os.path.dirname(__file__))
|
||||
sys.path.insert(0, path)
|
||||
|
@ -28,7 +30,7 @@ QTextCharFormat = QtGui.QTextCharFormat
|
|||
QSyntaxHighlighter = QtGui.QSyntaxHighlighter
|
||||
|
||||
|
||||
def format(color, style=''):
|
||||
def charFormat(color, style='', background=None):
|
||||
"""
|
||||
Return a QTextCharFormat with the given attributes.
|
||||
"""
|
||||
|
@ -41,9 +43,11 @@ def format(color, style=''):
|
|||
_format = QTextCharFormat()
|
||||
_format.setForeground(_color)
|
||||
if 'bold' in style:
|
||||
_format.setFontWeight(QFont.Bold)
|
||||
_format.setFontWeight(QFont.Weight.Bold)
|
||||
if 'italic' in style:
|
||||
_format.setFontItalic(True)
|
||||
if background is not None:
|
||||
_format.setBackground(pg.mkColor(background))
|
||||
|
||||
return _format
|
||||
|
||||
|
@ -95,28 +99,28 @@ class DarkThemeColors:
|
|||
|
||||
|
||||
LIGHT_STYLES = {
|
||||
'keyword': format(LightThemeColors.Blue, 'bold'),
|
||||
'operator': format(LightThemeColors.Red, 'bold'),
|
||||
'brace': format(LightThemeColors.Purple),
|
||||
'defclass': format(LightThemeColors.Indigo, 'bold'),
|
||||
'string': format(LightThemeColors.Amber),
|
||||
'string2': format(LightThemeColors.DeepPurple),
|
||||
'comment': format(LightThemeColors.Green, 'italic'),
|
||||
'self': format(LightThemeColors.Blue, 'bold'),
|
||||
'numbers': format(LightThemeColors.Teal),
|
||||
'keyword': charFormat(LightThemeColors.Blue, 'bold'),
|
||||
'operator': charFormat(LightThemeColors.Red, 'bold'),
|
||||
'brace': charFormat(LightThemeColors.Purple),
|
||||
'defclass': charFormat(LightThemeColors.Indigo, 'bold'),
|
||||
'string': charFormat(LightThemeColors.Amber),
|
||||
'string2': charFormat(LightThemeColors.DeepPurple),
|
||||
'comment': charFormat(LightThemeColors.Green, 'italic'),
|
||||
'self': charFormat(LightThemeColors.Blue, 'bold'),
|
||||
'numbers': charFormat(LightThemeColors.Teal),
|
||||
}
|
||||
|
||||
|
||||
DARK_STYLES = {
|
||||
'keyword': format(DarkThemeColors.Blue, 'bold'),
|
||||
'operator': format(DarkThemeColors.Red, 'bold'),
|
||||
'brace': format(DarkThemeColors.Purple),
|
||||
'defclass': format(DarkThemeColors.Indigo, 'bold'),
|
||||
'string': format(DarkThemeColors.Amber),
|
||||
'string2': format(DarkThemeColors.DeepPurple),
|
||||
'comment': format(DarkThemeColors.Green, 'italic'),
|
||||
'self': format(DarkThemeColors.Blue, 'bold'),
|
||||
'numbers': format(DarkThemeColors.Teal),
|
||||
'keyword': charFormat(DarkThemeColors.Blue, 'bold'),
|
||||
'operator': charFormat(DarkThemeColors.Red, 'bold'),
|
||||
'brace': charFormat(DarkThemeColors.Purple),
|
||||
'defclass': charFormat(DarkThemeColors.Indigo, 'bold'),
|
||||
'string': charFormat(DarkThemeColors.Amber),
|
||||
'string2': charFormat(DarkThemeColors.DeepPurple),
|
||||
'comment': charFormat(DarkThemeColors.Green, 'italic'),
|
||||
'self': charFormat(DarkThemeColors.Blue, 'bold'),
|
||||
'numbers': charFormat(DarkThemeColors.Teal),
|
||||
}
|
||||
|
||||
|
||||
|
@ -145,7 +149,7 @@ class PythonHighlighter(QSyntaxHighlighter):
|
|||
]
|
||||
|
||||
def __init__(self, document):
|
||||
QSyntaxHighlighter.__init__(self, document)
|
||||
super().__init__(document)
|
||||
|
||||
# Multi-line strings (expression, flag, style)
|
||||
self.tri_single = (QRegularExpression("'''"), 1, 'string2')
|
||||
|
@ -185,17 +189,19 @@ class PythonHighlighter(QSyntaxHighlighter):
|
|||
(r'#[^\n]*', 0, 'comment'),
|
||||
]
|
||||
self.rules = rules
|
||||
self.searchText = None
|
||||
|
||||
@property
|
||||
def styles(self):
|
||||
app = QtGui.QApplication.instance()
|
||||
app = QtWidgets.QApplication.instance()
|
||||
return DARK_STYLES if app.property('darkMode') else LIGHT_STYLES
|
||||
|
||||
def highlightBlock(self, text):
|
||||
"""Apply syntax highlighting to the given block of text.
|
||||
"""
|
||||
# Do other syntax formatting
|
||||
for expression, nth, format in self.rules:
|
||||
rules = self.rules.copy()
|
||||
for expression, nth, format in rules:
|
||||
format = self.styles[format]
|
||||
|
||||
for n, match in enumerate(re.finditer(expression, text)):
|
||||
|
@ -204,6 +210,8 @@ class PythonHighlighter(QSyntaxHighlighter):
|
|||
start = match.start()
|
||||
length = match.end() - start
|
||||
self.setFormat(start, length, format)
|
||||
|
||||
self.applySearchHighlight(text)
|
||||
self.setCurrentBlockState(0)
|
||||
|
||||
# Do multi-line strings
|
||||
|
@ -249,39 +257,86 @@ class PythonHighlighter(QSyntaxHighlighter):
|
|||
length = len(text) - start + add
|
||||
# Apply formatting
|
||||
self.setFormat(start, length, self.styles[style])
|
||||
# Highlighting sits on top of this formatting
|
||||
# Look for the next match
|
||||
match = delimiter.match(text, start + length)
|
||||
start = match.capturedStart()
|
||||
|
||||
self.applySearchHighlight(text)
|
||||
|
||||
# Return True if still inside a multi-line string, False otherwise
|
||||
if self.currentBlockState() == in_state:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def applySearchHighlight(self, text):
|
||||
if not self.searchText:
|
||||
return
|
||||
expr = f'(?i){self.searchText}'
|
||||
palette: QtGui.QPalette = app.palette()
|
||||
color = palette.highlight().color()
|
||||
fgndColor = palette.color(palette.ColorGroup.Current,
|
||||
palette.ColorRole.Text).name()
|
||||
style = charFormat(fgndColor, background=color.name())
|
||||
for match in re.finditer(expr, text):
|
||||
start = match.start()
|
||||
length = match.end() - start
|
||||
self.setFormat(start, length, style)
|
||||
|
||||
|
||||
class ExampleLoader(QtGui.QMainWindow):
|
||||
def unnestedDict(exDict):
|
||||
"""Converts a dict-of-dicts to a singly nested dict for non-recursive parsing"""
|
||||
out = {}
|
||||
for kk, vv in exDict.items():
|
||||
if isinstance(vv, dict):
|
||||
out.update(unnestedDict(vv))
|
||||
else:
|
||||
out[kk] = vv
|
||||
return out
|
||||
|
||||
|
||||
|
||||
class ExampleLoader(QtWidgets.QMainWindow):
|
||||
def __init__(self):
|
||||
QtGui.QMainWindow.__init__(self)
|
||||
QtWidgets.QMainWindow.__init__(self)
|
||||
self.ui = ui_template.Ui_Form()
|
||||
self.cw = QtGui.QWidget()
|
||||
self.cw = QtWidgets.QWidget()
|
||||
self.setCentralWidget(self.cw)
|
||||
self.ui.setupUi(self.cw)
|
||||
self.setWindowTitle("PyQtGraph Examples")
|
||||
self.codeBtn = QtGui.QPushButton('Run Edited Code')
|
||||
self.codeLayout = QtGui.QGridLayout()
|
||||
self.codeBtn = QtWidgets.QPushButton('Run Edited Code')
|
||||
self.codeLayout = QtWidgets.QGridLayout()
|
||||
self.ui.codeView.setLayout(self.codeLayout)
|
||||
self.hl = PythonHighlighter(self.ui.codeView.document())
|
||||
app = QtGui.QApplication.instance()
|
||||
app = QtWidgets.QApplication.instance()
|
||||
app.paletteChanged.connect(self.updateTheme)
|
||||
self.codeLayout.addItem(QtGui.QSpacerItem(100,100,QtGui.QSizePolicy.Expanding,QtGui.QSizePolicy.Expanding), 0, 0)
|
||||
policy = QtWidgets.QSizePolicy.Policy.Expanding
|
||||
self.codeLayout.addItem(QtWidgets.QSpacerItem(100,100, policy, policy), 0, 0)
|
||||
self.codeLayout.addWidget(self.codeBtn, 1, 1)
|
||||
self.codeBtn.hide()
|
||||
|
||||
global examples
|
||||
textFil = self.ui.exampleFilter
|
||||
self.curListener = None
|
||||
self.ui.exampleFilter.setFocus()
|
||||
|
||||
def onComboChanged(searchType):
|
||||
if self.curListener is not None:
|
||||
self.curListener.disconnect()
|
||||
self.curListener = textFil.textChanged
|
||||
if searchType == 'Content Search':
|
||||
self.curListener.connect(self.filterByContent)
|
||||
else:
|
||||
self.hl.searchText = None
|
||||
self.curListener.connect(self.filterByTitle)
|
||||
# Fire on current text, too
|
||||
self.curListener.emit(textFil.text())
|
||||
|
||||
self.ui.searchFiles.currentTextChanged.connect(onComboChanged)
|
||||
onComboChanged(self.ui.searchFiles.currentText())
|
||||
|
||||
self.itemCache = []
|
||||
self.populateTree(self.ui.exampleTree.invisibleRootItem(), examples)
|
||||
self.populateTree(self.ui.exampleTree.invisibleRootItem(), examples_)
|
||||
self.ui.exampleTree.expandAll()
|
||||
|
||||
self.resize(1000,500)
|
||||
|
@ -290,9 +345,76 @@ class ExampleLoader(QtGui.QMainWindow):
|
|||
self.ui.loadBtn.clicked.connect(self.loadFile)
|
||||
self.ui.exampleTree.currentItemChanged.connect(self.showFile)
|
||||
self.ui.exampleTree.itemDoubleClicked.connect(self.loadFile)
|
||||
self.ui.codeView.textChanged.connect(self.codeEdited)
|
||||
|
||||
# textChanged fires when the highlighter is reassigned the same document. Prevent this
|
||||
# from showing "run edited code" by checking for actual content change
|
||||
oldText = self.ui.codeView.toPlainText()
|
||||
def onTextChange():
|
||||
nonlocal oldText
|
||||
newText = self.ui.codeView.toPlainText()
|
||||
if newText != oldText:
|
||||
oldText = newText
|
||||
self.codeEdited()
|
||||
|
||||
self.ui.codeView.textChanged.connect(onTextChange)
|
||||
self.codeBtn.clicked.connect(self.runEditedCode)
|
||||
|
||||
def filterByTitle(self, text):
|
||||
self.showExamplesByTitle(self.getMatchingTitles(text))
|
||||
self.hl.setDocument(self.ui.codeView.document())
|
||||
|
||||
def filterByContent(self, text=None):
|
||||
# Don't filter very short strings
|
||||
checkDict = unnestedDict(examples_)
|
||||
self.hl.searchText = text
|
||||
# Need to reapply to current document
|
||||
self.hl.setDocument(self.ui.codeView.document())
|
||||
titles = []
|
||||
text = text.lower()
|
||||
for kk, vv in checkDict.items():
|
||||
if isinstance(vv, Namespace):
|
||||
vv = vv.filename
|
||||
filename = os.path.join(path, vv)
|
||||
contents = self.getExampleContent(filename).lower()
|
||||
if text in contents:
|
||||
titles.append(kk)
|
||||
self.showExamplesByTitle(titles)
|
||||
|
||||
def getMatchingTitles(self, text, exDict=None, acceptAll=False):
|
||||
if exDict is None:
|
||||
exDict = examples_
|
||||
text = text.lower()
|
||||
titles = []
|
||||
for kk, vv in exDict.items():
|
||||
matched = acceptAll or text in kk.lower()
|
||||
if isinstance(vv, dict):
|
||||
titles.extend(self.getMatchingTitles(text, vv, acceptAll=matched))
|
||||
elif matched:
|
||||
titles.append(kk)
|
||||
return titles
|
||||
|
||||
def showExamplesByTitle(self, titles):
|
||||
QTWI = QtWidgets.QTreeWidgetItemIterator
|
||||
flag = QTWI.IteratorFlag.NoChildren
|
||||
treeIter = QTWI(self.ui.exampleTree, flag)
|
||||
item = treeIter.value()
|
||||
while item is not None:
|
||||
parent = item.parent()
|
||||
show = (item.childCount() or item.text(0) in titles)
|
||||
item.setHidden(not show)
|
||||
|
||||
# If all children of a parent are gone, hide it
|
||||
if parent:
|
||||
hideParent = True
|
||||
for ii in range(parent.childCount()):
|
||||
if not parent.child(ii).isHidden():
|
||||
hideParent = False
|
||||
break
|
||||
parent.setHidden(hideParent)
|
||||
|
||||
treeIter += 1
|
||||
item = treeIter.value()
|
||||
|
||||
def simulate_black_mode(self):
|
||||
"""
|
||||
used to simulate MacOS "black mode" on other platforms
|
||||
|
@ -301,15 +423,15 @@ class ExampleLoader(QtGui.QMainWindow):
|
|||
# first, a dark background
|
||||
c = QtGui.QColor('#171717')
|
||||
p = self.ui.codeView.palette()
|
||||
p.setColor(QtGui.QPalette.Active, QtGui.QPalette.Base, c)
|
||||
p.setColor(QtGui.QPalette.Inactive, QtGui.QPalette.Base, c)
|
||||
p.setColor(QtGui.QPalette.ColorGroup.Active, QtGui.QPalette.ColorRole.Base, c)
|
||||
p.setColor(QtGui.QPalette.ColorGroup.Inactive, QtGui.QPalette.ColorRole.Base, c)
|
||||
self.ui.codeView.setPalette(p)
|
||||
# then, a light font
|
||||
f = QtGui.QTextCharFormat()
|
||||
f.setForeground(QtGui.QColor('white'))
|
||||
self.ui.codeView.setCurrentCharFormat(f)
|
||||
# finally, override application automatic detection
|
||||
app = QtGui.QApplication.instance()
|
||||
app = QtWidgets.QApplication.instance()
|
||||
app.setProperty('darkMode', True)
|
||||
|
||||
def updateTheme(self):
|
||||
|
@ -318,7 +440,7 @@ class ExampleLoader(QtGui.QMainWindow):
|
|||
def populateTree(self, root, examples):
|
||||
bold_font = None
|
||||
for key, val in examples.items():
|
||||
item = QtGui.QTreeWidgetItem([key])
|
||||
item = QtWidgets.QTreeWidgetItem([key])
|
||||
self.itemCache.append(item) # PyQt 4.9.6 no longer keeps references to these wrappers,
|
||||
# so we need to make an explicit reference or else the .file
|
||||
# attribute will disappear.
|
||||
|
@ -338,7 +460,6 @@ class ExampleLoader(QtGui.QMainWindow):
|
|||
def currentFile(self):
|
||||
item = self.ui.exampleTree.currentItem()
|
||||
if hasattr(item, 'file'):
|
||||
global path
|
||||
return os.path.join(path, item.file)
|
||||
return None
|
||||
|
||||
|
@ -360,39 +481,67 @@ class ExampleLoader(QtGui.QMainWindow):
|
|||
fn = self.currentFile()
|
||||
if fn is None:
|
||||
return
|
||||
if sys.platform.startswith('win'):
|
||||
args = [os.P_NOWAIT, sys.executable, '"'+sys.executable+'"', '"' + fn + '"']
|
||||
else:
|
||||
args = [os.P_NOWAIT, sys.executable, sys.executable, fn]
|
||||
if env is None:
|
||||
os.spawnl(*args)
|
||||
else:
|
||||
args.append(env)
|
||||
os.spawnle(*args)
|
||||
subprocess.Popen([sys.executable, fn], env=env)
|
||||
|
||||
def showFile(self):
|
||||
fn = self.currentFile()
|
||||
if fn is None:
|
||||
self.ui.codeView.clear()
|
||||
return
|
||||
if os.path.isdir(fn):
|
||||
fn = os.path.join(fn, '__main__.py')
|
||||
text = open(fn).read()
|
||||
text = self.getExampleContent(fn)
|
||||
self.ui.codeView.setPlainText(text)
|
||||
self.ui.loadedFileLabel.setText(fn)
|
||||
self.codeBtn.hide()
|
||||
|
||||
@lru_cache(100)
|
||||
def getExampleContent(self, filename):
|
||||
if filename is None:
|
||||
self.ui.codeView.clear()
|
||||
return
|
||||
if os.path.isdir(filename):
|
||||
filename = os.path.join(filename, '__main__.py')
|
||||
with open(filename, "r") as currentFile:
|
||||
text = currentFile.read()
|
||||
return text
|
||||
|
||||
def codeEdited(self):
|
||||
self.codeBtn.show()
|
||||
|
||||
def runEditedCode(self):
|
||||
self.loadFile(edited=True)
|
||||
|
||||
def keyPressEvent(self, event):
|
||||
ret = super().keyPressEvent(event)
|
||||
if not QtCore.Qt.KeyboardModifier.ControlModifier & event.modifiers():
|
||||
return ret
|
||||
key = event.key()
|
||||
Key = QtCore.Qt.Key
|
||||
|
||||
# Allow quick navigate to search
|
||||
if key == Key.Key_F:
|
||||
self.ui.exampleFilter.setFocus()
|
||||
event.accept()
|
||||
return
|
||||
|
||||
if key not in [Key.Key_Plus, Key.Key_Minus, Key.Key_Underscore, Key.Key_Equal, Key.Key_0]:
|
||||
return ret
|
||||
font = self.ui.codeView.font()
|
||||
oldSize = font.pointSize()
|
||||
if key == Key.Key_Plus or key == Key.Key_Equal:
|
||||
font.setPointSize(oldSize + max(oldSize*.15, 1))
|
||||
elif key == Key.Key_Minus or key == Key.Key_Underscore:
|
||||
newSize = oldSize - max(oldSize*.15, 1)
|
||||
font.setPointSize(max(newSize, 1))
|
||||
elif key == Key.Key_0:
|
||||
# Reset to original size
|
||||
font.setPointSize(10)
|
||||
self.ui.codeView.setFont(font)
|
||||
event.accept()
|
||||
|
||||
def main():
|
||||
app = pg.mkQApp()
|
||||
loader = ExampleLoader()
|
||||
app.exec_()
|
||||
loader.ui.exampleTree.setCurrentIndex(
|
||||
loader.ui.exampleTree.model().index(0,0)
|
||||
)
|
||||
pg.exec()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
|
@ -46,4 +46,4 @@ timer.start(30)
|
|||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pg.mkQApp().exec_()
|
||||
pg.exec()
|
||||
|
|
|
@ -76,4 +76,4 @@ fc.connectTerminals(fNode['Out'], pw2Node['In'])
|
|||
fc.connectTerminals(fNode['Out'], fc['dataOut'])
|
||||
|
||||
if __name__ == '__main__':
|
||||
pg.mkQApp().exec_()
|
||||
pg.exec()
|
||||
|
|
|
@ -151,4 +151,4 @@ fc.connectTerminals(fNode['dataOut'], v2Node['data'])
|
|||
fc.connectTerminals(fNode['dataOut'], fc['dataOut'])
|
||||
|
||||
if __name__ == '__main__':
|
||||
pg.mkQApp().exec_()
|
||||
pg.exec()
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Demonstrate use of GLLinePlotItem to draw cross-sections of a surface.
|
||||
This example demonstrates the use of GLBarGraphItem.
|
||||
|
||||
"""
|
||||
## Add path to library (just for examples; you do not need this)
|
||||
|
@ -13,9 +13,9 @@ import numpy as np
|
|||
|
||||
app = pg.mkQApp("GLBarGraphItem Example")
|
||||
w = gl.GLViewWidget()
|
||||
w.opts['distance'] = 40
|
||||
w.show()
|
||||
w.setWindowTitle('pyqtgraph example: GLBarGraphItem')
|
||||
w.setCameraPosition(distance=40)
|
||||
|
||||
gx = gl.GLGridItem()
|
||||
gx.rotate(90, 0, 1, 0)
|
||||
|
@ -40,4 +40,4 @@ bg = gl.GLBarGraphItem(pos, size)
|
|||
w.addItem(bg)
|
||||
|
||||
if __name__ == '__main__':
|
||||
pg.mkQApp().exec_()
|
||||
pg.exec()
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
import initExample
|
||||
|
||||
import pyqtgraph as pg
|
||||
import pyqtgraph.opengl as gl
|
||||
import numpy
|
||||
|
||||
app = pg.mkQApp()
|
||||
w = gl.GLViewWidget()
|
||||
w.show()
|
||||
w.setWindowTitle("pyqtgraph example: GLGradientLegendItem")
|
||||
w.setCameraPosition(distance=60)
|
||||
|
||||
gx = gl.GLGridItem()
|
||||
gx.rotate(90, 0, 1, 0)
|
||||
w.addItem(gx)
|
||||
|
||||
md = gl.MeshData.cylinder(rows=10, cols=20, radius=[5.0, 5], length=20.0)
|
||||
md._vertexes[:, 2] = md._vertexes[:, 2] - 10
|
||||
|
||||
# set color based on z coordinates
|
||||
color_map = pg.colormap.get("CET-L10")
|
||||
|
||||
h = md.vertexes()[:, 2]
|
||||
# remember these
|
||||
h_max, h_min = h.max(), h.min()
|
||||
h = (h - h_min) / (h_max - h_min)
|
||||
colors = color_map.map(h, mode="float")
|
||||
md.setFaceColors(colors)
|
||||
m = gl.GLMeshItem(meshdata=md, smooth=True)
|
||||
w.addItem(m)
|
||||
|
||||
legendLabels = numpy.linspace(h_max, h_min, 5)
|
||||
legendPos = numpy.linspace(1, 0, 5)
|
||||
legend = dict(zip(map(str, legendLabels), legendPos))
|
||||
|
||||
gll = gl.GLGradientLegendItem(
|
||||
pos=(10, 10), size=(50, 300), gradient=color_map, labels=legend
|
||||
)
|
||||
w.addItem(gll)
|
||||
|
||||
## Start Qt event loop unless running in interactive mode.
|
||||
if __name__ == "__main__":
|
||||
pg.exec()
|
|
@ -0,0 +1,47 @@
|
|||
"""
|
||||
Demonstrates use of GLGraphItem
|
||||
|
||||
"""
|
||||
|
||||
## Add path to library (just for examples; you do not need this)
|
||||
import initExample
|
||||
|
||||
import pyqtgraph as pg
|
||||
import pyqtgraph.opengl as gl
|
||||
import numpy as np
|
||||
|
||||
app = pg.mkQApp("GLGraphItem Example")
|
||||
w = gl.GLViewWidget()
|
||||
w.setCameraPosition(distance=20)
|
||||
w.show()
|
||||
|
||||
edges = np.array([
|
||||
[0, 2],
|
||||
[0, 3],
|
||||
[1, 2],
|
||||
[1, 3],
|
||||
[2, 3]
|
||||
])
|
||||
|
||||
nodes = np.array(
|
||||
[
|
||||
[0, 0, 0],
|
||||
[1, 0, 0],
|
||||
[0, 1, 0],
|
||||
[1, 1, 1]
|
||||
]
|
||||
)
|
||||
|
||||
edgeColor=pg.glColor("w")
|
||||
|
||||
gi = gl.GLGraphItem(
|
||||
edges=edges,
|
||||
nodePositions=nodes,
|
||||
edgeWidth=1.,
|
||||
nodeSize=10.
|
||||
)
|
||||
|
||||
w.addItem(gi)
|
||||
|
||||
if __name__ == "__main__":
|
||||
pg.exec()
|
|
@ -15,9 +15,9 @@ import numpy as np
|
|||
|
||||
app = pg.mkQApp("GLImageItem Example")
|
||||
w = gl.GLViewWidget()
|
||||
w.opts['distance'] = 200
|
||||
w.show()
|
||||
w.setWindowTitle('pyqtgraph example: GLImageItem')
|
||||
w.setCameraPosition(distance=200)
|
||||
|
||||
## create volume data set to slice three images from
|
||||
shape = (100,100,70)
|
||||
|
@ -51,4 +51,4 @@ ax = gl.GLAxisItem()
|
|||
w.addItem(ax)
|
||||
|
||||
if __name__ == '__main__':
|
||||
pg.mkQApp().exec_()
|
||||
pg.exec()
|
||||
|
|
|
@ -7,7 +7,7 @@ This example uses the isosurface function to convert a scalar field
|
|||
## Add path to library (just for examples; you do not need this)
|
||||
import initExample
|
||||
|
||||
from pyqtgraph.Qt import QtCore, QtGui
|
||||
import numpy as np
|
||||
import pyqtgraph as pg
|
||||
import pyqtgraph.opengl as gl
|
||||
|
||||
|
@ -22,23 +22,16 @@ g = gl.GLGridItem()
|
|||
g.scale(2,2,1)
|
||||
w.addItem(g)
|
||||
|
||||
import numpy as np
|
||||
|
||||
## Define a scalar field from which we will generate an isosurface
|
||||
def psi(i, j, k, offset=(25, 25, 50)):
|
||||
x = i-offset[0]
|
||||
y = j-offset[1]
|
||||
z = k-offset[2]
|
||||
th = np.arctan2(z, (x**2+y**2)**0.5)
|
||||
phi = np.arctan2(y, x)
|
||||
r = (x**2 + y**2 + z **2)**0.5
|
||||
th = np.arctan2(z, np.hypot(x, y))
|
||||
r = np.sqrt(x**2 + y**2 + z **2)
|
||||
a0 = 1
|
||||
#ps = (1./81.) * (2./np.pi)**0.5 * (1./a0)**(3/2) * (6 - r/a0) * (r/a0) * np.exp(-r/(3*a0)) * np.cos(th)
|
||||
ps = (1./81.) * 1./(6.*np.pi)**0.5 * (1./a0)**(3/2) * (r/a0)**2 * np.exp(-r/(3*a0)) * (3 * np.cos(th)**2 - 1)
|
||||
|
||||
return ps
|
||||
|
||||
#return ((1./81.) * (1./np.pi)**0.5 * (1./a0)**(3/2) * (r/a0)**2 * (r/a0) * np.exp(-r/(3*a0)) * np.sin(th) * np.cos(th) * np.exp(2 * 1j * phi))**2
|
||||
|
||||
|
||||
print("Generating scalar field..")
|
||||
|
@ -67,4 +60,4 @@ w.addItem(m2)
|
|||
m2.translate(-25, -25, -50)
|
||||
|
||||
if __name__ == '__main__':
|
||||
pg.mkQApp().exec_()
|
||||
pg.exec()
|
||||
|
|
|
@ -6,16 +6,15 @@ Demonstrate use of GLLinePlotItem to draw cross-sections of a surface.
|
|||
## Add path to library (just for examples; you do not need this)
|
||||
import initExample
|
||||
|
||||
from pyqtgraph.Qt import QtCore, QtGui
|
||||
import pyqtgraph.opengl as gl
|
||||
import pyqtgraph as pg
|
||||
import numpy as np
|
||||
|
||||
app = pg.mkQApp("GLLinePlotItem Example")
|
||||
w = gl.GLViewWidget()
|
||||
w.opts['distance'] = 40
|
||||
w.show()
|
||||
w.setWindowTitle('pyqtgraph example: GLLinePlotItem')
|
||||
w.setCameraPosition(distance=40)
|
||||
|
||||
gx = gl.GLGridItem()
|
||||
gx.rotate(90, 0, 1, 0)
|
||||
|
@ -29,19 +28,16 @@ gz = gl.GLGridItem()
|
|||
gz.translate(0, 0, -10)
|
||||
w.addItem(gz)
|
||||
|
||||
def fn(x, y):
|
||||
return np.cos((x**2 + y**2)**0.5)
|
||||
|
||||
n = 51
|
||||
y = np.linspace(-10,10,n)
|
||||
x = np.linspace(-10,10,100)
|
||||
for i in range(n):
|
||||
yi = np.array([y[i]]*100)
|
||||
d = (x**2 + yi**2)**0.5
|
||||
yi = y[i]
|
||||
d = np.hypot(x, yi)
|
||||
z = 10 * np.cos(d) / (d+1)
|
||||
pts = np.vstack([x,yi,z]).transpose()
|
||||
plt = gl.GLLinePlotItem(pos=pts, color=pg.glColor((i,n*1.3)), width=(i+1)/10., antialias=True)
|
||||
pts = np.column_stack([x, np.full_like(x, yi), z])
|
||||
plt = gl.GLLinePlotItem(pos=pts, color=pg.mkColor((i,n*1.3)), width=(i+1)/10., antialias=True)
|
||||
w.addItem(plt)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pg.mkQApp().exec_()
|
||||
pg.exec()
|
||||
|
|
|
@ -118,4 +118,4 @@ w.addItem(m5)
|
|||
w.addItem(m6)
|
||||
|
||||
if __name__ == '__main__':
|
||||
pg.mkQApp().exec_()
|
||||
pg.exec()
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
"""
|
||||
Demonstrate using QPainter on a subclass of GLGraphicsItem.
|
||||
"""
|
||||
## Add path to library (just for examples; you do not need this)
|
||||
import initExample
|
||||
|
||||
import pyqtgraph as pg
|
||||
import pyqtgraph.opengl
|
||||
from pyqtgraph.Qt import QtCore, QtGui
|
||||
import OpenGL.GL as GL
|
||||
|
||||
SIZE = 32
|
||||
|
||||
class GLPainterItem(pg.opengl.GLGraphicsItem.GLGraphicsItem):
|
||||
def __init__(self, **kwds):
|
||||
super().__init__()
|
||||
glopts = kwds.pop('glOptions', 'additive')
|
||||
self.setGLOptions(glopts)
|
||||
|
||||
def compute_projection(self):
|
||||
modelview = GL.glGetDoublev(GL.GL_MODELVIEW_MATRIX)
|
||||
projection = GL.glGetDoublev(GL.GL_PROJECTION_MATRIX)
|
||||
mvp = projection.T @ modelview.T
|
||||
mvp = QtGui.QMatrix4x4(mvp.ravel().tolist())
|
||||
|
||||
# note that QRectF.bottom() != QRect.bottom()
|
||||
rect = QtCore.QRectF(self.view().rect())
|
||||
ndc_to_viewport = QtGui.QMatrix4x4()
|
||||
ndc_to_viewport.viewport(rect.left(), rect.bottom(), rect.width(), -rect.height())
|
||||
|
||||
return ndc_to_viewport * mvp
|
||||
|
||||
def paint(self):
|
||||
self.setupGLState()
|
||||
|
||||
painter = QtGui.QPainter(self.view())
|
||||
self.draw(painter)
|
||||
painter.end()
|
||||
|
||||
def draw(self, painter):
|
||||
painter.setPen(QtCore.Qt.GlobalColor.white)
|
||||
painter.setRenderHints(QtGui.QPainter.RenderHint.Antialiasing | QtGui.QPainter.RenderHint.TextAntialiasing)
|
||||
|
||||
rect = self.view().rect()
|
||||
af = QtCore.Qt.AlignmentFlag
|
||||
|
||||
painter.drawText(rect, af.AlignTop | af.AlignRight, 'TR')
|
||||
painter.drawText(rect, af.AlignBottom | af.AlignLeft, 'BL')
|
||||
painter.drawText(rect, af.AlignBottom | af.AlignRight, 'BR')
|
||||
|
||||
opts = self.view().cameraParams()
|
||||
lines = []
|
||||
center = opts['center']
|
||||
lines.append(f"center : ({center.x():.1f}, {center.y():.1f}, {center.z():.1f})")
|
||||
for key in ['distance', 'fov', 'elevation', 'azimuth']:
|
||||
lines.append(f"{key} : {opts[key]:.1f}")
|
||||
xyz = self.view().cameraPosition()
|
||||
lines.append(f"xyz : ({xyz.x():.1f}, {xyz.y():.1f}, {xyz.z():.1f})")
|
||||
info = "\n".join(lines)
|
||||
painter.drawText(rect, af.AlignTop | af.AlignLeft, info)
|
||||
|
||||
project = self.compute_projection()
|
||||
|
||||
hsize = SIZE // 2
|
||||
for xi in range(-hsize, hsize+1):
|
||||
for yi in range(-hsize, hsize+1):
|
||||
if xi == -hsize and yi == -hsize:
|
||||
# skip one corner for visual orientation
|
||||
continue
|
||||
vec3 = QtGui.QVector3D(xi, yi, 0)
|
||||
pos = project.map(vec3).toPointF()
|
||||
painter.drawEllipse(pos, 1, 1)
|
||||
|
||||
|
||||
pg.mkQApp("GLPainterItem Example")
|
||||
glv = pg.opengl.GLViewWidget()
|
||||
glv.show()
|
||||
glv.setWindowTitle('pyqtgraph example: GLPainterItem')
|
||||
glv.setCameraPosition(distance=50, elevation=90, azimuth=0)
|
||||
|
||||
griditem = pg.opengl.GLGridItem()
|
||||
griditem.setSize(SIZE, SIZE)
|
||||
griditem.setSpacing(1, 1)
|
||||
glv.addItem(griditem)
|
||||
|
||||
axisitem = pg.opengl.GLAxisItem()
|
||||
axisitem.setSize(SIZE/2, SIZE/2, 1)
|
||||
glv.addItem(axisitem)
|
||||
|
||||
paintitem = GLPainterItem()
|
||||
glv.addItem(paintitem)
|
||||
|
||||
if __name__ == '__main__':
|
||||
pg.exec()
|
|
@ -8,15 +8,16 @@ Demonstrates use of GLScatterPlotItem with rapidly-updating plots.
|
|||
import initExample
|
||||
|
||||
import pyqtgraph as pg
|
||||
from pyqtgraph import functions as fn
|
||||
from pyqtgraph.Qt import QtCore, QtGui
|
||||
import pyqtgraph.opengl as gl
|
||||
import numpy as np
|
||||
|
||||
app = pg.mkQApp("GLScatterPlotItem Example")
|
||||
w = gl.GLViewWidget()
|
||||
w.opts['distance'] = 20
|
||||
w.show()
|
||||
w.setWindowTitle('pyqtgraph example: GLScatterPlotItem')
|
||||
w.setCameraPosition(distance=20)
|
||||
|
||||
g = gl.GLGridItem()
|
||||
w.addItem(g)
|
||||
|
@ -84,10 +85,10 @@ def update():
|
|||
global phase, sp2, d2
|
||||
s = -np.cos(d2*2+phase)
|
||||
color = np.empty((len(d2),4), dtype=np.float32)
|
||||
color[:,3] = np.clip(s * 0.1, 0, 1)
|
||||
color[:,0] = np.clip(s * 3.0, 0, 1)
|
||||
color[:,1] = np.clip(s * 1.0, 0, 1)
|
||||
color[:,2] = np.clip(s ** 3, 0, 1)
|
||||
color[:,3] = fn.clip_array(s * 0.1, 0., 1.)
|
||||
color[:,0] = fn.clip_array(s * 3.0, 0., 1.)
|
||||
color[:,1] = fn.clip_array(s * 1.0, 0., 1.)
|
||||
color[:,2] = fn.clip_array(s ** 3, 0., 1.)
|
||||
sp2.setData(color=color)
|
||||
phase -= 0.1
|
||||
|
||||
|
@ -107,4 +108,4 @@ t.timeout.connect(update)
|
|||
t.start(50)
|
||||
|
||||
if __name__ == '__main__':
|
||||
pg.mkQApp().exec_()
|
||||
pg.exec()
|
||||
|
|
|
@ -93,4 +93,4 @@ timer.timeout.connect(update)
|
|||
timer.start(30)
|
||||
|
||||
if __name__ == '__main__':
|
||||
pg.mkQApp().exec_()
|
||||
pg.exec()
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Simple examples demonstrating the use of GLTextItem.
|
||||
|
||||
"""
|
||||
|
||||
import initExample
|
||||
|
||||
import pyqtgraph as pg
|
||||
from pyqtgraph.Qt import QtCore, QtGui, mkQApp
|
||||
import pyqtgraph.opengl as gl
|
||||
|
||||
app = mkQApp("GLTextItem Example")
|
||||
|
||||
gvw = gl.GLViewWidget()
|
||||
gvw.show()
|
||||
gvw.setWindowTitle('pyqtgraph example: GLTextItem')
|
||||
|
||||
griditem = gl.GLGridItem()
|
||||
griditem.setSize(10, 10)
|
||||
griditem.setSpacing(1, 1)
|
||||
gvw.addItem(griditem)
|
||||
|
||||
axisitem = gl.GLAxisItem()
|
||||
gvw.addItem(axisitem)
|
||||
|
||||
txtitem1 = gl.GLTextItem(pos=(0.0, 0.0, 0.0), text='text1')
|
||||
gvw.addItem(txtitem1)
|
||||
|
||||
txtitem2 = gl.GLTextItem()
|
||||
txtitem2.setData(pos=(1.0, -1.0, 2.0), color=(127, 255, 127, 255), text='text2')
|
||||
gvw.addItem(txtitem2)
|
||||
|
||||
if __name__ == '__main__':
|
||||
pg.exec()
|
|
@ -6,14 +6,14 @@ Very basic 3D graphics example; create a view widget and add a few items.
|
|||
## Add path to library (just for examples; you do not need this)
|
||||
import initExample
|
||||
|
||||
from pyqtgraph.Qt import QtCore, QtGui, mkQApp
|
||||
import pyqtgraph as pg
|
||||
import pyqtgraph.opengl as gl
|
||||
|
||||
app = mkQApp("GLViewWidget Example")
|
||||
pg.mkQApp("GLViewWidget Example")
|
||||
w = gl.GLViewWidget()
|
||||
w.opts['distance'] = 20
|
||||
w.show()
|
||||
w.setWindowTitle('pyqtgraph example: GLViewWidget')
|
||||
w.setCameraPosition(distance=20)
|
||||
|
||||
ax = gl.GLAxisItem()
|
||||
ax.setSize(5,5,5)
|
||||
|
@ -28,4 +28,4 @@ ax2.setParentItem(b)
|
|||
b.translate(1,1,1)
|
||||
|
||||
if __name__ == '__main__':
|
||||
pg.mkQApp().exec_()
|
||||
pg.exec()
|
||||
|
|
|
@ -7,47 +7,66 @@ Demonstrates GLVolumeItem for displaying volumetric data.
|
|||
## Add path to library (just for examples; you do not need this)
|
||||
import initExample
|
||||
|
||||
import numpy as np
|
||||
import pyqtgraph as pg
|
||||
from pyqtgraph.Qt import QtCore, QtGui
|
||||
import pyqtgraph.opengl as gl
|
||||
from pyqtgraph import functions as fn
|
||||
|
||||
app = pg.mkQApp("GLVolumeItem Example")
|
||||
w = gl.GLViewWidget()
|
||||
w.opts['distance'] = 200
|
||||
w.show()
|
||||
w.setWindowTitle('pyqtgraph example: GLVolumeItem')
|
||||
w.setCameraPosition(distance=200)
|
||||
|
||||
#b = gl.GLBoxItem()
|
||||
#w.addItem(b)
|
||||
g = gl.GLGridItem()
|
||||
g.scale(10, 10, 1)
|
||||
w.addItem(g)
|
||||
|
||||
import numpy as np
|
||||
## Hydrogen electron probability density
|
||||
def psi(i, j, k, offset=(50,50,100)):
|
||||
x = i-offset[0]
|
||||
y = j-offset[1]
|
||||
z = k-offset[2]
|
||||
th = np.arctan2(z, (x**2+y**2)**0.5)
|
||||
phi = np.arctan2(y, x)
|
||||
r = (x**2 + y**2 + z **2)**0.5
|
||||
th = np.arctan2(z, np.hypot(x, y))
|
||||
r = np.sqrt(x**2 + y**2 + z **2)
|
||||
a0 = 2
|
||||
#ps = (1./81.) * (2./np.pi)**0.5 * (1./a0)**(3/2) * (6 - r/a0) * (r/a0) * np.exp(-r/(3*a0)) * np.cos(th)
|
||||
ps = (1./81.) * 1./(6.*np.pi)**0.5 * (1./a0)**(3/2) * (r/a0)**2 * np.exp(-r/(3*a0)) * (3 * np.cos(th)**2 - 1)
|
||||
|
||||
return ps
|
||||
|
||||
#return ((1./81.) * (1./np.pi)**0.5 * (1./a0)**(3/2) * (r/a0)**2 * (r/a0) * np.exp(-r/(3*a0)) * np.sin(th) * np.cos(th) * np.exp(2 * 1j * phi))**2
|
||||
return (
|
||||
(1.0 / 81.0)
|
||||
* 1.0 / (6.0 * np.pi) ** 0.5
|
||||
* (1.0 / a0) ** (3 / 2)
|
||||
* (r / a0) ** 2
|
||||
* np.exp(-r / (3 * a0))
|
||||
* (3 * np.cos(th) ** 2 - 1)
|
||||
)
|
||||
|
||||
|
||||
data = np.fromfunction(psi, (100,100,200))
|
||||
positive = np.log(np.clip(data, 0, data.max())**2)
|
||||
negative = np.log(np.clip(-data, 0, -data.min())**2)
|
||||
with np.errstate(divide = 'ignore'):
|
||||
positive = np.log(fn.clip_array(data, 0, data.max())**2)
|
||||
negative = np.log(fn.clip_array(-data, 0, -data.min())**2)
|
||||
|
||||
d2 = np.empty(data.shape + (4,), dtype=np.ubyte)
|
||||
d2[..., 0] = positive * (255./positive.max())
|
||||
d2[..., 1] = negative * (255./negative.max())
|
||||
|
||||
# Original Code
|
||||
# d2[..., 0] = positive * (255./positive.max())
|
||||
# d2[..., 1] = negative * (255./negative.max())
|
||||
|
||||
# Reformulated Code
|
||||
# Both positive.max() and negative.max() are negative-valued.
|
||||
# Thus the next 2 lines are _not_ bounded to [0, 255]
|
||||
positive = positive * (255./positive.max())
|
||||
negative = negative * (255./negative.max())
|
||||
# When casting to ubyte, the original code relied on +Inf to be
|
||||
# converted to 0. On arm64, it gets converted to 255.
|
||||
# Thus the next 2 lines change +Inf explicitly to 0 instead.
|
||||
positive[np.isinf(positive)] = 0
|
||||
negative[np.isinf(negative)] = 0
|
||||
# When casting to ubyte, the original code relied on the conversion
|
||||
# to do modulo 256. The next 2 lines do it explicitly instead as
|
||||
# documentation.
|
||||
d2[..., 0] = positive.astype(int) % 256
|
||||
d2[..., 1] = negative.astype(int) % 256
|
||||
|
||||
d2[..., 2] = d2[...,1]
|
||||
d2[..., 3] = d2[..., 0]*0.3 + d2[..., 1]*0.3
|
||||
d2[..., 3] = (d2[..., 3].astype(float) / 255.) **2 * 255
|
||||
|
@ -64,4 +83,4 @@ ax = gl.GLAxisItem()
|
|||
w.addItem(ax)
|
||||
|
||||
if __name__ == '__main__':
|
||||
pg.mkQApp().exec_()
|
||||
pg.exec()
|
||||
|
|
|
@ -9,6 +9,7 @@ used to affect the appearance of a surface.
|
|||
## Add path to library (just for examples; you do not need this)
|
||||
import initExample
|
||||
|
||||
import numpy as np
|
||||
from pyqtgraph.Qt import QtCore, QtGui
|
||||
import pyqtgraph as pg
|
||||
import pyqtgraph.opengl as gl
|
||||
|
@ -23,9 +24,6 @@ g = gl.GLGridItem()
|
|||
g.scale(2,2,1)
|
||||
w.addItem(g)
|
||||
|
||||
import numpy as np
|
||||
|
||||
|
||||
md = gl.MeshData.sphere(rows=10, cols=20)
|
||||
x = np.linspace(-8, 8, 6)
|
||||
|
||||
|
@ -102,4 +100,4 @@ w.addItem(m6)
|
|||
#m2.translate(-25, -25, -50)
|
||||
|
||||
if __name__ == '__main__':
|
||||
pg.mkQApp().exec_()
|
||||
pg.exec()
|
||||
|
|
|
@ -21,4 +21,4 @@ ge = pg.GradientEditorItem()
|
|||
mw.setCentralItem(ge)
|
||||
|
||||
if __name__ == '__main__':
|
||||
pg.mkQApp().exec_()
|
||||
pg.exec()
|
||||
|
|
|
@ -48,7 +48,7 @@ l.addWidget(w4, 1, 0)
|
|||
l.addWidget(label, 1, 1)
|
||||
|
||||
if __name__ == '__main__':
|
||||
pg.mkQApp().exec_()
|
||||
pg.exec()
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -58,4 +58,4 @@ lines = np.array([
|
|||
g.setData(pos=pos, adj=adj, pen=lines, size=1, symbol=symbols, pxMode=False)
|
||||
|
||||
if __name__ == '__main__':
|
||||
pg.mkQApp().exec_()
|
||||
pg.exec()
|
||||
|
|
|
@ -79,4 +79,4 @@ p4.plot([1,3,2,4,3,5])
|
|||
p5.plot([1,3,2,4,3,5])
|
||||
|
||||
if __name__ == '__main__':
|
||||
pg.mkQApp().exec_()
|
||||
pg.exec()
|
||||
|
|
|
@ -59,4 +59,4 @@ g = pg.GridItem()
|
|||
vb.addItem(g)
|
||||
|
||||
if __name__ == '__main__':
|
||||
pg.mkQApp().exec_()
|
||||
pg.exec()
|
||||
|
|
|
@ -3,45 +3,49 @@
|
|||
Use a HistogramLUTWidget to control the contrast / coloration of an image.
|
||||
"""
|
||||
|
||||
## Add path to library (just for examples; you do not need this)
|
||||
# Add path to library (just for examples; you do not need this)
|
||||
import initExample
|
||||
|
||||
import numpy as np
|
||||
from pyqtgraph.Qt import QtGui, QtCore
|
||||
import pyqtgraph as pg
|
||||
|
||||
import pyqtgraph as pg
|
||||
from pyqtgraph.Qt import QtGui
|
||||
|
||||
app = pg.mkQApp("Histogram Lookup Table Example")
|
||||
win = QtGui.QMainWindow()
|
||||
win.resize(800,600)
|
||||
win.resize(880, 600)
|
||||
win.show()
|
||||
win.setWindowTitle('pyqtgraph example: Histogram LUT')
|
||||
|
||||
cw = QtGui.QWidget()
|
||||
win.setCentralWidget(cw)
|
||||
|
||||
l = QtGui.QGridLayout()
|
||||
cw.setLayout(l)
|
||||
l.setSpacing(0)
|
||||
layout = QtGui.QGridLayout()
|
||||
cw.setLayout(layout)
|
||||
layout.setSpacing(0)
|
||||
|
||||
v = pg.GraphicsView()
|
||||
view = pg.GraphicsView()
|
||||
vb = pg.ViewBox()
|
||||
vb.setAspectLocked()
|
||||
v.setCentralItem(vb)
|
||||
l.addWidget(v, 0, 0, 3, 1)
|
||||
view.setCentralItem(vb)
|
||||
layout.addWidget(view, 0, 1, 3, 1)
|
||||
|
||||
hist = pg.HistogramLUTWidget(gradientPosition="left")
|
||||
layout.addWidget(hist, 0, 2)
|
||||
|
||||
w = pg.HistogramLUTWidget()
|
||||
l.addWidget(w, 0, 1)
|
||||
|
||||
monoRadio = QtGui.QRadioButton('mono')
|
||||
rgbaRadio = QtGui.QRadioButton('rgba')
|
||||
l.addWidget(monoRadio, 1, 1)
|
||||
l.addWidget(rgbaRadio, 2, 1)
|
||||
layout.addWidget(monoRadio, 1, 2)
|
||||
layout.addWidget(rgbaRadio, 2, 2)
|
||||
monoRadio.setChecked(True)
|
||||
|
||||
|
||||
def setLevelMode():
|
||||
mode = 'mono' if monoRadio.isChecked() else 'rgba'
|
||||
w.setLevelMode(mode)
|
||||
hist.setLevelMode(mode)
|
||||
|
||||
|
||||
monoRadio.toggled.connect(setLevelMode)
|
||||
|
||||
data = pg.gaussianFilter(np.random.normal(size=(256, 256, 3)), (20, 20, 0))
|
||||
|
@ -52,7 +56,7 @@ img = pg.ImageItem(data)
|
|||
vb.addItem(img)
|
||||
vb.autoRange()
|
||||
|
||||
w.setImageItem(img)
|
||||
hist.setImageItem(img)
|
||||
|
||||
if __name__ == '__main__':
|
||||
pg.mkQApp().exec_()
|
||||
pg.exec()
|
||||
|
|
|
@ -9,7 +9,7 @@ import initExample
|
|||
from pyqtgraph.Qt import QtCore, QtGui
|
||||
import numpy as np
|
||||
import pyqtgraph as pg
|
||||
import pyqtgraph.ptime as ptime
|
||||
from time import perf_counter
|
||||
|
||||
app = pg.mkQApp("ImageItem Example")
|
||||
|
||||
|
@ -33,30 +33,30 @@ view.setRange(QtCore.QRectF(0, 0, 600, 600))
|
|||
data = np.random.normal(size=(15, 600, 600), loc=1024, scale=64).astype(np.uint16)
|
||||
i = 0
|
||||
|
||||
updateTime = ptime.time()
|
||||
fps = 0
|
||||
updateTime = perf_counter()
|
||||
elapsed = 0
|
||||
|
||||
timer = QtCore.QTimer()
|
||||
timer.setSingleShot(True)
|
||||
# not using QTimer.singleShot() because of persistence on PyQt. see PR #1605
|
||||
|
||||
def updateData():
|
||||
global img, data, i, updateTime, fps
|
||||
global img, data, i, updateTime, elapsed
|
||||
|
||||
## Display the data
|
||||
img.setImage(data[i])
|
||||
i = (i+1) % data.shape[0]
|
||||
|
||||
timer.start(1)
|
||||
now = ptime.time()
|
||||
fps2 = 1.0 / (now-updateTime)
|
||||
now = perf_counter()
|
||||
elapsed_now = now - updateTime
|
||||
updateTime = now
|
||||
fps = fps * 0.9 + fps2 * 0.1
|
||||
|
||||
#print "%0.1f fps" % fps
|
||||
elapsed = elapsed * 0.9 + elapsed_now * 0.1
|
||||
|
||||
# print(f"{1 / elapsed:.1f} fps")
|
||||
|
||||
timer.timeout.connect(updateData)
|
||||
updateData()
|
||||
|
||||
if __name__ == '__main__':
|
||||
pg.mkQApp().exec_()
|
||||
pg.exec()
|
||||
|
|
|
@ -64,4 +64,4 @@ imv.ui.roiBtn.setChecked(True)
|
|||
imv.roiClicked()
|
||||
|
||||
if __name__ == '__main__':
|
||||
pg.mkQApp().exec_()
|
||||
pg.exec()
|
||||
|
|
|
@ -32,10 +32,54 @@ p1.addItem(inf1)
|
|||
p1.addItem(inf2)
|
||||
p1.addItem(inf3)
|
||||
|
||||
targetItem1 = pg.TargetItem()
|
||||
|
||||
targetItem2 = pg.TargetItem(
|
||||
pos=(30, 5),
|
||||
size=20,
|
||||
symbol="star",
|
||||
pen="#F4511E",
|
||||
label="vert={1:0.2f}",
|
||||
labelOpts={
|
||||
"offset": QtCore.QPoint(15, 15)
|
||||
}
|
||||
)
|
||||
targetItem2.label().setAngle(45)
|
||||
|
||||
targetItem3 = pg.TargetItem(
|
||||
pos=(10, 10),
|
||||
size=10,
|
||||
symbol="x",
|
||||
pen="#00ACC1",
|
||||
)
|
||||
targetItem3.setLabel(
|
||||
"Third Label",
|
||||
{
|
||||
"anchor": QtCore.QPointF(0.5, 0.5),
|
||||
"offset": QtCore.QPointF(30, 0),
|
||||
"color": "#558B2F",
|
||||
"rotateAxis": (0, 1)
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def callableFunction(x, y):
|
||||
return f"Square Values: ({x**2:.4f}, {y**2:.4f})"
|
||||
|
||||
targetItem4 = pg.TargetItem(
|
||||
pos=(10, -10),
|
||||
label=callableFunction
|
||||
)
|
||||
|
||||
p1.addItem(targetItem1)
|
||||
p1.addItem(targetItem2)
|
||||
p1.addItem(targetItem3)
|
||||
p1.addItem(targetItem4)
|
||||
|
||||
# Add a linear region with a label
|
||||
lr = pg.LinearRegionItem(values=[70, 80])
|
||||
p1.addItem(lr)
|
||||
label = pg.InfLineLabel(lr.lines[1], "region 1", position=0.95, rotateAxis=(1,0), anchor=(1, 1))
|
||||
|
||||
if __name__ == '__main__':
|
||||
pg.mkQApp().exec_()
|
||||
pg.exec()
|
||||
|
|
|
@ -47,4 +47,4 @@ timer.timeout.connect(update)
|
|||
timer.start(30)
|
||||
|
||||
if __name__ == '__main__':
|
||||
pg.mkQApp().exec_()
|
||||
pg.exec()
|
||||
|
|
|
@ -40,4 +40,4 @@ legend.addItem(c2, 'curve2')
|
|||
legend.addItem(s1, 'scatter')
|
||||
|
||||
if __name__ == '__main__':
|
||||
pg.mkQApp().exec_()
|
||||
pg.exec()
|
||||
|
|
|
@ -31,4 +31,4 @@ p5.setLogMode(x=True, y=False)
|
|||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pg.mkQApp().exec_()
|
||||
pg.exec()
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
This example demonstrates ViewBox and AxisItem configuration to plot a correlation matrix.
|
||||
"""
|
||||
## Add path to library (just for examples; you do not need this)
|
||||
import initExample
|
||||
|
||||
import numpy as np
|
||||
import pyqtgraph as pg
|
||||
from pyqtgraph.Qt import QtWidgets, mkQApp, QtGui
|
||||
|
||||
class MainWindow(QtWidgets.QMainWindow):
|
||||
""" example application main window """
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(MainWindow, self).__init__(*args, **kwargs)
|
||||
gr_wid = pg.GraphicsLayoutWidget(show=True)
|
||||
self.setCentralWidget(gr_wid)
|
||||
self.setWindowTitle('pyqtgraph example: Correlation matrix display')
|
||||
self.resize(600,500)
|
||||
self.show()
|
||||
|
||||
corrMatrix = np.array([
|
||||
[ 1. , 0.5184571 , -0.70188642],
|
||||
[ 0.5184571 , 1. , -0.86094096],
|
||||
[-0.70188642, -0.86094096, 1. ]
|
||||
])
|
||||
columns = ["A", "B", "C"]
|
||||
|
||||
pg.setConfigOption('imageAxisOrder', 'row-major') # Switch default order to Row-major
|
||||
|
||||
correlogram = pg.ImageItem()
|
||||
# create transform to center the corner element on the origin, for any assigned image:
|
||||
tr = QtGui.QTransform().translate(-0.5, -0.5)
|
||||
correlogram.setTransform(tr)
|
||||
correlogram.setImage(corrMatrix)
|
||||
|
||||
plotItem = gr_wid.addPlot() # add PlotItem to the main GraphicsLayoutWidget
|
||||
plotItem.invertY(True) # orient y axis to run top-to-bottom
|
||||
plotItem.setDefaultPadding(0.0) # plot without padding data range
|
||||
plotItem.addItem(correlogram) # display correlogram
|
||||
|
||||
# show full frame, label tick marks at top and left sides, with some extra space for labels:
|
||||
plotItem.showAxes( True, showValues=(True, True, False, False), size=20 )
|
||||
|
||||
# define major tick marks and labels:
|
||||
ticks = [ (idx, label) for idx, label in enumerate( columns ) ]
|
||||
for side in ('left','top','right','bottom'):
|
||||
plotItem.getAxis(side).setTicks( (ticks, []) ) # add list of major ticks; no minor ticks
|
||||
plotItem.getAxis('bottom').setHeight(10) # include some additional space at bottom of figure
|
||||
|
||||
colorMap = pg.colormap.get("CET-D1") # choose perceptually uniform, diverging color map
|
||||
# generate an adjustabled color bar, initially spanning -1 to 1:
|
||||
bar = pg.ColorBarItem( values=(-1,1), cmap=colorMap)
|
||||
# link color bar and color map to correlogram, and show it in plotItem:
|
||||
bar.setImageItem(correlogram, insert_in=plotItem)
|
||||
|
||||
mkQApp("Correlation matrix display")
|
||||
main_window = MainWindow()
|
||||
|
||||
## Start Qt event loop
|
||||
if __name__ == '__main__':
|
||||
pg.exec()
|
|
@ -31,4 +31,4 @@ for c in curves:
|
|||
c.sigClicked.connect(plotClicked)
|
||||
|
||||
if __name__ == '__main__':
|
||||
pg.mkQApp().exec_()
|
||||
pg.exec()
|
||||
|
|
|
@ -11,7 +11,9 @@ import initExample
|
|||
from pyqtgraph.Qt import QtGui, QtCore
|
||||
import numpy as np
|
||||
import pyqtgraph as pg
|
||||
from pyqtgraph.ptime import time
|
||||
|
||||
from time import perf_counter
|
||||
# pg.setConfigOptions(useOpenGL=True)
|
||||
app = pg.mkQApp("MultiPlot Speed Test")
|
||||
|
||||
plot = pg.plot()
|
||||
|
@ -22,7 +24,7 @@ nPlots = 100
|
|||
nSamples = 500
|
||||
curves = []
|
||||
for idx in range(nPlots):
|
||||
curve = pg.PlotCurveItem(pen=(idx,nPlots*1.3))
|
||||
curve = pg.PlotCurveItem(pen=({'color': (idx, nPlots*1.3), 'width': 1}), skipFiniteCheck=True)
|
||||
plot.addItem(curve)
|
||||
curve.setPos(0,idx*6)
|
||||
curves.append(curve)
|
||||
|
@ -37,7 +39,7 @@ plot.addItem(rgn)
|
|||
|
||||
data = np.random.normal(size=(nPlots*23,nSamples))
|
||||
ptr = 0
|
||||
lastTime = time()
|
||||
lastTime = perf_counter()
|
||||
fps = None
|
||||
count = 0
|
||||
def update():
|
||||
|
@ -48,7 +50,7 @@ def update():
|
|||
curves[i].setData(data[(ptr+i)%data.shape[0]])
|
||||
|
||||
ptr += nPlots
|
||||
now = time()
|
||||
now = perf_counter()
|
||||
dt = now - lastTime
|
||||
lastTime = now
|
||||
if fps is None:
|
||||
|
@ -63,4 +65,4 @@ timer.timeout.connect(update)
|
|||
timer.start(0)
|
||||
|
||||
if __name__ == '__main__':
|
||||
pg.mkQApp().exec_()
|
||||
pg.exec()
|
||||
|
|
|
@ -33,5 +33,5 @@ ma = MetaArray(data, info=[
|
|||
pw.plot(ma, pen='y')
|
||||
|
||||
if __name__ == '__main__':
|
||||
pg.mkQApp().exec_()
|
||||
pg.exec()
|
||||
|
||||
|
|
|
@ -61,4 +61,4 @@ p2.addItem(pg.PlotCurveItem([10,20,40,80,40,20], pen='b'))
|
|||
p3.addItem(pg.PlotCurveItem([3200,1600,800,400,200,100], pen='r'))
|
||||
|
||||
if __name__ == '__main__':
|
||||
pg.mkQApp().exec_()
|
||||
pg.exec()
|
||||
|
|
|
@ -52,10 +52,11 @@ win.setWindowTitle('pyqtgraph example: Non-uniform Image')
|
|||
|
||||
p = cw.addPlot(title="Power Losses [W]", row=0, col=0)
|
||||
|
||||
lut = pg.HistogramLUTItem()
|
||||
lut = pg.HistogramLUTItem(orientation="horizontal")
|
||||
|
||||
p.setMouseEnabled(x=False, y=False)
|
||||
|
||||
cw.nextRow()
|
||||
cw.addItem(lut)
|
||||
|
||||
# load the gradient
|
||||
|
@ -79,4 +80,4 @@ p.axes['bottom']['item'].setZValue(1000)
|
|||
p.axes['left']['item'].setZValue(1000)
|
||||
|
||||
if __name__ == '__main__':
|
||||
pg.mkQApp().exec_()
|
||||
pg.exec()
|
||||
|
|
|
@ -6,10 +6,9 @@ Demonstrates very basic use of PColorMeshItem
|
|||
## Add path to library (just for examples; you do not need this)
|
||||
import initExample
|
||||
|
||||
from pyqtgraph.Qt import QtCore, QtGui
|
||||
from pyqtgraph.Qt import QtCore
|
||||
import numpy as np
|
||||
import pyqtgraph as pg
|
||||
import pyqtgraph.ptime as ptime
|
||||
|
||||
app = pg.mkQApp("PColorMesh Example")
|
||||
|
||||
|
@ -84,4 +83,4 @@ timer.timeout.connect(updateData)
|
|||
updateData()
|
||||
|
||||
if __name__ == '__main__':
|
||||
pg.mkQApp().exec_()
|
||||
pg.exec()
|
||||
|
|
|
@ -31,4 +31,4 @@ timer.timeout.connect(update)
|
|||
timer.start(50)
|
||||
|
||||
if __name__ == '__main__':
|
||||
pg.mkQApp().exec_()
|
||||
pg.exec()
|
||||
|
|
|
@ -41,4 +41,4 @@ timer.timeout.connect(update)
|
|||
timer.start(50)
|
||||
|
||||
if __name__ == '__main__':
|
||||
pg.mkQApp().exec_()
|
||||
pg.exec()
|
||||
|
|
|
@ -7,46 +7,200 @@ Update a simple plot as rapidly as possible to measure speed.
|
|||
## Add path to library (just for examples; you do not need this)
|
||||
import initExample
|
||||
|
||||
|
||||
from pyqtgraph.Qt import QtGui, QtCore
|
||||
from collections import deque
|
||||
from pyqtgraph.Qt import QtCore, QtGui, QtWidgets, QT_LIB
|
||||
import numpy as np
|
||||
import pyqtgraph as pg
|
||||
from pyqtgraph.ptime import time
|
||||
from time import perf_counter
|
||||
import pyqtgraph.parametertree as ptree
|
||||
import pyqtgraph.functions as fn
|
||||
import itertools
|
||||
import argparse
|
||||
|
||||
if QT_LIB.startswith('PyQt'):
|
||||
wrapinstance = pg.Qt.sip.wrapinstance
|
||||
else:
|
||||
wrapinstance = pg.Qt.shiboken.wrapInstance
|
||||
|
||||
# defaults here result in the same configuration as the original PlotSpeedTest
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('--noise', dest='noise', action='store_true')
|
||||
parser.add_argument('--no-noise', dest='noise', action='store_false')
|
||||
parser.set_defaults(noise=True)
|
||||
parser.add_argument('--nsamples', default=5000, type=int)
|
||||
parser.add_argument('--frames', default=50, type=int)
|
||||
parser.add_argument('--fsample', default=1000, type=float)
|
||||
parser.add_argument('--frequency', default=0, type=float)
|
||||
parser.add_argument('--amplitude', default=5, type=float)
|
||||
parser.add_argument('--opengl', dest='use_opengl', action='store_true')
|
||||
parser.add_argument('--no-opengl', dest='use_opengl', action='store_false')
|
||||
parser.set_defaults(use_opengl=None)
|
||||
parser.add_argument('--allow-opengl-toggle', action='store_true',
|
||||
help="""Allow on-the-fly change of OpenGL setting. This may cause unwanted side effects.
|
||||
""")
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.use_opengl is not None:
|
||||
pg.setConfigOption('useOpenGL', args.use_opengl)
|
||||
pg.setConfigOption('enableExperimental', args.use_opengl)
|
||||
|
||||
# don't limit frame rate to vsync
|
||||
sfmt = QtGui.QSurfaceFormat()
|
||||
sfmt.setSwapInterval(0)
|
||||
QtGui.QSurfaceFormat.setDefaultFormat(sfmt)
|
||||
|
||||
class LineInstances:
|
||||
def __init__(self):
|
||||
self.alloc(0)
|
||||
|
||||
def alloc(self, size):
|
||||
self.arr = np.empty((size, 4), dtype=np.float64)
|
||||
self.ptrs = list(map(wrapinstance,
|
||||
itertools.count(self.arr.ctypes.data, self.arr.strides[0]),
|
||||
itertools.repeat(QtCore.QLineF, self.arr.shape[0])))
|
||||
|
||||
def array(self, size):
|
||||
if size > self.arr.shape[0]:
|
||||
self.alloc(size + 16)
|
||||
return self.arr[:size]
|
||||
|
||||
def instances(self, size):
|
||||
return self.ptrs[:size]
|
||||
|
||||
class MonkeyCurveItem(pg.PlotCurveItem):
|
||||
def __init__(self, *args, **kwds):
|
||||
super().__init__(*args, **kwds)
|
||||
self.monkey_mode = ''
|
||||
self._lineInstances = LineInstances()
|
||||
|
||||
def setMethod(self, param, value):
|
||||
self.monkey_mode = value
|
||||
|
||||
def paint(self, painter, opt, widget):
|
||||
if self.monkey_mode not in ['drawPolyline', 'drawLines']:
|
||||
return super().paint(painter, opt, widget)
|
||||
|
||||
painter.setRenderHint(painter.RenderHint.Antialiasing, self.opts['antialias'])
|
||||
painter.setPen(pg.mkPen(self.opts['pen']))
|
||||
|
||||
if self.monkey_mode == 'drawPolyline':
|
||||
painter.drawPolyline(fn.arrayToQPolygonF(self.xData, self.yData))
|
||||
elif self.monkey_mode == 'drawLines':
|
||||
lines = self._lineInstances
|
||||
npts = len(self.xData)
|
||||
even_slice = slice(0, 0+(npts-0)//2*2)
|
||||
odd_slice = slice(1, 1+(npts-1)//2*2)
|
||||
for sl in [even_slice, odd_slice]:
|
||||
npairs = (sl.stop - sl.start) // 2
|
||||
memory = lines.array(npairs).reshape((-1, 2))
|
||||
memory[:, 0] = self.xData[sl]
|
||||
memory[:, 1] = self.yData[sl]
|
||||
painter.drawLines(lines.instances(npairs))
|
||||
|
||||
app = pg.mkQApp("Plot Speed Test")
|
||||
|
||||
p = pg.plot()
|
||||
p.setWindowTitle('pyqtgraph example: PlotSpeedTest')
|
||||
p.setRange(QtCore.QRectF(0, -10, 5000, 20))
|
||||
p.setLabel('bottom', 'Index', units='B')
|
||||
curve = p.plot()
|
||||
default_pen = pg.mkPen()
|
||||
|
||||
#curve.setFillBrush((0, 0, 100, 100))
|
||||
#curve.setFillLevel(0)
|
||||
children = [
|
||||
dict(name='sigopts', title='Signal Options', type='group', children=[
|
||||
dict(name='noise', type='bool', value=args.noise),
|
||||
dict(name='nsamples', type='int', limits=[0, None], value=args.nsamples),
|
||||
dict(name='frames', type='int', limits=[1, None], value=args.frames),
|
||||
dict(name='fsample', title='sample rate', type='float', value=args.fsample, units='Hz'),
|
||||
dict(name='frequency', type='float', value=args.frequency, units='Hz'),
|
||||
dict(name='amplitude', type='float', value=args.amplitude),
|
||||
]),
|
||||
dict(name='useOpenGL', type='bool', value=pg.getConfigOption('useOpenGL'),
|
||||
readonly=not args.allow_opengl_toggle),
|
||||
dict(name='enableExperimental', type='bool', value=pg.getConfigOption('enableExperimental')),
|
||||
dict(name='pen', type='pen', value=default_pen),
|
||||
dict(name='antialias', type='bool', value=pg.getConfigOption('antialias')),
|
||||
dict(name='connect', type='list', limits=['all', 'pairs', 'finite', 'array'], value='all'),
|
||||
dict(name='skipFiniteCheck', type='bool', value=False),
|
||||
dict(name='plotMethod', title='Plot Method', type='list', limits=['pyqtgraph', 'drawPolyline', 'drawLines'])
|
||||
]
|
||||
|
||||
#lr = pg.LinearRegionItem([100, 4900])
|
||||
#p.addItem(lr)
|
||||
params = ptree.Parameter.create(name='Parameters', type='group', children=children)
|
||||
pt = ptree.ParameterTree(showHeader=False)
|
||||
pt.setParameters(params)
|
||||
pw = pg.PlotWidget()
|
||||
splitter = QtWidgets.QSplitter()
|
||||
splitter.addWidget(pt)
|
||||
splitter.addWidget(pw)
|
||||
splitter.show()
|
||||
|
||||
data = np.random.normal(size=(50,5000))
|
||||
ptr = 0
|
||||
lastTime = time()
|
||||
fps = None
|
||||
pw.setWindowTitle('pyqtgraph example: PlotSpeedTest')
|
||||
pw.setLabel('bottom', 'Index', units='B')
|
||||
curve = MonkeyCurveItem(pen=default_pen)
|
||||
pw.addItem(curve)
|
||||
|
||||
rollingAverageSize = 1000
|
||||
elapsed = deque(maxlen=rollingAverageSize)
|
||||
|
||||
def resetTimings(*args):
|
||||
elapsed.clear()
|
||||
|
||||
def makeData(*args):
|
||||
global data, connect_array, ptr
|
||||
sigopts = params.child('sigopts')
|
||||
nsamples = sigopts['nsamples']
|
||||
frames = sigopts['frames']
|
||||
Fs = sigopts['fsample']
|
||||
A = sigopts['amplitude']
|
||||
F = sigopts['frequency']
|
||||
ttt = np.arange(frames * nsamples, dtype=np.float64) / Fs
|
||||
data = A*np.sin(2*np.pi*F*ttt).reshape((frames, nsamples))
|
||||
if sigopts['noise']:
|
||||
data += np.random.normal(size=data.shape)
|
||||
connect_array = np.ones(data.shape[-1], dtype=bool)
|
||||
ptr = 0
|
||||
pw.setRange(QtCore.QRectF(0, -10, nsamples, 20))
|
||||
|
||||
def onUseOpenGLChanged(param, enable):
|
||||
pw.useOpenGL(enable)
|
||||
|
||||
def onEnableExperimentalChanged(param, enable):
|
||||
pg.setConfigOption('enableExperimental', enable)
|
||||
|
||||
def onPenChanged(param, pen):
|
||||
curve.setPen(pen)
|
||||
|
||||
params.child('sigopts').sigTreeStateChanged.connect(makeData)
|
||||
params.child('useOpenGL').sigValueChanged.connect(onUseOpenGLChanged)
|
||||
params.child('enableExperimental').sigValueChanged.connect(onEnableExperimentalChanged)
|
||||
params.child('pen').sigValueChanged.connect(onPenChanged)
|
||||
params.child('plotMethod').sigValueChanged.connect(curve.setMethod)
|
||||
params.sigTreeStateChanged.connect(resetTimings)
|
||||
|
||||
makeData()
|
||||
|
||||
fpsLastUpdate = perf_counter()
|
||||
def update():
|
||||
global curve, data, ptr, p, lastTime, fps
|
||||
curve.setData(data[ptr%10])
|
||||
ptr += 1
|
||||
now = time()
|
||||
dt = now - lastTime
|
||||
lastTime = now
|
||||
if fps is None:
|
||||
fps = 1.0/dt
|
||||
else:
|
||||
s = np.clip(dt*3., 0, 1)
|
||||
fps = fps * (1-s) + (1.0/dt) * s
|
||||
p.setTitle('%0.2f fps' % fps)
|
||||
app.processEvents() ## force complete redraw for every plot
|
||||
global curve, data, ptr, elapsed, fpsLastUpdate
|
||||
|
||||
options = ['antialias', 'connect', 'skipFiniteCheck']
|
||||
kwds = { k : params[k] for k in options }
|
||||
if kwds['connect'] == 'array':
|
||||
kwds['connect'] = connect_array
|
||||
|
||||
# Measure
|
||||
t_start = perf_counter()
|
||||
curve.setData(data[ptr], **kwds)
|
||||
app.processEvents(QtCore.QEventLoop.ProcessEventsFlag.AllEvents)
|
||||
t_end = perf_counter()
|
||||
elapsed.append(t_end - t_start)
|
||||
ptr = (ptr + 1) % data.shape[0]
|
||||
|
||||
# update fps at most once every 0.2 secs
|
||||
if t_end - fpsLastUpdate > 0.2:
|
||||
fpsLastUpdate = t_end
|
||||
average = np.mean(elapsed)
|
||||
fps = 1 / average
|
||||
pw.setTitle('%0.2f fps - %0.1f ms avg' % (fps, average * 1_000))
|
||||
|
||||
timer = QtCore.QTimer()
|
||||
timer.timeout.connect(update)
|
||||
timer.start(0)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pg.mkQApp().exec_()
|
||||
pg.exec()
|
||||
|
|
|
@ -87,4 +87,4 @@ pw3.addItem(line)
|
|||
line.setBounds([0,200])
|
||||
|
||||
if __name__ == '__main__':
|
||||
pg.mkQApp().exec_()
|
||||
pg.exec()
|
||||
|
|
|
@ -96,4 +96,4 @@ p9.sigXRangeChanged.connect(updateRegion)
|
|||
updatePlot()
|
||||
|
||||
if __name__ == '__main__':
|
||||
pg.mkQApp().exec_()
|
||||
pg.exec()
|
||||
|
|
|
@ -158,4 +158,4 @@ def remove():
|
|||
r4.sigRemoveRequested.connect(remove)
|
||||
|
||||
if __name__ == '__main__':
|
||||
pg.mkQApp().exec_()
|
||||
pg.exec()
|
||||
|
|
|
@ -119,4 +119,4 @@ t.timeout.connect(updateImage)
|
|||
t.start(50)
|
||||
|
||||
if __name__ == '__main__':
|
||||
pg.mkQApp().exec_()
|
||||
pg.exec()
|
||||
|
|
|
@ -27,4 +27,4 @@ v.setCentralItem(plt)
|
|||
plt.plot([1,4,2,3,6,2,3,4,2,3], pen='g')
|
||||
|
||||
if __name__ == '__main__':
|
||||
pg.mkQApp().exec_()
|
||||
pg.exec()
|
||||
|
|
|
@ -16,6 +16,7 @@ from pyqtgraph.Qt import QtGui, QtCore
|
|||
import pyqtgraph as pg
|
||||
import pyqtgraph.widgets.RemoteGraphicsView
|
||||
import numpy as np
|
||||
from time import perf_counter
|
||||
|
||||
app = pg.mkQApp()
|
||||
|
||||
|
@ -45,7 +46,7 @@ rplt = view.pg.PlotItem()
|
|||
rplt._setProxyOptions(deferGetattr=True) ## speeds up access to rplt.plot
|
||||
view.setCentralItem(rplt)
|
||||
|
||||
lastUpdate = pg.ptime.time()
|
||||
lastUpdate = perf_counter()
|
||||
avgFps = 0.0
|
||||
|
||||
def update():
|
||||
|
@ -62,7 +63,7 @@ def update():
|
|||
if lcheck.isChecked():
|
||||
lplt.plot(data, clear=True)
|
||||
|
||||
now = pg.ptime.time()
|
||||
now = perf_counter()
|
||||
fps = 1.0 / (now - lastUpdate)
|
||||
lastUpdate = now
|
||||
avgFps = avgFps * 0.8 + fps * 0.2
|
||||
|
@ -73,4 +74,4 @@ timer.timeout.connect(update)
|
|||
timer.start(0)
|
||||
|
||||
if __name__ == '__main__':
|
||||
pg.mkQApp().exec_()
|
||||
pg.exec()
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
import initExample ## Add path to library (just for examples; you do not need this)
|
||||
|
||||
import pyqtgraph as pg
|
||||
from pyqtgraph.Qt import QtTest
|
||||
|
||||
from examples.ExampleApp import ExampleLoader
|
||||
"""
|
||||
This file is used by test_examples.py for ensuring the Example App works.
|
||||
It is not named test_ExampleApp.py as that way the Example application is
|
||||
not run twice.
|
||||
"""
|
||||
|
||||
pg.mkQApp()
|
||||
|
||||
def test_ExampleLoader():
|
||||
loader = ExampleLoader()
|
||||
QtTest.QTest.qWaitForWindowExposed(loader)
|
||||
QtTest.QTest.qWait(200)
|
||||
loader.close()
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_ExampleLoader()
|
||||
pg.exec()
|
|
@ -25,4 +25,4 @@ scale.setParentItem(vb)
|
|||
scale.anchor((1, 1), (1, 1), offset=(-20, -20))
|
||||
|
||||
if __name__ == '__main__':
|
||||
pg.mkQApp().exec_()
|
||||
pg.exec()
|
||||
|
|
|
@ -135,4 +135,4 @@ w4.addItem(s4)
|
|||
s4.sigClicked.connect(clicked)
|
||||
|
||||
if __name__ == '__main__':
|
||||
pg.mkQApp().exec_()
|
||||
pg.exec()
|
||||
|
|
|
@ -12,9 +12,9 @@ import initExample
|
|||
import numpy as np
|
||||
import pyqtgraph as pg
|
||||
from pyqtgraph.Qt import QtGui, QtCore, QtWidgets
|
||||
from pyqtgraph.ptime import time
|
||||
import pyqtgraph.parametertree as ptree
|
||||
import pyqtgraph.graphicsItems.ScatterPlotItem
|
||||
from time import perf_counter
|
||||
|
||||
translate = QtCore.QCoreApplication.translate
|
||||
|
||||
|
@ -24,10 +24,9 @@ param = ptree.Parameter.create(name=translate('ScatterPlot', 'Parameters'), type
|
|||
dict(name='count', title=translate('ScatterPlot', 'Count: '), type='int', limits=[1, None], value=500, step=100),
|
||||
dict(name='size', title=translate('ScatterPlot', 'Size: '), type='int', limits=[1, None], value=10),
|
||||
dict(name='randomize', title=translate('ScatterPlot', 'Randomize: '), type='bool', value=False),
|
||||
dict(name='_USE_QRECT', title='_USE_QRECT: ', type='bool', value=pyqtgraph.graphicsItems.ScatterPlotItem._USE_QRECT),
|
||||
dict(name='pxMode', title='pxMode: ', type='bool', value=True),
|
||||
dict(name='useCache', title='useCache: ', type='bool', value=True),
|
||||
dict(name='mode', title=translate('ScatterPlot', 'Mode: '), type='list', values={translate('ScatterPlot', 'New Item'): 'newItem', translate('ScatterPlot', 'Reuse Item'): 'reuseItem', translate('ScatterPlot', 'Simulate Pan/Zoom'): 'panZoom', translate('ScatterPlot', 'Simulate Hover'): 'hover'}, value='reuseItem'),
|
||||
dict(name='mode', title=translate('ScatterPlot', 'Mode: '), type='list', limits={translate('ScatterPlot', 'New Item'): 'newItem', translate('ScatterPlot', 'Reuse Item'): 'reuseItem', translate('ScatterPlot', 'Simulate Pan/Zoom'): 'panZoom', translate('ScatterPlot', 'Simulate Hover'): 'hover'}, value='reuseItem'),
|
||||
])
|
||||
for c in param.children():
|
||||
c.setDefault(c.value())
|
||||
|
@ -44,7 +43,7 @@ data = {}
|
|||
item = pg.ScatterPlotItem()
|
||||
hoverBrush = pg.mkBrush('y')
|
||||
ptr = 0
|
||||
lastTime = time()
|
||||
lastTime = perf_counter()
|
||||
fps = None
|
||||
timer = QtCore.QTimer()
|
||||
|
||||
|
@ -68,7 +67,6 @@ def mkDataAndItem():
|
|||
|
||||
def mkItem():
|
||||
global item
|
||||
pyqtgraph.graphicsItems.ScatterPlotItem._USE_QRECT = param['_USE_QRECT']
|
||||
item = pg.ScatterPlotItem(pxMode=param['pxMode'], **getData())
|
||||
item.opts['useCache'] = param['useCache']
|
||||
p.clear()
|
||||
|
@ -106,7 +104,7 @@ def update():
|
|||
new.setBrush(hoverBrush)
|
||||
|
||||
ptr += 1
|
||||
now = time()
|
||||
now = perf_counter()
|
||||
dt = now - lastTime
|
||||
lastTime = now
|
||||
if fps is None:
|
||||
|
@ -122,11 +120,11 @@ def update():
|
|||
mkDataAndItem()
|
||||
for name in ['count', 'size']:
|
||||
param.child(name).sigValueChanged.connect(mkDataAndItem)
|
||||
for name in ['_USE_QRECT', 'useCache', 'pxMode', 'randomize']:
|
||||
for name in ['useCache', 'pxMode', 'randomize']:
|
||||
param.child(name).sigValueChanged.connect(mkItem)
|
||||
param.child('paused').sigValueChanged.connect(lambda _, v: timer.stop() if v else timer.start())
|
||||
timer.timeout.connect(update)
|
||||
timer.start(0)
|
||||
|
||||
if __name__ == '__main__':
|
||||
pg.mkQApp().exec_()
|
||||
pg.exec()
|
||||
|
|
|
@ -63,4 +63,4 @@ spw.setData(data)
|
|||
spw.show()
|
||||
|
||||
if __name__ == '__main__':
|
||||
pg.mkQApp().exec_()
|
||||
pg.exec()
|
||||
|
|
|
@ -6,4 +6,4 @@ import numpy as np
|
|||
plt = pg.plot(np.random.normal(size=100), title="Simplest possible plotting example")
|
||||
|
||||
if __name__ == '__main__':
|
||||
pg.mkQApp().exec_()
|
||||
pg.exec()
|
||||
|
|
|
@ -129,4 +129,4 @@ layout.addWidget(changedLabel, 2, 1)
|
|||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pg.mkQApp().exec_()
|
||||
pg.exec()
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue