35 Commits

Author SHA1 Message Date
jdegenstein
9579757c98 debugger.py -> abspath to absolute 2024-08-22 12:34:28 -05:00
jdegenstein
5d8f651f97 widgets/editor.py -> abspath to absolute 2024-08-22 12:34:12 -05:00
jdegenstein
7f01d73f54 editor.py -> abspath to absolute 2024-08-22 12:33:40 -05:00
jdegenstein
d657da605a setup.py -> add numpy dep below v2 2024-08-22 12:27:06 -05:00
jdegenstein
5fdeda5076 pyinstaller-builds-actions-PIP-TAR.yml -> pip install self built macos arm64 nlopt 2024-06-05 08:45:57 -05:00
jdegenstein
ebdc391f3d Update pyinstaller-builds-actions-PIP-TAR.yml 2024-06-04 20:36:06 -05:00
jdegenstein
3ba85424c7 Update pyinstaller-builds-actions-PIP-TAR.yml -> use mainline pyinstaller-hooks-contrib 2024-06-04 14:09:54 -05:00
jdegenstein
069df10dd0 Update pyinstaller-builds-actions-PIP-TAR.yml 2024-06-01 15:20:44 -05:00
jdegenstein
c2ba027cd5 Update pyinstaller-builds-actions-PIP-TAR.yml 2024-06-01 09:54:33 -05:00
jdegenstein
9653933c2e CQ-editor.sh workaround from issue #13 2024-05-30 20:44:56 -05:00
jdegenstein
7878bad430 Update pyinstaller-builds-actions-PIP-TAR.yml 2024-05-30 15:21:05 -05:00
jdegenstein
80b9dd5bef pyinstaller-builds-actions-PIP-TAR.yml 2024-05-30 14:48:30 -05:00
jdegenstein
67d64f67a7 pyinstaller-builds-actions-PIP-TAR.yml -> attempt to add macos-arm64 2024-05-30 14:40:50 -05:00
jdegenstein
4ae685aba2 pyinstaller-builds-actions-PIP-TAR.yml -> Fix win path for use with bash 2024-05-30 14:20:24 -05:00
jdegenstein
189c37e284 pyinstaller-builds-actions-PIP-TAR.yml -> shell: bash --login {0} 2024-05-30 14:06:37 -05:00
jdegenstein
8b63d10dc5 Update pyinstaller-builds-actions-PIP-TAR.yml 2024-05-30 13:59:06 -05:00
jdegenstein
38feb5ddc4 pyinstaller-builds-actions-PIP-TAR.yml -> activate environment 2024-05-30 13:49:42 -05:00
jdegenstein
0254de2be7 Update cqgui_env.yml python 3.11 2024-05-30 13:15:30 -05:00
jdegenstein
33e813dad8 pyinstaller_pip.spec -> python 3.11 2024-05-30 13:14:42 -05:00
jdegenstein
5354369dcd environment.yml -> python=3.11 2024-05-30 13:14:04 -05:00
jdegenstein
20e8d623d0 pyinstaller-builds-actions-PIP-TAR.yml -> Updating actions and fixing MacOS/x86_64 builds 2024-05-30 13:13:05 -05:00
jdegenstein
f26f95396a pyinstaller-builds-actions-PIP-TAR.yml -> update some GH actions for node20 2024-04-12 09:46:11 -05:00
jdegenstein
6fe767c0a4 Delete pyinstaller.spec 2024-01-02 11:46:25 -06:00
jdegenstein
1bb94c1d76 Delete .github/workflows/pyinstaller-builds-actions.yml 2024-01-02 11:46:13 -06:00
jdegenstein
003b9eb3f5 Delete .github/workflows/pyinstaller-builds-actions-mmamba.yml 2024-01-02 11:46:06 -06:00
jdegenstein
143d7712cd Delete .github/workflows/pyinstaller-builds-actions-mmamba-TAR.yml 2024-01-02 11:45:55 -06:00
jdegenstein
aaf0d31b52 Delete .github/workflows/pyinstaller-builds-actions-build-PIP-TAR-pyinst5-6p.yml 2024-01-02 11:45:33 -06:00
jdegenstein
14d0477d1d README.md -> add bd_warehouse 2024-01-02 10:56:34 -06:00
jdegenstein
5b0504f22b pyinstaller_pip.spec -> add bd_warehouse 2024-01-02 10:55:24 -06:00
jdegenstein
3ed0deff41 pyinstaller-builds-actions-PIP-TAR.yml -> install bd_warehouse 2024-01-02 10:49:14 -06:00
jdegenstein
e00e40c7b0 Update pyinstaller-builds-actions-PIP-TAR.yml
back to python 3.10
2023-10-30 13:44:07 -05:00
jdegenstein
035b95c508 Update pyinstaller-builds-actions-PIP-TAR.yml
bump to Ubuntu-22.04 and python 3.11
2023-10-30 13:42:10 -05:00
jdegenstein
656426f645 Delete pyinstaller/extrahooks/hook-rtree.py
fixed on pyinstaller hooks contrib, this file did not produce working linux builds anyway
2023-10-26 13:40:46 -05:00
jdegenstein
ab6526c1fe Create hook-rtree.py 2023-10-20 15:19:04 -05:00
jdegenstein
d8c4e5bfac Create hook-py_lib3mf.py 2023-10-20 14:01:40 -05:00
31 changed files with 1156 additions and 1748 deletions

View File

@@ -9,34 +9,31 @@ on:
required: true required: true
default: 'dir' default: 'dir'
jobs: jobs:
build-linux: build-linux-x86_64:
runs-on: ubuntu-20.04 runs-on: ubuntu-22.04
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- uses: mamba-org/setup-micromamba@v1 - uses: mamba-org/setup-micromamba@v1
with: with:
#miniconda-version: "latest"
#auto-update-conda: true
environment-name: test environment-name: test
environment-file: environment.yml environment-file: environment.yml
create-args: >- init-shell: >-
python=3.10 bash
- name: pip install cadquery CQ-editor ... etc - name: pip install cadquery CQ-editor ... etc
shell: bash --login {0} shell: bash --login {0}
run: | run: |
sudo apt install -y libblas-dev libblas3 libblas64-3 libblas64-dev sudo apt install -y libblas-dev libblas3 libblas64-3 libblas64-dev
sudo apt install -y libxkbcommon0 sudo apt install -y libxkbcommon0 libxkbcommon-x11-0 libxcb-xinerama0
sudo apt install -y libxkbcommon-x11-0
sudo apt install -y libxcb-xinerama0
sudo apt install -y qtbase5-dev qt5-qmake sudo apt install -y qtbase5-dev qt5-qmake
micromamba info micromamba info
pip install pyopengl pip install pyopengl
pip install git+https://github.com/jdegenstein/jmwright-CQ-Editor pip install git+https://github.com/jdegenstein/jmwright-CQ-Editor
pip install -vvv --pre git+https://github.com/cadquery/cadquery casadi pip install -vvv --pre git+https://github.com/cadquery/cadquery casadi
pip install pyinstaller>=5.6 git+https://github.com/jdegenstein/pyinstaller-hooks-contrib pip install pyinstaller>=5.6
pip install path pip install path
pip install jupyter-rfb pip install jupyter-rfb
pip install git+https://github.com/gumyr/cq_warehouse.git#egg=cq_warehouse pip install git+https://github.com/gumyr/cq_warehouse.git#egg=cq_warehouse
pip install git+https://github.com/gumyr/bd_warehouse
pip install git+https://github.com/meadiode/cq_gears.git@main pip install git+https://github.com/meadiode/cq_gears.git@main
pip install -e "git+https://github.com/CadQuery/cadquery-plugins.git#egg=cq_cache&subdirectory=plugins/cq_cache" pip install -e "git+https://github.com/CadQuery/cadquery-plugins.git#egg=cq_cache&subdirectory=plugins/cq_cache"
pip install git+https://github.com/gumyr/build123d pip install git+https://github.com/gumyr/build123d
@@ -45,37 +42,37 @@ jobs:
- name: Run build - name: Run build
shell: bash --login {0} shell: bash --login {0}
run: | run: |
micromamba activate test
micromamba info micromamba info
pyinstaller pyinstaller_pip.spec ${{ github.event.inputs.type }} pyinstaller pyinstaller_pip.spec ${{ github.event.inputs.type }}
cp /home/runner/work/jmwright-CQ-Editor/jmwright-CQ-Editor/pyinstaller/CQ-editor.sh /home/runner/work/jmwright-CQ-Editor/jmwright-CQ-Editor/dist/ cp /home/runner/work/jmwright-CQ-Editor/jmwright-CQ-Editor/pyinstaller/CQ-editor.sh /home/runner/work/jmwright-CQ-Editor/jmwright-CQ-Editor/dist/
- uses: alehechka/upload-tartifact@v2 - uses: actions/upload-artifact@v4
with: with:
name: CQ-editor-Linux-x86_64 name: CQ-editor-Linux-x86_64
path: dist path: dist
build-macos: build-macos-x86_64:
runs-on: macos-latest runs-on: macos-13
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- uses: mamba-org/setup-micromamba@v1 - uses: mamba-org/setup-micromamba@v1
with: with:
#miniconda-version: "latest"
#auto-update-conda: true
environment-name: test environment-name: test
environment-file: environment.yml environment-file: environment.yml
create-args: >- init-shell: >-
python=3.10 bash
- name: pip install cadquery CQ-editor ... etc - name: pip install cadquery CQ-editor ... etc
shell: bash --login {0} shell: bash --login {0}
run: | run: |
micromamba info micromamba info
pip install git+https://github.com/jdegenstein/jmwright-CQ-Editor pip install git+https://github.com/jdegenstein/jmwright-CQ-Editor
pip install --pre git+https://github.com/cadquery/cadquery casadi pip install --pre git+https://github.com/cadquery/cadquery casadi
pip install pyinstaller>=5.6 git+https://github.com/jdegenstein/pyinstaller-hooks-contrib pip install pyinstaller>=5.6
pip install path pip install path
pip uninstall -y PyQt5 pip uninstall -y PyQt5
pip install PyQt5==5.15.7 pip install PyQt5==5.15.7
pip install PyQtWebEngine==5.15.6 pip install PyQtWebEngine==5.15.6
pip install git+https://github.com/gumyr/cq_warehouse.git#egg=cq_warehouse pip install git+https://github.com/gumyr/cq_warehouse.git#egg=cq_warehouse
pip install git+https://github.com/gumyr/bd_warehouse
pip install git+https://github.com/meadiode/cq_gears.git@main pip install git+https://github.com/meadiode/cq_gears.git@main
pip install -e "git+https://github.com/CadQuery/cadquery-plugins.git#egg=cq_cache&subdirectory=plugins/cq_cache" pip install -e "git+https://github.com/CadQuery/cadquery-plugins.git#egg=cq_cache&subdirectory=plugins/cq_cache"
pip install git+https://github.com/gumyr/build123d pip install git+https://github.com/gumyr/build123d
@@ -84,48 +81,93 @@ jobs:
- name: Run build - name: Run build
shell: bash --login {0} shell: bash --login {0}
run: | run: |
micromamba activate test
micromamba info micromamba info
pyinstaller pyinstaller_pip.spec ${{ github.event.inputs.type }} pyinstaller pyinstaller_pip.spec ${{ github.event.inputs.type }}
cp /Users/runner/work/jmwright-CQ-Editor/jmwright-CQ-Editor/pyinstaller/CQ-editor.sh /Users/runner/work/jmwright-CQ-Editor/jmwright-CQ-Editor/dist/ cp /Users/runner/work/jmwright-CQ-Editor/jmwright-CQ-Editor/pyinstaller/CQ-editor.sh /Users/runner/work/jmwright-CQ-Editor/jmwright-CQ-Editor/dist/
- uses: alehechka/upload-tartifact@v1 - uses: actions/upload-artifact@v4
with: with:
name: CQ-editor-MacOS name: CQ-editor-MacOS-x86_64
path: dist path: dist
build-windows: build-macos-arm64:
runs-on: windows-latest runs-on: macos-14
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- uses: mamba-org/setup-micromamba@v1 - uses: mamba-org/setup-micromamba@v1
with: with:
#miniconda-version: "latest"
#auto-update-conda: true
environment-name: test environment-name: test
environment-file: environment.yml environment-file: environment.yml
init-shell: >- init-shell: >-
powershell bash
create-args: >-
python=3.10
- name: pip install cadquery CQ-editor ... etc - name: pip install cadquery CQ-editor ... etc
shell: powershell shell: bash --login {0}
run: | run: |
micromamba info micromamba info
pip install git+https://github.com/jdegenstein/jmwright-CQ-Editor pip install git+https://github.com/jdegenstein/jmwright-CQ-Editor
pip install https://github.com/CadQuery/ocp-build-system/releases/download/7.7.2.0/cadquery_ocp-7.7.2-cp311-cp311-macosx_11_0_arm64.whl
pip install https://github.com/jdegenstein/nlopt-python/releases/download/2.7.1.3/nlopt-2.7.1-cp311-cp311-macosx_11_0_arm64.whl
pip install --pre git+https://github.com/cadquery/cadquery casadi pip install --pre git+https://github.com/cadquery/cadquery casadi
pip install pyinstaller>=5.6 git+https://github.com/jdegenstein/pyinstaller-hooks-contrib pip install pyinstaller>=5.6
pip install path pip install path
pip uninstall -y PyQt5
pip install PyQt5==5.15.10
pip install PyQtWebEngine==5.15.6
pip install git+https://github.com/gumyr/cq_warehouse.git#egg=cq_warehouse pip install git+https://github.com/gumyr/cq_warehouse.git#egg=cq_warehouse
pip install git+https://github.com/gumyr/bd_warehouse
pip install git+https://github.com/meadiode/cq_gears.git@main pip install git+https://github.com/meadiode/cq_gears.git@main
pip install -e "git+https://github.com/CadQuery/cadquery-plugins.git#egg=cq_cache&subdirectory=plugins/cq_cache" pip install -e "git+https://github.com/CadQuery/cadquery-plugins.git#egg=cq_cache&subdirectory=plugins/cq_cache"
pip install git+https://github.com/gumyr/build123d pip install git+https://github.com/gumyr/build123d
pip install git+https://github.com/JustinSDK/cqMore pip install git+https://github.com/JustinSDK/cqMore
pip list pip list
- name: Run build - name: Run build
shell: powershell shell: bash --login {0}
run: | run: |
micromamba activate test
micromamba info micromamba info
pyinstaller pyinstaller_pip.spec ${{ github.event.inputs.type }} pyinstaller pyinstaller_pip.spec ${{ github.event.inputs.type }}
Copy-Item D:\a\jmwright-CQ-Editor\jmwright-CQ-Editor\pyinstaller\CQ-editor.cmd D:\a\jmwright-CQ-Editor\jmwright-CQ-Editor\dist\ cp /Users/runner/work/jmwright-CQ-Editor/jmwright-CQ-Editor/pyinstaller/CQ-editor.sh /Users/runner/work/jmwright-CQ-Editor/jmwright-CQ-Editor/dist/
- uses: alehechka/upload-tartifact@v2 - uses: actions/upload-artifact@v4
with:
name: CQ-editor-MacOS-arm64
path: dist
build-windows:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- uses: mamba-org/setup-micromamba@v1
with:
environment-name: test
environment-file: environment.yml
init-shell: >-
bash
- name: pip install cadquery CQ-editor ... etc
shell: bash --login {0}
run: |
micromamba info
pip install git+https://github.com/jdegenstein/jmwright-CQ-Editor
pip install --pre git+https://github.com/cadquery/cadquery casadi
pip install pyinstaller>=5.6
pip install path
pip install git+https://github.com/gumyr/cq_warehouse.git#egg=cq_warehouse
pip install git+https://github.com/gumyr/bd_warehouse
pip install git+https://github.com/meadiode/cq_gears.git@main
pip install -e "git+https://github.com/CadQuery/cadquery-plugins.git#egg=cq_cache&subdirectory=plugins/cq_cache"
pip install git+https://github.com/gumyr/build123d
pip install git+https://github.com/JustinSDK/cqMore
pip list
- name: Run build
shell: bash --login {0}
run: |
micromamba activate test
micromamba info
pyinstaller pyinstaller_pip.spec ${{ github.event.inputs.type }}
cp D:/a/jmwright-CQ-Editor/jmwright-CQ-Editor/pyinstaller/CQ-editor.cmd D:/a/jmwright-CQ-Editor/jmwright-CQ-Editor/dist/
- name: 7zip artifact (workaround for too many files during artifact upload)
shell: bash --login {0}
run: 7z a release.zip ./dist/*
- uses: actions/upload-artifact@v4
with: with:
name: CQ-editor-Windows name: CQ-editor-Windows
path: dist path: release.zip

View File

@@ -1,126 +0,0 @@
name: build-PIP-TAR-pyinst5-6p
on:
# schedule:
# - cron: '0 0 * * 1'
workflow_dispatch:
inputs:
type:
description: 'Whether to build a single file (onefile) or directory (dir) dist'
required: true
default: 'dir'
jobs:
# build-linux:
# runs-on: ubuntu-20.04
# steps:
# - uses: actions/checkout@v2
# - uses: mamba-org/provision-with-micromamba@main
# with:
# #miniconda-version: "latest"
# #auto-update-conda: true
# environment-name: test
# environment-file: environment.yml
# extra-specs:
# python=3.9
# - name: Mamba install CadQuery and pyinstaller
# shell: bash --login {0}
# run: |
# sudo apt install -y libblas-dev libblas3 libblas64-3 libblas64-dev
# sudo apt install -y libxkbcommon0
# sudo apt install -y libxkbcommon-x11-0
# sudo apt install -y libxcb-xinerama0
# micromamba info
# pip install git+https://github.com/jdegenstein/jmwright-CQ-Editor
# pip install -vvv --pre git+https://github.com/cadquery/cadquery casadi==3.5.5
# pip install pyinstaller==4.10
# pip install path
# pip install git+https://github.com/gumyr/cq_warehouse.git#egg=cq_warehouse
# pip install git+https://github.com/meadiode/cq_gears.git@main
# pip install -e "git+https://github.com/CadQuery/cadquery-plugins.git#egg=cq_cache&subdirectory=plugins/cq_cache"
# pip install git+https://github.com/gumyr/build123d.git#egg=build123d
# pip install git+https://github.com/JustinSDK/cqMore
# pip list
# - name: Run build
# shell: bash --login {0}
# run: |
# micromamba info
# pyinstaller pyinstaller_pip.spec ${{ github.event.inputs.type }}
# cp /home/runner/work/jmwright-CQ-Editor/jmwright-CQ-Editor/pyinstaller/CQ-editor.sh /home/runner/work/jmwright-CQ-Editor/jmwright-CQ-Editor/dist/
# - uses: alehechka/upload-tartifact@v1
# with:
# name: CQ-editor-Linux-x86_64
# path: dist
# build-macos:
# runs-on: macos-latest
# steps:
# - uses: actions/checkout@v2
# - uses: mamba-org/provision-with-micromamba@main
# with:
# #miniconda-version: "latest"
# #auto-update-conda: true
# environment-name: test
# environment-file: environment.yml
# extra-specs:
# python=3.9
# - name: Mamba install CadQuery and pyinstaller
# shell: bash --login {0}
# run: |
# micromamba info
# pip install git+https://github.com/jdegenstein/jmwright-CQ-Editor
# pip install --pre git+https://github.com/cadquery/cadquery casadi==3.5.5
# pip install pyinstaller==4.10
# pip install path
# pip uninstall -y PyQt5
# pip install PyQt5==5.15.7
# pip install PyQtWebEngine==5.15.6
# pip install git+https://github.com/gumyr/cq_warehouse.git#egg=cq_warehouse
# pip install git+https://github.com/meadiode/cq_gears.git@main
# pip install -e "git+https://github.com/CadQuery/cadquery-plugins.git#egg=cq_cache&subdirectory=plugins/cq_cache"
# pip install git+https://github.com/gumyr/build123d.git#egg=build123d
# pip install git+https://github.com/JustinSDK/cqMore
# pip list
# - name: Run build
# shell: bash --login {0}
# run: |
# micromamba info
# pyinstaller pyinstaller_pip.spec ${{ github.event.inputs.type }}
# cp /Users/runner/work/jmwright-CQ-Editor/jmwright-CQ-Editor/pyinstaller/CQ-editor.sh /Users/runner/work/jmwright-CQ-Editor/jmwright-CQ-Editor/dist/
# - uses: alehechka/upload-tartifact@v1
# with:
# name: CQ-editor-MacOS
# path: dist
build-windows:
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
- uses: mamba-org/provision-with-micromamba@main
with:
#miniconda-version: "latest"
#auto-update-conda: true
environment-name: test
environment-file: environment.yml
extra-specs:
python=3.9
- name: pip install cadquery CQ-editor ... etc
shell: powershell
run: |
micromamba info
pip install git+https://github.com/jdegenstein/jmwright-CQ-Editor
pip install --pre git+https://github.com/cadquery/cadquery casadi==3.5.5
pip install pyinstaller>=5.6
pip install path
pip install git+https://github.com/gumyr/cq_warehouse.git#egg=cq_warehouse
pip install git+https://github.com/meadiode/cq_gears.git@main
pip install -e "git+https://github.com/CadQuery/cadquery-plugins.git#egg=cq_cache&subdirectory=plugins/cq_cache"
pip install git+https://github.com/gumyr/build123d
pip install git+https://github.com/JustinSDK/cqMore
pip list
- name: Run build
shell: powershell
run: |
micromamba info
pyinstaller pyinstaller_pip.spec ${{ github.event.inputs.type }}
Copy-Item D:\a\jmwright-CQ-Editor\jmwright-CQ-Editor\pyinstaller\CQ-editor.cmd D:\a\jmwright-CQ-Editor\jmwright-CQ-Editor\dist\
- uses: alehechka/upload-tartifact@v1
with:
name: CQ-editor-Windows
path: dist

View File

@@ -1,122 +0,0 @@
name: build-micromamba-DEV
on:
schedule:
- cron: '0 0 * * 1'
workflow_dispatch:
inputs:
type:
description: 'Whether to build a single file (onefile) or directory (dir) dist'
required: true
default: 'dir'
jobs:
# build-linux:
# runs-on: ubuntu-latest
# steps:
# - uses: actions/checkout@v2
# - uses: mamba-org/provision-with-micromamba@main
# with:
# #miniconda-version: "latest"
# #auto-update-conda: true
# environment-name: test
# environment-file: environment.yml
# extra-specs:
# python=3.9
# - name: Mamba install CadQuery and pyinstaller
# shell: bash --login {0}
# run: |
# sudo apt install -y libblas-dev libblas3 libblas64-3 libblas64-dev
# sudo apt install -y libxkbcommon0
# sudo apt install -y libxkbcommon-x11-0
# sudo apt install -y libxcb-xinerama0
# micromamba info
# micromamba install -c cadquery -c conda-forge cq-editor=master cadquery=master debugpy ipython=8.4.0 jedi=0.17.2 python=3.9
# micromamba install -c conda-forge pyinstaller=4.10
# pip install path
# pip install git+https://github.com/gumyr/cq_warehouse.git#egg=cq_warehouse
# pip install git+https://github.com/meadiode/cq_gears.git@main
# pip install -e "git+https://github.com/CadQuery/cadquery-plugins.git#egg=cq_cache&subdirectory=plugins/cq_cache"
# pip install git+https://github.com/gumyr/build123d.git#egg=build123d
# pip install git+https://github.com/JustinSDK/cqMore
# - name: Run build
# shell: bash --login {0}
# run: |
# micromamba info
# pyinstaller pyinstaller.spec ${{ github.event.inputs.type }}
# cp /home/runner/work/jmwright-CQ-Editor/jmwright-CQ-Editor/pyinstaller/CQ-editor.sh /home/runner/work/jmwright-CQ-Editor/jmwright-CQ-Editor/dist/
# - uses: actions/upload-artifact@v2
# with:
# name: CQ-editor-Linux-x86_64
# path: dist
# build-macos:
# runs-on: macos-latest
# steps:
# - uses: actions/checkout@v2
# - uses: mamba-org/provision-with-micromamba@main
# with:
# #miniconda-version: "latest"
# #auto-update-conda: true
# environment-name: test
# environment-file: environment.yml
# extra-specs:
# python=3.9
# - name: Mamba install CadQuery and pyinstaller
# shell: bash --login {0}
# run: |
# micromamba info
# micromamba install -c cadquery -c conda-forge cq-editor=master cadquery=master debugpy ipython=8.4.0 jedi=0.17.2 python=3.9
# micromamba install -c conda-forge pyinstaller
# pip install path
# pip uninstall -y PyQt5
# pip install PyQt5==5.15.7
# pip install PyQtWebEngine==5.15.6
# pip install git+https://github.com/gumyr/cq_warehouse.git#egg=cq_warehouse
# pip install git+https://github.com/meadiode/cq_gears.git@main
# pip install -e "git+https://github.com/CadQuery/cadquery-plugins.git#egg=cq_cache&subdirectory=plugins/cq_cache"
# pip install git+https://github.com/gumyr/build123d.git#egg=build123d
# pip install git+https://github.com/JustinSDK/cqMore
# - name: Run build
# shell: bash --login {0}
# run: |
# micromamba info
# pyinstaller pyinstaller.spec ${{ github.event.inputs.type }}
# cp /Users/runner/work/jmwright-CQ-Editor/jmwright-CQ-Editor/pyinstaller/CQ-editor.sh /Users/runner/work/jmwright-CQ-Editor/jmwright-CQ-Editor/dist/
# - uses: actions/upload-artifact@v2
# with:
# name: CQ-editor-MacOS
# path: dist
build-windows:
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
- uses: mamba-org/provision-with-micromamba@main
with:
#miniconda-version: "latest"
#auto-update-conda: true
environment-name: test
environment-file: environment.yml
extra-specs:
python=3.9
- name: Mamba install CadQuery and pyinstaller
shell: powershell
run: |
micromamba install -c cadquery -c conda-forge cq-editor=master cadquery=master debugpy ipython=8.4.0 jedi=0.17.2 python=3.9
micromamba install -c conda-forge pyinstaller=4.10 casadi ipopt
pip install path
pip install pipwin
pipwin install numpy
pip install git+https://github.com/gumyr/cq_warehouse.git#egg=cq_warehouse
pip install git+https://github.com/meadiode/cq_gears.git@main
pip install -e "git+https://github.com/CadQuery/cadquery-plugins.git#egg=cq_cache&subdirectory=plugins/cq_cache"
pip install git+https://github.com/gumyr/build123d.git#egg=build123d
pip install git+https://github.com/JustinSDK/cqMore
- name: Run build
shell: powershell
run: |
micromamba info
pyinstaller --debug all pyinstaller.spec ${{ github.event.inputs.type }}
Copy-Item C:\Users\runneradmin\micromamba-root\envs\test\Library\bin\casadi_nlpsol_ipopt.dll D:\a\jmwright-CQ-Editor\jmwright-CQ-Editor\dist\CQ-editor\
Copy-Item D:\a\jmwright-CQ-Editor\jmwright-CQ-Editor\pyinstaller\CQ-editor.cmd D:\a\jmwright-CQ-Editor\jmwright-CQ-Editor\dist\
- uses: alehechka/upload-tartifact@v1
with:
name: CQ-editor-Windows
path: dist

View File

@@ -1,121 +0,0 @@
name: build-micromamba
on:
schedule:
- cron: '0 0 * * 1'
workflow_dispatch:
inputs:
type:
description: 'Whether to build a single file (onefile) or directory (dir) dist'
required: true
default: 'dir'
jobs:
build-linux:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: mamba-org/provision-with-micromamba@main
with:
#miniconda-version: "latest"
#auto-update-conda: true
environment-name: test
environment-file: environment.yml
extra-specs:
python=3.9
- name: Mamba install CadQuery and pyinstaller
shell: bash --login {0}
run: |
sudo apt install -y libblas-dev libblas3 libblas64-3 libblas64-dev
sudo apt install -y libxkbcommon0
sudo apt install -y libxkbcommon-x11-0
sudo apt install -y libxcb-xinerama0
micromamba info
micromamba install -c cadquery -c conda-forge cq-editor=master cadquery=master debugpy ipython=8.4.0 jedi=0.17.2 python=3.9
micromamba install -c conda-forge pyinstaller=4.10
pip install path
pip install git+https://github.com/gumyr/cq_warehouse.git#egg=cq_warehouse
pip install git+https://github.com/meadiode/cq_gears.git@main
pip install -e "git+https://github.com/CadQuery/cadquery-plugins.git#egg=cq_cache&subdirectory=plugins/cq_cache"
pip install git+https://github.com/gumyr/build123d.git#egg=build123d
pip install git+https://github.com/JustinSDK/cqMore
- name: Run build
shell: bash --login {0}
run: |
micromamba info
pyinstaller pyinstaller.spec ${{ github.event.inputs.type }}
cp /home/runner/work/jmwright-CQ-Editor/jmwright-CQ-Editor/pyinstaller/CQ-editor.sh /home/runner/work/jmwright-CQ-Editor/jmwright-CQ-Editor/dist/
- uses: actions/upload-artifact@v2
with:
name: CQ-editor-Linux-x86_64
path: dist
build-macos:
runs-on: macos-latest
steps:
- uses: actions/checkout@v2
- uses: mamba-org/provision-with-micromamba@main
with:
#miniconda-version: "latest"
#auto-update-conda: true
environment-name: test
environment-file: environment.yml
extra-specs:
python=3.9
- name: Mamba install CadQuery and pyinstaller
shell: bash --login {0}
run: |
micromamba info
micromamba install -c cadquery -c conda-forge cq-editor=master cadquery=master debugpy ipython=8.4.0 jedi=0.17.2 python=3.9
micromamba install -c conda-forge pyinstaller
pip install path
pip uninstall -y PyQt5
pip install PyQt5==5.15.7
pip install PyQtWebEngine==5.15.6
pip install git+https://github.com/gumyr/cq_warehouse.git#egg=cq_warehouse
pip install git+https://github.com/meadiode/cq_gears.git@main
pip install -e "git+https://github.com/CadQuery/cadquery-plugins.git#egg=cq_cache&subdirectory=plugins/cq_cache"
pip install git+https://github.com/gumyr/build123d.git#egg=build123d
pip install git+https://github.com/JustinSDK/cqMore
- name: Run build
shell: bash --login {0}
run: |
micromamba info
pyinstaller pyinstaller.spec ${{ github.event.inputs.type }}
cp /Users/runner/work/jmwright-CQ-Editor/jmwright-CQ-Editor/pyinstaller/CQ-editor.sh /Users/runner/work/jmwright-CQ-Editor/jmwright-CQ-Editor/dist/
- uses: actions/upload-artifact@v2
with:
name: CQ-editor-MacOS
path: dist
build-windows:
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
- uses: mamba-org/provision-with-micromamba@main
with:
#miniconda-version: "latest"
#auto-update-conda: true
environment-name: test
environment-file: environment.yml
extra-specs:
python=3.9
- name: Mamba install CadQuery and pyinstaller
shell: powershell
run: |
micromamba install -c cadquery -c conda-forge cq-editor=master cadquery=master debugpy ipython=8.4.0 jedi=0.17.2 python=3.9
micromamba install -c conda-forge pyinstaller=4.10
pip install path
pip install pipwin
pipwin install numpy
pip install git+https://github.com/gumyr/cq_warehouse.git#egg=cq_warehouse
pip install git+https://github.com/meadiode/cq_gears.git@main
pip install -e "git+https://github.com/CadQuery/cadquery-plugins.git#egg=cq_cache&subdirectory=plugins/cq_cache"
pip install git+https://github.com/gumyr/build123d.git#egg=build123d
pip install git+https://github.com/JustinSDK/cqMore
- name: Run build
shell: powershell
run: |
micromamba info
pyinstaller --debug all pyinstaller.spec ${{ github.event.inputs.type }}
Copy-Item D:\a\jmwright-CQ-Editor\jmwright-CQ-Editor\pyinstaller\CQ-editor.cmd D:\a\jmwright-CQ-Editor\jmwright-CQ-Editor\dist\
- uses: actions/upload-artifact@v2
with:
name: CQ-editor-Windows
path: dist

View File

@@ -1,120 +0,0 @@
name: build
on:
schedule:
- cron: '0 0 * * 1'
workflow_dispatch:
inputs:
type:
description: 'Whether to build a single file (onefile) or directory (dir) dist'
required: true
default: 'dir'
jobs:
# build-linux:
# runs-on: ubuntu-latest
# steps:
# - uses: actions/checkout@v2
# - uses: conda-incubator/setup-miniconda@v2
# with:
# mamba-version: "*"
# channels: conda-forge,defaults
# channel-priority: true
# # auto-update-conda: true
# python-version: 3.9
# activate-environment: test
# - name: Install CadQuery, CQ-editor and pyinstaller
# shell: bash --login {0}
# run: |
# sudo apt install -y libblas-dev libblas3 libblas64-3 libblas64-dev
# sudo apt install -y libxkbcommon0
# sudo apt install -y libxkbcommon-x11-0
# sudo apt install -y libxcb-xinerama0
# conda info
# conda install -c cadquery -c conda-forge cq-editor=master cadquery=master python=3.9
# conda install -c conda-forge pyinstaller=4.10
# conda uninstall --force -y importlib_resources
# pip install path
# pip install git+https://github.com/gumyr/cq_warehouse.git#egg=cq_warehouse
# pip install git+https://github.com/meadiode/cq_gears.git@main
# pip install -e "git+https://github.com/CadQuery/cadquery-plugins.git#egg=cq_cache&subdirectory=plugins/cq_cache"
# pip install git+https://github.com/gumyr/build123d.git#egg=build123d
# - name: Run build
# shell: bash --login {0}
# run: |
# conda info
# pyinstaller pyinstaller.spec ${{ github.event.inputs.type }}
# cp /home/runner/work/jmwright-CQ-Editor/jmwright-CQ-Editor/pyinstaller/CQ-editor.sh /home/runner/work/jmwright-CQ-Editor/jmwright-CQ-Editor/dist/
# rm /home/runner/work/jmwright-CQ-Editor/jmwright-CQ-Editor/dist/CQ-editor/libstdc++.so.6
# - uses: actions/upload-artifact@v2
# with:
# name: CQ-editor-Linux-x86_64
# path: dist
# build-macos:
# runs-on: macos-latest
# steps:
# - uses: actions/checkout@v2
# - uses: conda-incubator/setup-miniconda@v2
# with:
# mamba-version: "*"
# channels: conda-forge,defaults
# # auto-update-conda: true
# python-version: 3.9
# activate-environment: test
# - name: Install CadQuery, CQ-editor and pyinstaller=4.10
# shell: bash --login {0}
# run: |
# conda info
# conda install -c cadquery -c conda-forge cq-editor=master cadquery=master python=3.9
# conda install -c conda-forge pyinstaller
# conda uninstall --force -y importlib_resources
# pip install path
# pip uninstall -y PyQt5
# pip install PyQt5==5.15.7
# pip install PyQtWebEngine==5.15.6
# pip install git+https://github.com/gumyr/cq_warehouse.git#egg=cq_warehouse
# pip install git+https://github.com/meadiode/cq_gears.git@main
# pip install -e "git+https://github.com/CadQuery/cadquery-plugins.git#egg=cq_cache&subdirectory=plugins/cq_cache"
# pip install git+https://github.com/gumyr/build123d.git#egg=build123d
# - name: Run build
# shell: bash --login {0}
# run: |
# conda info
# pyinstaller pyinstaller.spec ${{ github.event.inputs.type }}
# cp /Users/runner/work/jmwright-CQ-Editor/jmwright-CQ-Editor/pyinstaller/CQ-editor.sh /Users/runner/work/jmwright-CQ-Editor/jmwright-CQ-Editor/dist/
# - uses: actions/upload-artifact@v2
# with:
# name: CQ-editor-MacOS
# path: dist
build-windows:
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
- uses: conda-incubator/setup-miniconda@v2
with:
miniconda-version: "latest"
auto-update-conda: true
python-version: 3.9
activate-environment: test
- name: Install CadQuery and pyinstaller
shell: powershell
run: |
conda install -c cadquery -c conda-forge cq-editor=master cadquery=master ipython=7.20 python=3.9
conda install -c conda-forge pyinstaller=4.10
pip install pipwin
pipwin install numpy
pip install path
pip install git+https://github.com/gumyr/cq_warehouse.git#egg=cq_warehouse
pip install git+https://github.com/meadiode/cq_gears.git@main
pip install -e "git+https://github.com/CadQuery/cadquery-plugins.git#egg=cq_cache&subdirectory=plugins/cq_cache"
pip install git+https://github.com/gumyr/build123d.git#egg=build123d
- name: Run build
shell: powershell
run: |
conda info
pyinstaller --debug all pyinstaller.spec ${{ github.event.inputs.type }}
Copy-Item C:\Miniconda3\Library\bin\libssl-1_1-x64.dll D:\a\jmwright-CQ-Editor\jmwright-CQ-Editor\dist\CQ-editor\
Copy-Item C:\Miniconda3\Library\bin\libcrypto-1_1-x64.dll D:\a\jmwright-CQ-Editor\jmwright-CQ-Editor\dist\CQ-editor\
Copy-Item D:\a\jmwright-CQ-Editor\jmwright-CQ-Editor\pyinstaller\CQ-editor.cmd D:\a\jmwright-CQ-Editor\jmwright-CQ-Editor\dist\
- uses: actions/upload-artifact@v2
with:
name: CQ-editor-Windows
path: dist

View File

@@ -1,6 +1,6 @@
# CadQuery editor # CadQuery editor
This is a fork of [jmwright's fork](https://github.com/jmwright/CQ-editor) of [CadQuery/CQ-editor](https://github.com/CadQuery/CQ-editor). This fork includes changes that enable dark mode for CQ-editor (see screenshot below). Under the GitHub Actions menu this fork also contains static builds of CQ-editor for Linux/MacOS/Windows that include the [cq_gears](https://github.com/meadiode/cq_gears), [cq_cache](https://github.com/CadQuery/cadquery-plugins/tree/main/plugins/cq_cache), [cq_more](https://github.com/JustinSDK/cqMore), [cq_warehouse](https://github.com/gumyr/cq_warehouse), and [build123d](https://github.com/gumyr/build123d) libraries. Note you need to change color preferences to enable dark mode for all panes (see Edit -> Preferences). This is a fork of [jmwright's fork](https://github.com/jmwright/CQ-editor) of [CadQuery/CQ-editor](https://github.com/CadQuery/CQ-editor). This fork includes changes that enable dark mode for CQ-editor (see screenshot below). Under the GitHub Actions menu this fork also contains static builds of CQ-editor for Linux/MacOS/Windows that include the [cq_gears](https://github.com/meadiode/cq_gears), [cq_cache](https://github.com/CadQuery/cadquery-plugins/tree/main/plugins/cq_cache), [cq_more](https://github.com/JustinSDK/cqMore), [cq_warehouse](https://github.com/gumyr/cq_warehouse), [bd_warehouse](https://github.com/gumyr/bd_warehouse), and [build123d](https://github.com/gumyr/build123d) libraries. Note you need to change color preferences to enable dark mode for all panes (see Edit -> Preferences).
This fork also contains additional changes to the `show_object` function in CQ-editor that make it easier to display and export build123d objects and object lists. This fork also contains additional changes to the `show_object` function in CQ-editor that make it easier to display and export build123d objects and object lists.

View File

@@ -1,7 +1,7 @@
import sys import sys
import argparse import argparse
from PySide6.QtWidgets import QApplication from PyQt5.QtWidgets import QApplication
NAME = 'CQ-editor' NAME = 'CQ-editor'

View File

@@ -15,7 +15,7 @@ from OCP.Quantity import (
) )
from OCP.Graphic3d import Graphic3d_NOM_JADE, Graphic3d_MaterialAspect from OCP.Graphic3d import Graphic3d_NOM_JADE, Graphic3d_MaterialAspect
from PySide6.QtGui import QColor from PyQt5.QtGui import QColor
DEFAULT_FACE_COLOR = Quantity_Color(GOLD) DEFAULT_FACE_COLOR = Quantity_Color(GOLD)
DEFAULT_MATERIAL = Graphic3d_MaterialAspect(Graphic3d_NOM_JADE) DEFAULT_MATERIAL = Graphic3d_MaterialAspect(Graphic3d_NOM_JADE)

View File

@@ -3,9 +3,9 @@ import spyder.utils.encoding
from modulefinder import ModuleFinder from modulefinder import ModuleFinder
from spyder.plugins.editor.widgets.codeeditor import CodeEditor from spyder.plugins.editor.widgets.codeeditor import CodeEditor
from PySide6.QtCore import Signal as pyqtSignal, QFileSystemWatcher, QTimer from PyQt5.QtCore import pyqtSignal, QFileSystemWatcher, QTimer
from PySide6.QtWidgets import QAction, QFileDialog from PyQt5.QtWidgets import QAction, QFileDialog
from PySide6.QtGui import QFontDatabase from PyQt5.QtGui import QFontDatabase
from path import Path from path import Path
import sys import sys
@@ -17,104 +17,91 @@ from ..utils import get_save_filename, get_open_filename, confirm
from ..icons import icon from ..icons import icon
class Editor(CodeEditor,ComponentMixin):
class Editor(CodeEditor, ComponentMixin): name = 'Code Editor'
name = "Code Editor"
# This signal is emitted whenever the currently-open file changes and # This signal is emitted whenever the currently-open file changes and
# autoreload is enabled. # autoreload is enabled.
triggerRerender = pyqtSignal(bool) triggerRerender = pyqtSignal(bool)
sigFilenameChanged = pyqtSignal(str) sigFilenameChanged = pyqtSignal(str)
preferences = Parameter.create( preferences = Parameter.create(name='Preferences',children=[
name="Preferences", {'name': 'Font size', 'type': 'int', 'value': 12},
children=[ {'name': 'Autoreload', 'type': 'bool', 'value': False},
{"name": "Font size", "type": "int", "value": 12}, {'name': 'Autoreload delay', 'type': 'int', 'value': 50},
{"name": "Autoreload", "type": "bool", "value": False}, {'name': 'Autoreload: watch imported modules', 'type': 'bool', 'value': False},
{"name": "Autoreload delay", "type": "int", "value": 50}, {'name': 'Line wrap', 'type': 'bool', 'value': False},
{ {'name': 'Color scheme', 'type': 'list',
"name": "Autoreload: watch imported modules", 'values': ['Spyder','Monokai','Zenburn'], 'value': 'Spyder'}])
"type": "bool",
"value": False,
},
{"name": "Line wrap", "type": "bool", "value": False},
{
"name": "Color scheme",
"type": "list",
"values": ["Spyder", "Monokai", "Zenburn"],
"value": "Spyder",
},
],
)
EXTENSIONS = "py" EXTENSIONS = 'py'
def __init__(self,parent=None):
def __init__(self, parent=None):
self._watched_file = None self._watched_file = None
super(Editor, self).__init__(parent) super(Editor,self).__init__(parent)
ComponentMixin.__init__(self) ComponentMixin.__init__(self)
self.setup_editor( self.setup_editor(linenumbers=True,
linenumbers=True, markers=True,
markers=True, edge_line=False,
edge_line=False, tab_mode=False,
tab_mode=False, show_blanks=True,
show_blanks=True, font=QFontDatabase.systemFont(QFontDatabase.FixedFont),
font=QFontDatabase.systemFont(QFontDatabase.FixedFont), language='Python',
language="Python", filename='')
filename="",
)
self._actions = { self._actions = \
"File": [ {'File' : [QAction(icon('new'),
QAction( 'New',
icon("new"), "New", self, shortcut="ctrl+N", triggered=self.new self,
), shortcut='ctrl+N',
QAction( triggered=self.new),
icon("open"), "Open", self, shortcut="ctrl+O", triggered=self.open QAction(icon('open'),
), 'Open',
QAction( self,
icon("save"), "Save", self, shortcut="ctrl+S", triggered=self.save shortcut='ctrl+O',
), triggered=self.open),
QAction( QAction(icon('save'),
icon("save_as"), 'Save',
"Save as", self,
self, shortcut='ctrl+S',
shortcut="ctrl+shift+S", triggered=self.save),
triggered=self.save_as, QAction(icon('save_as'),
), 'Save as',
QAction( self,
icon("autoreload"), shortcut='ctrl+shift+S',
"Automatic reload and preview", triggered=self.save_as),
self, QAction(icon('autoreload'),
triggered=self.autoreload, 'Automatic reload and preview',
checkable=True, self,triggered=self.autoreload,
checked=False, checkable=True,
objectName="autoreload", checked=False,
), objectName='autoreload'),
] ]}
}
for a in self._actions.values(): for a in self._actions.values():
self.addActions(a) self.addActions(a)
self._fixContextMenu() self._fixContextMenu()
# autoreload support # autoreload support
self._file_watcher = QFileSystemWatcher(self) self._file_watcher = QFileSystemWatcher(self)
# we wait for 50ms after a file change for the file to be written completely # we wait for 50ms after a file change for the file to be written completely
self._file_watch_timer = QTimer(self) self._file_watch_timer = QTimer(self)
self._file_watch_timer.setInterval(self.preferences["Autoreload delay"]) self._file_watch_timer.setInterval(self.preferences['Autoreload delay'])
self._file_watch_timer.setSingleShot(True) self._file_watch_timer.setSingleShot(True)
self._file_watcher.fileChanged.connect( self._file_watcher.fileChanged.connect(
lambda val: self._file_watch_timer.start() lambda val: self._file_watch_timer.start())
)
self._file_watch_timer.timeout.connect(self._file_changed) self._file_watch_timer.timeout.connect(self._file_changed)
self.updatePreferences() self.updatePreferences()
def _fixContextMenu(self): def _fixContextMenu(self):
menu = self.menu menu = self.menu
menu.removeAction(self.run_cell_action) menu.removeAction(self.run_cell_action)
@@ -122,52 +109,52 @@ class Editor(CodeEditor, ComponentMixin):
menu.removeAction(self.run_selection_action) menu.removeAction(self.run_selection_action)
menu.removeAction(self.re_run_last_cell_action) menu.removeAction(self.re_run_last_cell_action)
def updatePreferences(self, *args): def updatePreferences(self,*args):
self.set_color_scheme(self.preferences["Color scheme"])
self.set_color_scheme(self.preferences['Color scheme'])
font = self.font() font = self.font()
font.setPointSize(self.preferences["Font size"]) font.setPointSize(self.preferences['Font size'])
self.set_font(font) self.set_font(font)
self.findChild(QAction, "autoreload").setChecked(self.preferences["Autoreload"]) self.findChild(QAction, 'autoreload') \
.setChecked(self.preferences['Autoreload'])
self._file_watch_timer.setInterval(self.preferences["Autoreload delay"]) self._file_watch_timer.setInterval(self.preferences['Autoreload delay'])
self.toggle_wrap_mode(self.preferences["Line wrap"]) self.toggle_wrap_mode(self.preferences['Line wrap'])
self._clear_watched_paths() self._clear_watched_paths()
self._watch_paths() self._watch_paths()
def confirm_discard(self): def confirm_discard(self):
if self.modified: if self.modified:
rv = confirm( rv = confirm(self,'Please confirm','Current document is not saved - do you want to continue?')
self,
"Please confirm",
"Current document is not saved - do you want to continue?",
)
else: else:
rv = True rv = True
return rv return rv
def new(self): def new(self):
if not self.confirm_discard():
return
self.set_text("") if not self.confirm_discard(): return
self.filename = ""
self.set_text('')
self.filename = ''
self.reset_modified() self.reset_modified()
def open(self): def open(self):
if not self.confirm_discard():
return if not self.confirm_discard(): return
curr_dir = Path(self.filename).abspath().dirname() curr_dir = Path(self.filename).absolute().dirname()
fname = get_open_filename(self.EXTENSIONS, curr_dir) fname = get_open_filename(self.EXTENSIONS, curr_dir)
if fname != "": if fname != '':
self.load_from_file(fname) self.load_from_file(fname)
def load_from_file(self, fname): def load_from_file(self,fname):
self.set_text_from_file(fname) self.set_text_from_file(fname)
self.filename = fname self.filename = fname
self.reset_modified() self.reset_modified()
@@ -177,22 +164,24 @@ class Editor(CodeEditor, ComponentMixin):
# this function returns the encoding spyder used to read the file # this function returns the encoding spyder used to read the file
_, encoding = spyder.utils.encoding.read(fname) _, encoding = spyder.utils.encoding.read(fname)
# spyder returns a -guessed suffix in some cases # spyder returns a -guessed suffix in some cases
return encoding.replace("-guessed", "") return encoding.replace('-guessed', '')
else: else:
return "utf-8" return 'utf-8'
def save(self): def save(self):
if self._filename != "":
if self.preferences["Autoreload"]: if self._filename != '':
if self.preferences['Autoreload']:
self._file_watcher.blockSignals(True) self._file_watcher.blockSignals(True)
self._file_watch_timer.stop() self._file_watch_timer.stop()
encoding = self.determine_encoding(self._filename) encoding = self.determine_encoding(self._filename)
encoded = self.toPlainText().encode(encoding) encoded = self.toPlainText().encode(encoding)
with open(self._filename, "wb") as f: with open(self._filename, 'wb') as f:
f.write(encoded) f.write(encoded)
if self.preferences["Autoreload"]: if self.preferences['Autoreload']:
self._file_watcher.blockSignals(False) self._file_watcher.blockSignals(False)
self.triggerRerender.emit(True) self.triggerRerender.emit(True)
@@ -202,26 +191,21 @@ class Editor(CodeEditor, ComponentMixin):
self.save_as() self.save_as()
def save_as(self): def save_as(self):
fname = get_save_filename(self.EXTENSIONS) fname = get_save_filename(self.EXTENSIONS)
if fname != "": if fname != '':
encoded = self.toPlainText().encode("utf-8") encoded = self.toPlainText().encode('utf-8')
with open(fname, "wb") as f: with open(fname, 'wb') as f:
f.write(encoded) f.write(encoded)
self.filename = fname self.filename = fname
self.reset_modified() self.reset_modified()
def _update_filewatcher(self): def _update_filewatcher(self):
if self._watched_file and ( if self._watched_file and (self._watched_file != self.filename or not self.preferences['Autoreload']):
self._watched_file != self.filename or not self.preferences["Autoreload"]
):
self._clear_watched_paths() self._clear_watched_paths()
self._watched_file = None self._watched_file = None
if ( if self.preferences['Autoreload'] and self.filename and self.filename != self._watched_file:
self.preferences["Autoreload"]
and self.filename
and self.filename != self._watched_file
):
self._watched_file = self._filename self._watched_file = self._filename
self._watch_paths() self._watch_paths()
@@ -243,8 +227,8 @@ class Editor(CodeEditor, ComponentMixin):
def _watch_paths(self): def _watch_paths(self):
if Path(self._filename).exists(): if Path(self._filename).exists():
self._file_watcher.addPath(self._filename) self._file_watcher.addPath(self._filename)
if self.preferences["Autoreload: watch imported modules"]: if self.preferences['Autoreload: watch imported modules']:
module_paths = self.get_imported_module_paths(self._filename) module_paths = self.get_imported_module_paths(self._filename)
if module_paths: if module_paths:
self._file_watcher.addPaths(module_paths) self._file_watcher.addPaths(module_paths)
@@ -257,45 +241,51 @@ class Editor(CodeEditor, ComponentMixin):
# Turn autoreload on/off. # Turn autoreload on/off.
def autoreload(self, enabled): def autoreload(self, enabled):
self.preferences["Autoreload"] = enabled self.preferences['Autoreload'] = enabled
self._update_filewatcher() self._update_filewatcher()
def reset_modified(self): def reset_modified(self):
self.document().setModified(False)
self.document().setModified(False)
@property @property
def modified(self): def modified(self):
return self.document().isModified() return self.document().isModified()
def saveComponentState(self, store): def saveComponentState(self,store):
if self.filename != "":
store.setValue(self.name + "/state", self.filename)
def restoreComponentState(self, store): if self.filename != '':
filename = store.value(self.name + "/state") store.setValue(self.name+'/state',self.filename)
if filename and self.filename == "": def restoreComponentState(self,store):
filename = store.value(self.name+'/state')
if filename and self.filename == '':
try: try:
self.load_from_file(filename) self.load_from_file(filename)
except IOError: except IOError:
self._logger.warning(f"could not open {filename}") self._logger.warning(f'could not open {filename}')
def get_imported_module_paths(self, module_path): def get_imported_module_paths(self, module_path):
finder = ModuleFinder([os.path.dirname(module_path)]) finder = ModuleFinder([os.path.dirname(module_path)])
imported_modules = [] imported_modules = []
try: try:
finder.run_script(module_path) finder.run_script(module_path)
except SyntaxError as err: except SyntaxError as err:
self._logger.warning(f"Syntax error in {module_path}: {err}") self._logger.warning(f'Syntax error in {module_path}: {err}')
except Exception as err: except Exception as err:
self._logger.warning( self._logger.warning(
f"Cannot determine imported modules in {module_path}: {type(err).__name__} {err}" f'Cannot determine imported modules in {module_path}: {type(err).__name__} {err}'
) )
else: else:
for module_name, module in finder.modules.items(): for module_name, module in finder.modules.items():
if module_name != "__main__": if module_name != '__main__':
path = getattr(module, "__file__", None) path = getattr(module, '__file__', None)
if path is not None and os.path.isfile(path): if path is not None and os.path.isfile(path):
imported_modules.append(path) imported_modules.append(path)
@@ -303,7 +293,8 @@ class Editor(CodeEditor, ComponentMixin):
if __name__ == "__main__": if __name__ == "__main__":
from PySide6.QtWidgets import QApplication
from PyQt5.QtWidgets import QApplication
app = QApplication(sys.argv) app = QApplication(sys.argv)
editor = Editor() editor = Editor()

View File

@@ -6,7 +6,7 @@ Created on Fri May 25 14:47:10 2018
@author: adam @author: adam
""" """
from PySide6.QtGui import QIcon from PyQt5.QtGui import QIcon
from . import icons_res from . import icons_res
_icons = { _icons = {
@@ -56,4 +56,4 @@ def icon(name):
args,kwargs = _icons_specs[name] args,kwargs = _icons_specs[name]
return qta.icon(*args,**kwargs) return qta.icon(*args,**kwargs)

View File

@@ -6,7 +6,7 @@
# #
# WARNING! All changes made in this file will be lost! # WARNING! All changes made in this file will be lost!
from PySide6 import QtCore from PyQt5 import QtCore
qt_resource_data = b"\ qt_resource_data = b"\
\x00\x00\x4e\x4c\ \x00\x00\x4e\x4c\

View File

@@ -1,8 +1,6 @@
import sys import sys
from PySide6.QtWidgets import QLabel, QMainWindow, QToolBar, QDockWidget from PyQt5.QtWidgets import (QLabel, QMainWindow, QToolBar, QDockWidget, QAction)
from PySide6.QtGui import QAction
from PySide6.QtCore import Signal as pyqtSignal
from logbook import Logger from logbook import Logger
import cadquery as cq import cadquery as cq
@@ -16,26 +14,16 @@ from .widgets.cq_object_inspector import CQObjectInspector
from .widgets.log import LogViewer from .widgets.log import LogViewer
from . import __version__ from . import __version__
from .utils import ( from .utils import dock, add_actions, open_url, about_dialog, check_gtihub_for_updates, confirm
dock,
add_actions,
open_url,
about_dialog,
check_gtihub_for_updates,
confirm,
)
from .mixins import MainMixin from .mixins import MainMixin
from .icons import icon from .icons import icon
from .preferences import PreferencesWidget from .preferences import PreferencesWidget
# DARKMODE edits: https://stackoverflow.com/questions/48256772/dark-theme-for-qt-widgets #DARKMODE edits: https://stackoverflow.com/questions/48256772/dark-theme-for-qt-widgets
from PySide6.QtCore import Qt from PyQt5.QtCore import Qt
from PySide6.QtWidgets import QApplication from PyQt5.QtWidgets import QApplication
from PySide6.QtGui import QPalette, QColor from PyQt5.QtGui import QPalette, QColor
app = QApplication([])
app = QApplication.instance()
if app == None:
app = QApplication([])
# Force the style to be the same on all OSs: # Force the style to be the same on all OSs:
app.setStyle("Fusion") app.setStyle("Fusion")
# Now use a palette to switch to dark colors: # Now use a palette to switch to dark colors:
@@ -55,131 +43,141 @@ palette.setColor(QPalette.Highlight, QColor(42, 130, 218))
palette.setColor(QPalette.HighlightedText, Qt.black) palette.setColor(QPalette.HighlightedText, Qt.black)
app.setPalette(palette) app.setPalette(palette)
class MainWindow(QMainWindow,MainMixin):
class MainWindow(QMainWindow, MainMixin): name = 'CQ-Editor'
name = "CQ-Editor" org = 'CadQuery'
org = "CadQuery"
def __init__(self, parent=None, filename=None): def __init__(self,parent=None, filename=None):
super(MainWindow, self).__init__(parent)
super(MainWindow,self).__init__(parent)
MainMixin.__init__(self) MainMixin.__init__(self)
self.setWindowIcon(icon("app")) self.setWindowIcon(icon('app'))
self.viewer = OCCViewer(self) self.viewer = OCCViewer(self)
# self.setCentralWidget(self.viewer.canvas) self.setCentralWidget(self.viewer.canvas)
# self.prepare_panes() self.prepare_panes()
# self.registerComponent("viewer", self.viewer) self.registerComponent('viewer',self.viewer)
# self.prepare_toolbar() self.prepare_toolbar()
# self.prepare_menubar() self.prepare_menubar()
# self.prepare_statusbar() self.prepare_statusbar()
# self.prepare_actions() self.prepare_actions()
self.components['object_tree'].addLines()
# self.components["object_tree"].addLines() self.prepare_console()
# self.prepare_console() self.fill_dummy()
# self.fill_dummy() self.setup_logging()
# self.setup_logging() self.restorePreferences()
self.restoreWindow()
# self.restorePreferences() if filename:
# self.restoreWindow() self.components['editor'].load_from_file(filename)
# if filename: self.restoreComponentState()
# self.components["editor"].load_from_file(filename)
# self.restoreComponentState() def closeEvent(self,event):
def closeEvent(self, event):
self.saveWindow() self.saveWindow()
self.savePreferences() self.savePreferences()
self.saveComponentState() self.saveComponentState()
if self.components["editor"].document().isModified(): if self.components['editor'].document().isModified():
rv = confirm(self, "Confirm close", "Close without saving?")
rv = confirm(self, 'Confirm close', 'Close without saving?')
if rv: if rv:
event.accept() event.accept()
super(MainWindow, self).closeEvent(event) super(MainWindow,self).closeEvent(event)
else: else:
event.ignore() event.ignore()
else: else:
super(MainWindow, self).closeEvent(event) super(MainWindow,self).closeEvent(event)
def prepare_panes(self): def prepare_panes(self):
self.registerComponent(
"editor",
Editor(self),
lambda c: dock(c, "Editor", self, defaultArea="left"),
)
self.registerComponent( self.registerComponent('editor',
"object_tree", Editor(self),
ObjectTree(self), lambda c : dock(c,
lambda c: dock(c, "Objects", self, defaultArea="right"), 'Editor',
) self,
defaultArea='left'))
self.registerComponent( self.registerComponent('object_tree',
"console", ObjectTree(self),
ConsoleWidget(self), lambda c: dock(c,
lambda c: dock(c, "Console", self, defaultArea="bottom"), 'Objects',
) self,
defaultArea='right'))
self.registerComponent( self.registerComponent('console',
"traceback_viewer", ConsoleWidget(self),
TracebackPane(self), lambda c: dock(c,
lambda c: dock(c, "Current traceback", self, defaultArea="bottom"), 'Console',
) self,
defaultArea='bottom'))
self.registerComponent("debugger", Debugger(self)) self.registerComponent('traceback_viewer',
TracebackPane(self),
lambda c: dock(c,
'Current traceback',
self,
defaultArea='bottom'))
self.registerComponent( self.registerComponent('debugger',Debugger(self))
"variables_viewer",
LocalsView(self),
lambda c: dock(c, "Variables", self, defaultArea="right"),
)
self.registerComponent( self.registerComponent('variables_viewer',LocalsView(self),
"cq_object_inspector", lambda c: dock(c,
CQObjectInspector(self), 'Variables',
lambda c: dock(c, "CQ object inspector", self, defaultArea="right"), self,
) defaultArea='right'))
self.registerComponent(
"log", self.registerComponent('cq_object_inspector',
LogViewer(self), CQObjectInspector(self),
lambda c: dock(c, "Log viewer", self, defaultArea="bottom"), lambda c: dock(c,
) 'CQ object inspector',
self,
defaultArea='right'))
self.registerComponent('log',
LogViewer(self),
lambda c: dock(c,
'Log viewer',
self,
defaultArea='bottom'))
for d in self.docks.values(): for d in self.docks.values():
d.show() d.show()
def prepare_menubar(self): def prepare_menubar(self):
menu = self.menuBar() menu = self.menuBar()
menu_file = menu.addMenu("&File") menu_file = menu.addMenu('&File')
menu_edit = menu.addMenu("&Edit") menu_edit = menu.addMenu('&Edit')
menu_tools = menu.addMenu("&Tools") menu_tools = menu.addMenu('&Tools')
menu_run = menu.addMenu("&Run") menu_run = menu.addMenu('&Run')
menu_view = menu.addMenu("&View") menu_view = menu.addMenu('&View')
menu_help = menu.addMenu("&Help") menu_help = menu.addMenu('&Help')
# per component menu elements #per component menu elements
menus = { menus = {'File' : menu_file,
"File": menu_file, 'Edit' : menu_edit,
"Edit": menu_edit, 'Run' : menu_run,
"Run": menu_run, 'Tools': menu_tools,
"Tools": menu_tools, 'View' : menu_view,
"View": menu_view, 'Help' : menu_help}
"Help": menu_help,
}
for comp in self.components.values(): for comp in self.components.values():
self.prepare_menubar_component(menus, comp.menuActions()) self.prepare_menubar_component(menus,
comp.menuActions())
# global menu elements #global menu elements
menu_view.addSeparator() menu_view.addSeparator()
for d in self.findChildren(QDockWidget): for d in self.findChildren(QDockWidget):
menu_view.addAction(d.toggleViewAction()) menu_view.addAction(d.toggleViewAction())
@@ -188,200 +186,181 @@ class MainWindow(QMainWindow, MainMixin):
for t in self.findChildren(QToolBar): for t in self.findChildren(QToolBar):
menu_view.addAction(t.toggleViewAction()) menu_view.addAction(t.toggleViewAction())
menu_edit.addAction( menu_edit.addAction( \
QAction( QAction(icon('preferences'),
icon("preferences"), 'Preferences',
"Preferences", self,triggered=self.edit_preferences))
self,
triggered=self.edit_preferences,
)
)
menu_help.addAction( menu_help.addAction( \
QAction(icon("help"), "Documentation", self, triggered=self.documentation) QAction(icon('help'),
) 'Documentation',
self,triggered=self.documentation))
menu_help.addAction( menu_help.addAction( \
QAction("CQ documentation", self, triggered=self.cq_documentation) QAction('CQ documentation',
) self,triggered=self.cq_documentation))
menu_help.addAction(QAction(icon("about"), "About", self, triggered=self.about)) menu_help.addAction( \
QAction(icon('about'),
'About',
self,triggered=self.about))
menu_help.addAction( \
QAction('Check for CadQuery updates',
self,triggered=self.check_for_cq_updates))
menu_help.addAction( def prepare_menubar_component(self,menus,comp_menu_dict):
QAction(
"Check for CadQuery updates", self, triggered=self.check_for_cq_updates
)
)
def prepare_menubar_component(self, menus, comp_menu_dict): for name,action in comp_menu_dict.items():
for name, action in comp_menu_dict.items():
menus[name].addActions(action) menus[name].addActions(action)
def prepare_toolbar(self): def prepare_toolbar(self):
self.toolbar = QToolBar("Main toolbar", self, objectName="Main toolbar")
self.toolbar = QToolBar('Main toolbar',self,objectName='Main toolbar')
for c in self.components.values(): for c in self.components.values():
add_actions(self.toolbar, c.toolbarActions()) add_actions(self.toolbar,c.toolbarActions())
self.addToolBar(self.toolbar) self.addToolBar(self.toolbar)
def prepare_statusbar(self): def prepare_statusbar(self):
self.status_label = QLabel("", parent=self)
self.status_label = QLabel('',parent=self)
self.statusBar().insertPermanentWidget(0, self.status_label) self.statusBar().insertPermanentWidget(0, self.status_label)
def prepare_actions(self): def prepare_actions(self):
self.components["debugger"].sigRendered.connect(
self.components["object_tree"].addObjects
)
self.components["debugger"].sigTraceback.connect(
self.components["traceback_viewer"].addTraceback
)
self.components["debugger"].sigLocals.connect(
self.components["variables_viewer"].update_frame
)
self.components["debugger"].sigLocals.connect(
self.components["console"].push_vars
)
self.components["object_tree"].sigObjectsAdded[list].connect( self.components['debugger'].sigRendered\
self.components["viewer"].display_many .connect(self.components['object_tree'].addObjects)
) self.components['debugger'].sigTraceback\
self.components["object_tree"].sigObjectsAdded2[list, bool].connect( .connect(self.components['traceback_viewer'].addTraceback)
self.components["viewer"].display_many self.components['debugger'].sigLocals\
) .connect(self.components['variables_viewer'].update_frame)
self.components["object_tree"].sigItemChanged.connect( self.components['debugger'].sigLocals\
self.components["viewer"].update_item .connect(self.components['console'].push_vars)
)
self.components["object_tree"].sigObjectsRemoved.connect(
self.components["viewer"].remove_items
)
self.components["object_tree"].sigCQObjectSelected.connect(
self.components["cq_object_inspector"].setObject
)
self.components["object_tree"].sigObjectPropertiesChanged.connect(
self.components["viewer"].redraw
)
self.components["object_tree"].sigAISObjectsSelected.connect(
self.components["viewer"].set_selected
)
self.components["viewer"].sigObjectSelected.connect( self.components['object_tree'].sigObjectsAdded[list]\
self.components["object_tree"].handleGraphicalSelection .connect(self.components['viewer'].display_many)
) self.components['object_tree'].sigObjectsAdded[list,bool]\
.connect(self.components['viewer'].display_many)
self.components['object_tree'].sigItemChanged.\
connect(self.components['viewer'].update_item)
self.components['object_tree'].sigObjectsRemoved\
.connect(self.components['viewer'].remove_items)
self.components['object_tree'].sigCQObjectSelected\
.connect(self.components['cq_object_inspector'].setObject)
self.components['object_tree'].sigObjectPropertiesChanged\
.connect(self.components['viewer'].redraw)
self.components['object_tree'].sigAISObjectsSelected\
.connect(self.components['viewer'].set_selected)
self.components["traceback_viewer"].sigHighlightLine.connect( self.components['viewer'].sigObjectSelected\
self.components["editor"].go_to_line .connect(self.components['object_tree'].handleGraphicalSelection)
)
self.components["cq_object_inspector"].sigDisplayObjects.connect( self.components['traceback_viewer'].sigHighlightLine\
self.components["viewer"].display_many .connect(self.components['editor'].go_to_line)
)
self.components["cq_object_inspector"].sigRemoveObjects.connect(
self.components["viewer"].remove_items
)
self.components["cq_object_inspector"].sigShowPlane.connect(
self.components["viewer"].toggle_grid
)
self.components["cq_object_inspector"].sigShowPlane2[bool, float].connect(
self.components["viewer"].toggle_grid
)
self.components["cq_object_inspector"].sigChangePlane.connect(
self.components["viewer"].set_grid_orientation
)
self.components["debugger"].sigLocalsChanged.connect( self.components['cq_object_inspector'].sigDisplayObjects\
self.components["variables_viewer"].update_frame .connect(self.components['viewer'].display_many)
) self.components['cq_object_inspector'].sigRemoveObjects\
self.components["debugger"].sigLineChanged.connect( .connect(self.components['viewer'].remove_items)
self.components["editor"].go_to_line self.components['cq_object_inspector'].sigShowPlane\
) .connect(self.components['viewer'].toggle_grid)
self.components["debugger"].sigDebugging.connect( self.components['cq_object_inspector'].sigShowPlane[bool,float]\
self.components["object_tree"].stashObjects .connect(self.components['viewer'].toggle_grid)
) self.components['cq_object_inspector'].sigChangePlane\
self.components["debugger"].sigCQChanged.connect( .connect(self.components['viewer'].set_grid_orientation)
self.components["object_tree"].addObjects
) self.components['debugger'].sigLocalsChanged\
self.components["debugger"].sigTraceback.connect( .connect(self.components['variables_viewer'].update_frame)
self.components["traceback_viewer"].addTraceback self.components['debugger'].sigLineChanged\
) .connect(self.components['editor'].go_to_line)
self.components['debugger'].sigDebugging\
.connect(self.components['object_tree'].stashObjects)
self.components['debugger'].sigCQChanged\
.connect(self.components['object_tree'].addObjects)
self.components['debugger'].sigTraceback\
.connect(self.components['traceback_viewer'].addTraceback)
# trigger re-render when file is modified externally or saved # trigger re-render when file is modified externally or saved
self.components["editor"].triggerRerender.connect( self.components['editor'].triggerRerender \
self.components["debugger"].render .connect(self.components['debugger'].render)
) self.components['editor'].sigFilenameChanged\
self.components["editor"].sigFilenameChanged.connect( .connect(self.handle_filename_change)
self.handle_filename_change
)
def prepare_console(self): def prepare_console(self):
console = self.components["console"]
obj_tree = self.components["object_tree"]
# application related items console = self.components['console']
console.push_vars({"self": self}) obj_tree = self.components['object_tree']
# CQ related items #application related items
console.push_vars( console.push_vars({'self' : self})
{
"show": obj_tree.addObject,
"show_object": obj_tree.addObject,
"rand_color": self.components["debugger"]._rand_color,
"cq": cq,
"log": Logger(self.name).info,
}
)
#CQ related items
console.push_vars({'show' : obj_tree.addObject,
'show_object' : obj_tree.addObject,
'rand_color' : self.components['debugger']._rand_color,
'cq' : cq,
'log' : Logger(self.name).info})
def fill_dummy(self): def fill_dummy(self):
self.components["editor"].set_text(
'import cadquery as cq\nresult = cq.Workplane("XY" ).box(3, 3, 0.5).edges("|Z").fillet(0.125)' self.components['editor']\
) .set_text('import cadquery as cq\nresult = cq.Workplane("XY" ).box(3, 3, 0.5).edges("|Z").fillet(0.125)')
def setup_logging(self): def setup_logging(self):
from logbook.compat import redirect_logging from logbook.compat import redirect_logging
from logbook import INFO, Logger from logbook import INFO, Logger
redirect_logging() redirect_logging()
self.components["log"].handler.level = INFO self.components['log'].handler.level = INFO
self.components["log"].handler.push_application() self.components['log'].handler.push_application()
self._logger = Logger(self.name) self._logger = Logger(self.name)
def handle_exception(exc_type, exc_value, exc_traceback): def handle_exception(exc_type, exc_value, exc_traceback):
if issubclass(exc_type, KeyboardInterrupt): if issubclass(exc_type, KeyboardInterrupt):
sys.__excepthook__(exc_type, exc_value, exc_traceback) sys.__excepthook__(exc_type, exc_value, exc_traceback)
return return
self._logger.error( self._logger.error("Uncaught exception occurred",
"Uncaught exception occurred", exc_info=(exc_type, exc_value, exc_traceback))
exc_info=(exc_type, exc_value, exc_traceback),
)
sys.excepthook = handle_exception sys.excepthook = handle_exception
def edit_preferences(self): def edit_preferences(self):
prefs = PreferencesWidget(self, self.components)
prefs = PreferencesWidget(self,self.components)
prefs.exec_() prefs.exec_()
def about(self): def about(self):
about_dialog( about_dialog(
self, self,
f"About CQ-editor", f'About CQ-editor',
f"PyQt GUI for CadQuery.\nVersion: {__version__}.\nSource Code: https://github.com/CadQuery/CQ-editor", f'PyQt GUI for CadQuery.\nVersion: {__version__}.\nSource Code: https://github.com/CadQuery/CQ-editor',
) )
def check_for_cq_updates(self): def check_for_cq_updates(self):
check_gtihub_for_updates(self, cq)
check_gtihub_for_updates(self,cq)
def documentation(self): def documentation(self):
open_url("https://github.com/CadQuery")
open_url('https://github.com/CadQuery')
def cq_documentation(self): def cq_documentation(self):
open_url("https://cadquery.readthedocs.io/en/latest/")
open_url('https://cadquery.readthedocs.io/en/latest/')
def handle_filename_change(self, fname): def handle_filename_change(self, fname):
new_title = fname if fname else "*" new_title = fname if fname else "*"
self.setWindowTitle(f"{self.name}: {new_title}") self.setWindowTitle(f"{self.name}: {new_title}")
if __name__ == "__main__": if __name__ == "__main__":
pass pass

View File

@@ -10,67 +10,72 @@ from functools import reduce
from operator import add from operator import add
from logbook import Logger from logbook import Logger
from PySide6.QtCore import QSettings from PyQt5.QtCore import pyqtSlot, QSettings
from PySide6.QtCore import Slot as pyqtSlot
class MainMixin(object): class MainMixin(object):
name = "Main"
org = "Unknown" name = 'Main'
org = 'Unknown'
components = {} components = {}
docks = {} docks = {}
preferences = None preferences = None
def __init__(self): def __init__(self):
self.settings = QSettings(self.org, self.name)
def registerComponent(self, name, component, dock=None): self.settings = QSettings(self.org,self.name)
def registerComponent(self,name,component,dock=None):
self.components[name] = component self.components[name] = component
if dock: if dock:
self.docks[name] = dock(component) self.docks[name] = dock(component)
def saveWindow(self): def saveWindow(self):
self.settings.setValue("geometry", self.saveGeometry())
self.settings.setValue("windowState", self.saveState()) self.settings.setValue('geometry',self.saveGeometry())
self.settings.setValue('windowState',self.saveState())
def restoreWindow(self): def restoreWindow(self):
if self.settings.value("geometry"):
self.restoreGeometry(self.settings.value("geometry")) if self.settings.value('geometry'):
if self.settings.value("windowState"): self.restoreGeometry(self.settings.value('geometry'))
self.restoreState(self.settings.value("windowState")) if self.settings.value('windowState'):
self.restoreState(self.settings.value('windowState'))
def savePreferences(self): def savePreferences(self):
settings = self.settings settings = self.settings
if self.preferences: if self.preferences:
settings.setValue("General", self.preferences.saveState()) settings.setValue('General',self.preferences.saveState())
for comp in (c for c in self.components.values() if c.preferences): for comp in (c for c in self.components.values() if c.preferences):
settings.setValue(comp.name, comp.preferences.saveState()) settings.setValue(comp.name,comp.preferences.saveState())
def restorePreferences(self): def restorePreferences(self):
settings = self.settings settings = self.settings
if self.preferences and settings.value("General"): if self.preferences and settings.value('General'):
self.preferences.restoreState( self.preferences.restoreState(settings.value('General'),
settings.value("General"), removeChildren=False removeChildren=False)
)
for comp in (c for c in self.components.values() if c.preferences): for comp in (c for c in self.components.values() if c.preferences):
if settings.value(comp.name): if settings.value(comp.name):
comp.preferences.restoreState( comp.preferences.restoreState(settings.value(comp.name),
settings.value(comp.name), removeChildren=False removeChildren=False)
)
def saveComponentState(self): def saveComponentState(self):
settings = self.settings settings = self.settings
for comp in self.components.values(): for comp in self.components.values():
comp.saveComponentState(settings) comp.saveComponentState(settings)
def restoreComponentState(self): def restoreComponentState(self):
settings = self.settings settings = self.settings
for comp in self.components.values(): for comp in self.components.values():
@@ -78,32 +83,42 @@ class MainMixin(object):
class ComponentMixin(object): class ComponentMixin(object):
name = "Component"
name = 'Component'
preferences = None preferences = None
_actions = {} _actions = {}
def __init__(self):
if self.preferences:
self.preferences.sigTreeStateChanged.connect(self.updatePreferences)
def __init__(self):
if self.preferences:
self.preferences.sigTreeStateChanged.\
connect(self.updatePreferences)
self._logger = Logger(self.name) self._logger = Logger(self.name)
def menuActions(self): def menuActions(self):
return self._actions return self._actions
def toolbarActions(self): def toolbarActions(self):
if len(self._actions) > 0: if len(self._actions) > 0:
return reduce(add, [a for a in self._actions.values()]) return reduce(add,[a for a in self._actions.values()])
else: else:
return [] return []
@pyqtSlot(object, object) @pyqtSlot(object,object)
def updatePreferences(self, *args): def updatePreferences(self,*args):
pass pass
def saveComponentState(self, store): def saveComponentState(self,store):
pass pass
def restoreComponentState(self, store): def restoreComponentState(self,store):
pass
pass

View File

@@ -1,5 +1,6 @@
from PySide6.QtWidgets import QTreeWidget, QTreeWidgetItem, QStackedWidget, QDialog from PyQt5.QtWidgets import (QTreeWidget, QTreeWidgetItem,
from PySide6.QtCore import Slot as pyqtSlot, Qt QStackedWidget, QDialog)
from PyQt5.QtCore import pyqtSlot, Qt
from pyqtgraph.parametertree import ParameterTree from pyqtgraph.parametertree import ParameterTree
@@ -7,54 +8,55 @@ from .utils import splitter, layout
class PreferencesTreeItem(QTreeWidgetItem): class PreferencesTreeItem(QTreeWidgetItem):
def __init__(
self, def __init__(self,name,widget,):
name,
widget, super(PreferencesTreeItem,self).__init__(name)
):
super(PreferencesTreeItem, self).__init__(name)
self.widget = widget self.widget = widget
class PreferencesWidget(QDialog): class PreferencesWidget(QDialog):
def __init__(self, parent, components):
super(PreferencesWidget, self).__init__( def __init__(self,parent,components):
parent,
Qt.Window | Qt.CustomizeWindowHint | Qt.WindowCloseButtonHint, super(PreferencesWidget,self).__init__(
windowTitle="Preferences", parent,
) Qt.Window | Qt.CustomizeWindowHint | Qt.WindowCloseButtonHint,
windowTitle='Preferences')
self.stacked = QStackedWidget(self) self.stacked = QStackedWidget(self)
self.preferences_tree = QTreeWidget( self.preferences_tree = QTreeWidget(self,
self, headerHidden=True,
headerHidden=True, itemsExpandable=False,
itemsExpandable=False, rootIsDecorated=False,
rootIsDecorated=False, columnCount=1)
columnCount=1,
)
self.root = self.preferences_tree.invisibleRootItem() self.root = self.preferences_tree.invisibleRootItem()
self.add("General", parent) self.add('General',
parent)
for v in parent.components.values(): for v in parent.components.values():
self.add(v.name, v) self.add(v.name,v)
self.splitter = splitter((self.preferences_tree, self.stacked), (2, 5)) self.splitter = splitter((self.preferences_tree,self.stacked),(2,5))
layout(self, (self.splitter,), self) layout(self,(self.splitter,),self)
self.preferences_tree.currentItemChanged.connect(self.handleSelection) self.preferences_tree.currentItemChanged.connect(self.handleSelection)
def add(self, name, component): def add(self,name,component):
if component.preferences: if component.preferences:
widget = ParameterTree() widget = ParameterTree()
widget.setHeaderHidden(True) widget.setHeaderHidden(True)
widget.setParameters(component.preferences, showTop=False) widget.setParameters(component.preferences,showTop=False)
self.root.addChild(PreferencesTreeItem((name,), widget)) self.root.addChild(PreferencesTreeItem((name,),
widget))
self.stacked.addWidget(widget) self.stacked.addWidget(widget)
@pyqtSlot(QTreeWidgetItem, QTreeWidgetItem) @pyqtSlot(QTreeWidgetItem,QTreeWidgetItem)
def handleSelection(self, item, *args): def handleSelection(self,item,*args):
if item: if item:
self.stacked.setCurrentWidget(item.widget) self.stacked.setCurrentWidget(item.widget)

View File

@@ -2,27 +2,22 @@ import requests
from pkg_resources import parse_version from pkg_resources import parse_version
from PySide6 import QtCore, QtWidgets from PyQt5 import QtCore, QtWidgets
from PySide6.QtGui import QDesktopServices from PyQt5.QtGui import QDesktopServices
from PySide6.QtCore import QUrl from PyQt5.QtCore import QUrl
from PySide6.QtWidgets import QFileDialog, QMessageBox from PyQt5.QtWidgets import QFileDialog, QMessageBox
DOCK_POSITIONS = { DOCK_POSITIONS = {'right' : QtCore.Qt.RightDockWidgetArea,
"right": QtCore.Qt.RightDockWidgetArea, 'left' : QtCore.Qt.LeftDockWidgetArea,
"left": QtCore.Qt.LeftDockWidgetArea, 'top' : QtCore.Qt.TopDockWidgetArea,
"top": QtCore.Qt.TopDockWidgetArea, 'bottom' : QtCore.Qt.BottomDockWidgetArea}
"bottom": QtCore.Qt.BottomDockWidgetArea,
}
def layout(parent,items,
def layout( top_widget = None,
parent, layout_type = QtWidgets.QVBoxLayout,
items, margin = 2,
top_widget=None, spacing = 0):
layout_type=QtWidgets.QVBoxLayout,
margin=2,
spacing=0,
):
if not top_widget: if not top_widget:
top_widget = QtWidgets.QWidget(parent) top_widget = QtWidgets.QWidget(parent)
top_widget_was_none = True top_widget_was_none = True
@@ -30,124 +25,110 @@ def layout(
top_widget_was_none = False top_widget_was_none = False
layout = layout_type(top_widget) layout = layout_type(top_widget)
top_widget.setLayout(layout) top_widget.setLayout(layout)
for item in items: for item in items: layout.addWidget(item)
layout.addWidget(item)
layout.setSpacing(spacing) layout.setSpacing(spacing)
layout.setContentsMargins(margin, margin, margin, margin) layout.setContentsMargins(margin,margin,margin,margin)
if top_widget_was_none: if top_widget_was_none:
return top_widget return top_widget
else: else:
return layout return layout
def splitter(items,
def splitter(items, stretch_factors=None, orientation=QtCore.Qt.Horizontal): stretch_factors = None,
orientation=QtCore.Qt.Horizontal):
sp = QtWidgets.QSplitter(orientation) sp = QtWidgets.QSplitter(orientation)
for item in items: for item in items: sp.addWidget(item)
sp.addWidget(item)
if stretch_factors: if stretch_factors:
for i, s in enumerate(stretch_factors): for i,s in enumerate(stretch_factors):
sp.setStretchFactor(i, s) sp.setStretchFactor(i,s)
return sp return sp
def dock(widget,
def dock( title,
widget, parent,
title, allowedAreas = QtCore.Qt.AllDockWidgetAreas,
parent, defaultArea = 'right',
allowedAreas=QtCore.Qt.AllDockWidgetAreas, name=None,
defaultArea="right", icon = None):
name=None,
icon=None, dock = QtWidgets.QDockWidget(title,parent,objectName=title)
):
dock = QtWidgets.QDockWidget(title, parent, objectName=title) if name: dock.setObjectName(name)
if icon: dock.toggleViewAction().setIcon(icon)
if name:
dock.setObjectName(name)
if icon:
dock.toggleViewAction().setIcon(icon)
dock.setAllowedAreas(allowedAreas) dock.setAllowedAreas(allowedAreas)
dock.setWidget(widget) dock.setWidget(widget)
action = dock.toggleViewAction() action = dock.toggleViewAction()
action.setText(title) action.setText(title)
dock.setFeatures( dock.setFeatures(QtWidgets.QDockWidget.DockWidgetFeatures(\
QtWidgets.QDockWidget.DockWidgetFeatures( QtWidgets.QDockWidget.AllDockWidgetFeatures))
QtWidgets.QDockWidget.DockWidgetFloatable
) parent.addDockWidget(DOCK_POSITIONS[defaultArea],
) dock)
parent.addDockWidget(DOCK_POSITIONS[defaultArea], dock)
return dock return dock
def add_actions(menu,actions):
def add_actions(menu, actions):
if len(actions) > 0: if len(actions) > 0:
menu.addActions(actions) menu.addActions(actions)
menu.addSeparator() menu.addSeparator()
def open_url(url): def open_url(url):
QDesktopServices.openUrl(QUrl(url))
QDesktopServices.openUrl(QUrl(url))
def about_dialog(parent, title, text): def about_dialog(parent,title,text):
QtWidgets.QMessageBox.about(parent, title, text)
QtWidgets.QMessageBox.about(parent,title,text)
def get_save_filename(suffix): def get_save_filename(suffix):
rv, _ = QFileDialog.getSaveFileName(filter="*.{}".format(suffix))
if rv != "" and not rv.endswith(suffix): rv,_ = QFileDialog.getSaveFileName(filter='*.{}'.format(suffix))
rv += "." + suffix if rv != '' and not rv.endswith(suffix): rv += '.'+suffix
return rv return rv
def get_open_filename(suffix, curr_dir): def get_open_filename(suffix, curr_dir):
rv, _ = QFileDialog.getOpenFileName(
directory=curr_dir, filter="*.{}".format(suffix) rv,_ = QFileDialog.getOpenFileName(directory=curr_dir, filter='*.{}'.format(suffix))
) if rv != '' and not rv.endswith(suffix): rv += '.'+suffix
if rv != "" and not rv.endswith(suffix):
rv += "." + suffix
return rv return rv
def check_gtihub_for_updates(parent,
def check_gtihub_for_updates( mod,
parent, mod, github_org="cadquery", github_proj="cadquery" github_org='cadquery',
): github_proj='cadquery'):
url = f"https://api.github.com/repos/{github_org}/{github_proj}/releases"
url = f'https://api.github.com/repos/{github_org}/{github_proj}/releases'
resp = requests.get(url).json() resp = requests.get(url).json()
newer = [ newer = [el['tag_name'] for el in resp if not el['draft'] and \
el["tag_name"] parse_version(el['tag_name']) > parse_version(mod.__version__)]
for el in resp
if not el["draft"]
and parse_version(el["tag_name"]) > parse_version(mod.__version__)
]
if newer: if newer:
title = "Updates available" title='Updates available'
text = ( text=f'There are newer versions of {github_proj} ' \
f"There are newer versions of {github_proj} " f'available on github:\n' + '\n'.join(newer)
f"available on github:\n" + "\n".join(newer)
)
else: else:
title = "No updates available" title='No updates available'
text = f"You are already using the latest version of {github_proj}" text=f'You are already using the latest version of {github_proj}'
QtWidgets.QMessageBox.about(parent, title, text) QtWidgets.QMessageBox.about(parent,title,text)
def confirm(parent,title,msg):
def confirm(parent, title, msg):
rv = QMessageBox.question(parent, title, msg, QMessageBox.Yes, QMessageBox.No) rv = QMessageBox.question(parent, title, msg, QMessageBox.Yes, QMessageBox.No)
return True if rv == QMessageBox.Yes else False return True if rv == QMessageBox.Yes else False

View File

@@ -1,23 +1,23 @@
from PySide6.QtWidgets import QApplication from PyQt5.QtWidgets import QApplication
from PySide6.QtCore import Slot as pyqtSlot from PyQt5.QtCore import pyqtSlot
from qtconsole.rich_jupyter_widget import RichJupyterWidget from qtconsole.rich_jupyter_widget import RichJupyterWidget
from qtconsole.inprocess import QtInProcessKernelManager from qtconsole.inprocess import QtInProcessKernelManager
from ..mixins import ComponentMixin from ..mixins import ComponentMixin
class ConsoleWidget(RichJupyterWidget,ComponentMixin):
class ConsoleWidget(RichJupyterWidget, ComponentMixin):
name = "Console" name = 'Console'
def __init__(self, customBanner=None, namespace=dict(), *args, **kwargs): def __init__(self, customBanner=None, namespace=dict(), *args, **kwargs):
super(ConsoleWidget, self).__init__(*args, **kwargs) super(ConsoleWidget, self).__init__(*args, **kwargs)
# if not customBanner is None: # if not customBanner is None:
# self.banner = customBanner # self.banner = customBanner
self.font_size = 6 self.font_size = 6
self.style_sheet = """<style> self.style_sheet = '''<style>
QPlainTextEdit, QTextEdit { QPlainTextEdit, QTextEdit {
background-color: #3f3f3f; background-color: #3f3f3f;
background-clip: padding; background-clip: padding;
@@ -34,14 +34,14 @@ class ConsoleWidget(RichJupyterWidget, ComponentMixin):
.in-prompt { color: navy; } .in-prompt { color: navy; }
.out-prompt { color: darkred; } .out-prompt { color: darkred; }
</style> </style>
""" '''
self.syntax_style = "zenburn" # CHANGES FOR DARKMODE self.syntax_style = 'zenburn' #CHANGES FOR DARKMODE
self.kernel_manager = kernel_manager = QtInProcessKernelManager() self.kernel_manager = kernel_manager = QtInProcessKernelManager()
kernel_manager.start_kernel(show_banner=False) kernel_manager.start_kernel(show_banner=False)
kernel_manager.kernel.gui = "qt" kernel_manager.kernel.gui = 'qt'
kernel_manager.kernel.shell.banner1 = "" kernel_manager.kernel.shell.banner1 = ""
self.kernel_client = kernel_client = self._kernel_manager.client() self.kernel_client = kernel_client = self._kernel_manager.client()
kernel_client.start_channels() kernel_client.start_channels()
@@ -51,9 +51,9 @@ class ConsoleWidget(RichJupyterWidget, ComponentMixin):
QApplication.instance().exit() QApplication.instance().exit()
self.exit_requested.connect(stop) self.exit_requested.connect(stop)
self.clear() self.clear()
self.push_vars(namespace) self.push_vars(namespace)
@pyqtSlot(dict) @pyqtSlot(dict)
@@ -70,6 +70,7 @@ class ConsoleWidget(RichJupyterWidget, ComponentMixin):
""" """
self._control.clear() self._control.clear()
def print_text(self, text): def print_text(self, text):
""" """
Prints some plain text to the console Prints some plain text to the console
@@ -81,17 +82,20 @@ class ConsoleWidget(RichJupyterWidget, ComponentMixin):
Execute a command in the frame of the console widget Execute a command in the frame of the console widget
""" """
self._execute(command, False) self._execute(command, False)
def _banner_default(self): def _banner_default(self):
return ""
return ''
if __name__ == "__main__": if __name__ == "__main__":
import sys import sys
app = QApplication(sys.argv) app = QApplication(sys.argv)
console = ConsoleWidget(customBanner="IPython console test") console = ConsoleWidget(customBanner='IPython console test')
console.show() console.show()
sys.exit(app.exec_()) sys.exit(app.exec_())

View File

@@ -1,6 +1,6 @@
from PySide6.QtWidgets import QTreeWidget, QTreeWidgetItem from PyQt5.QtWidgets import QTreeWidget, QTreeWidgetItem, QAction
from PySide6.QtCore import Qt, Slot as pyqtSlot, Signal as pyqtSignal from PyQt5.QtCore import Qt, pyqtSlot, pyqtSignal
from PySide6.QtGui import QAction
from OCP.AIS import AIS_ColoredShape from OCP.AIS import AIS_ColoredShape
from OCP.gp import gp_Ax3 from OCP.gp import gp_Ax3
@@ -10,62 +10,63 @@ from ..mixins import ComponentMixin
from ..icons import icon from ..icons import icon
class CQChildItem(QTreeWidgetItem): class CQChildItem(QTreeWidgetItem):
def __init__(self, cq_item, **kwargs):
super(CQChildItem, self).__init__( def __init__(self,cq_item,**kwargs):
[type(cq_item).__name__, str(cq_item)], **kwargs
) super(CQChildItem,self).\
__init__([type(cq_item).__name__,str(cq_item)],**kwargs)
self.cq_item = cq_item self.cq_item = cq_item
class CQStackItem(QTreeWidgetItem): class CQStackItem(QTreeWidgetItem):
def __init__(self, name, workplane=None, **kwargs):
super(CQStackItem, self).__init__([name, ""], **kwargs) def __init__(self,name,workplane=None,**kwargs):
super(CQStackItem,self).__init__([name,''],**kwargs)
self.workplane = workplane self.workplane = workplane
class CQObjectInspector(QTreeWidget, ComponentMixin): class CQObjectInspector(QTreeWidget,ComponentMixin):
name = "CQ Object Inspector"
name = 'CQ Object Inspector'
sigRemoveObjects = pyqtSignal(list) sigRemoveObjects = pyqtSignal(list)
sigDisplayObjects = pyqtSignal(list, bool) sigDisplayObjects = pyqtSignal(list,bool)
sigShowPlane = pyqtSignal(bool) sigShowPlane = pyqtSignal([bool],[bool,float])
sigShowPlane2 = pyqtSignal(bool, float)
sigChangePlane = pyqtSignal(gp_Ax3) sigChangePlane = pyqtSignal(gp_Ax3)
def __init__(self, parent): def __init__(self,parent):
super(CQObjectInspector, self).__init__(parent)
super(CQObjectInspector,self).__init__(parent)
self.setHeaderHidden(False) self.setHeaderHidden(False)
self.setRootIsDecorated(True) self.setRootIsDecorated(True)
self.setContextMenuPolicy(Qt.ActionsContextMenu) self.setContextMenuPolicy(Qt.ActionsContextMenu)
self.setColumnCount(2) self.setColumnCount(2)
self.setHeaderLabels(["Type", "Value"]) self.setHeaderLabels(['Type','Value'])
self.root = self.invisibleRootItem() self.root = self.invisibleRootItem()
self.inspected_items = [] self.inspected_items = []
self._toolbar_actions = [ self._toolbar_actions = \
QAction( [QAction(icon('inspect'),'Inspect CQ object',self,\
icon("inspect"), toggled=self.inspect,checkable=True)]
"Inspect CQ object",
self,
toggled=self.inspect,
checkable=True,
)
]
self.addActions(self._toolbar_actions) self.addActions(self._toolbar_actions)
def menuActions(self): def menuActions(self):
return {"Tools": self._toolbar_actions}
return {'Tools' : self._toolbar_actions}
def toolbarActions(self): def toolbarActions(self):
return self._toolbar_actions return self._toolbar_actions
@pyqtSlot(bool) @pyqtSlot(bool)
def inspect(self, value): def inspect(self,value):
if value: if value:
self.itemSelectionChanged.connect(self.handleSelection) self.itemSelectionChanged.connect(self.handleSelection)
self.itemSelectionChanged.emit() self.itemSelectionChanged.emit()
@@ -73,52 +74,56 @@ class CQObjectInspector(QTreeWidget, ComponentMixin):
self.itemSelectionChanged.disconnect(self.handleSelection) self.itemSelectionChanged.disconnect(self.handleSelection)
self.sigRemoveObjects.emit(self.inspected_items) self.sigRemoveObjects.emit(self.inspected_items)
self.sigShowPlane.emit(False) self.sigShowPlane.emit(False)
@pyqtSlot() @pyqtSlot()
def handleSelection(self): def handleSelection(self):
inspected_items = self.inspected_items inspected_items = self.inspected_items
self.sigRemoveObjects.emit(inspected_items) self.sigRemoveObjects.emit(inspected_items)
inspected_items.clear() inspected_items.clear()
items = self.selectedItems() items = self.selectedItems()
if len(items) == 0: if len(items) == 0:
return return
item = items[-1] item = items[-1]
if type(item) is CQStackItem: if type(item) is CQStackItem:
cq_plane = item.workplane.plane cq_plane = item.workplane.plane
dim = item.workplane.largestDimension() dim = item.workplane.largestDimension()
plane = gp_Ax3( plane = gp_Ax3(cq_plane.origin.toPnt(),
cq_plane.origin.toPnt(), cq_plane.zDir.toDir(), cq_plane.xDir.toDir() cq_plane.zDir.toDir(),
) cq_plane.xDir.toDir())
self.sigChangePlane.emit(plane) self.sigChangePlane.emit(plane)
self.sigShowPlane[bool, float].emit(True, dim) self.sigShowPlane[bool,float].emit(True,dim)
for child in (item.child(i) for i in range(item.childCount())): for child in (item.child(i) for i in range(item.childCount())):
obj = child.cq_item obj = child.cq_item
if hasattr(obj, "wrapped") and type(obj) != Vector: if hasattr(obj,'wrapped') and type(obj) != Vector:
ais = AIS_ColoredShape(obj.wrapped) ais = AIS_ColoredShape(obj.wrapped)
inspected_items.append(ais) inspected_items.append(ais)
else: else:
self.sigShowPlane.emit(False) self.sigShowPlane.emit(False)
obj = item.cq_item obj = item.cq_item
if hasattr(obj, "wrapped") and type(obj) != Vector: if hasattr(obj,'wrapped') and type(obj) != Vector:
ais = AIS_ColoredShape(obj.wrapped) ais = AIS_ColoredShape(obj.wrapped)
inspected_items.append(ais) inspected_items.append(ais)
self.sigDisplayObjects.emit(inspected_items, False) self.sigDisplayObjects.emit(inspected_items,False)
@pyqtSlot(object) @pyqtSlot(object)
def setObject(self, cq_obj): def setObject(self,cq_obj):
self.root.takeChildren() self.root.takeChildren()
# iterate through parent objects if they exist # iterate through parent objects if they exist
while getattr(cq_obj, "parent", None): while getattr(cq_obj, 'parent', None):
current_frame = CQStackItem(str(cq_obj.plane.origin), workplane=cq_obj) current_frame = CQStackItem(str(cq_obj.plane.origin),workplane=cq_obj)
self.root.addChild(current_frame) self.root.addChild(current_frame)
for obj in cq_obj.objects: for obj in cq_obj.objects:
current_frame.addChild(CQChildItem(obj)) current_frame.addChild(CQChildItem(obj))
cq_obj = cq_obj.parent cq_obj = cq_obj.parent

View File

@@ -5,17 +5,16 @@ from types import SimpleNamespace, FrameType, ModuleType
from typing import List from typing import List
import cadquery as cq import cadquery as cq
from PySide6 import QtCore from PyQt5 import QtCore
from PySide6.QtCore import ( from PyQt5.QtCore import (
Qt, Qt,
QObject, QObject,
Slot as pyqtSlot, pyqtSlot,
Signal as pyqtSignal, pyqtSignal,
QEventLoop, QEventLoop,
QAbstractTableModel, QAbstractTableModel,
) )
from PySide6.QtWidgets import QTableView from PyQt5.QtWidgets import QAction, QTableView
from PySide6.QtGui import QAction
from logbook import info from logbook import info
from path import Path from path import Path
from pyqtgraph.parametertree import Parameter from pyqtgraph.parametertree import Parameter
@@ -187,7 +186,7 @@ class Debugger(QObject, ComponentMixin):
def _exec(self, code, locals_dict, globals_dict): def _exec(self, code, locals_dict, globals_dict):
with ExitStack() as stack: with ExitStack() as stack:
fname = self.parent().components["editor"].filename fname = self.parent().components["editor"].filename
p = Path(fname if fname else "").abspath().dirname() p = Path(fname if fname else "").absolute().dirname()
if self.preferences["Add script dir to path"] and p.exists(): if self.preferences["Add script dir to path"] and p.exists():
sys.path.insert(0, p) sys.path.insert(0, p)

View File

@@ -2,11 +2,9 @@ import os
from modulefinder import ModuleFinder from modulefinder import ModuleFinder
from spyder.plugins.editor.widgets.codeeditor import CodeEditor from spyder.plugins.editor.widgets.codeeditor import CodeEditor
from PySide6.QtCore import Signal as pyqtSignal from PyQt5.QtCore import pyqtSignal, QFileSystemWatcher, QTimer
from PySide6.QtCore import QFileSystemWatcher, QTimerEvent, QTimer from PyQt5.QtWidgets import QAction, QFileDialog
from PyQt5.QtGui import QFontDatabase
from PySide6.QtWidgets import QFileDialog
from PySide6.QtGui import QFontDatabase, QAction
from path import Path from path import Path
import sys import sys
@@ -18,104 +16,91 @@ from ..utils import get_save_filename, get_open_filename, confirm
from ..icons import icon from ..icons import icon
class Editor(CodeEditor,ComponentMixin):
class Editor(CodeEditor, ComponentMixin): name = 'Code Editor'
name = "Code Editor"
# This signal is emitted whenever the currently-open file changes and # This signal is emitted whenever the currently-open file changes and
# autoreload is enabled. # autoreload is enabled.
triggerRerender = pyqtSignal(bool) triggerRerender = pyqtSignal(bool)
sigFilenameChanged = pyqtSignal(str) sigFilenameChanged = pyqtSignal(str)
preferences = Parameter.create( preferences = Parameter.create(name='Preferences',children=[
name="Preferences", {'name': 'Font size', 'type': 'int', 'value': 12},
children=[ {'name': 'Autoreload', 'type': 'bool', 'value': False},
{"name": "Font size", "type": "int", "value": 12}, {'name': 'Autoreload delay', 'type': 'int', 'value': 50},
{"name": "Autoreload", "type": "bool", "value": False}, {'name': 'Autoreload: watch imported modules', 'type': 'bool', 'value': False},
{"name": "Autoreload delay", "type": "int", "value": 50}, {'name': 'Line wrap', 'type': 'bool', 'value': False},
{ {'name': 'Color scheme', 'type': 'list',
"name": "Autoreload: watch imported modules", 'values': ['Spyder','Monokai','Zenburn'], 'value': 'Spyder'}])
"type": "bool",
"value": False,
},
{"name": "Line wrap", "type": "bool", "value": False},
{
"name": "Color scheme",
"type": "list",
"values": ["Spyder", "Monokai", "Zenburn"],
"value": "Spyder",
},
],
)
EXTENSIONS = "py" EXTENSIONS = 'py'
def __init__(self,parent=None):
def __init__(self, parent=None):
self._watched_file = None self._watched_file = None
super(Editor, self).__init__(parent) super(Editor,self).__init__(parent)
ComponentMixin.__init__(self) ComponentMixin.__init__(self)
self.setup_editor( self.setup_editor(linenumbers=True,
linenumbers=True, markers=True,
markers=True, edge_line=False,
edge_line=False, tab_mode=False,
tab_mode=False, show_blanks=True,
show_blanks=True, font=QFontDatabase.systemFont(QFontDatabase.FixedFont),
font=QFontDatabase.systemFont(QFontDatabase.FixedFont), language='Python',
language="Python", filename='')
filename="",
)
self._actions = { self._actions = \
"File": [ {'File' : [QAction(icon('new'),
QAction( 'New',
icon("new"), "New", self, shortcut="ctrl+N", triggered=self.new self,
), shortcut='ctrl+N',
QAction( triggered=self.new),
icon("open"), "Open", self, shortcut="ctrl+O", triggered=self.open QAction(icon('open'),
), 'Open',
QAction( self,
icon("save"), "Save", self, shortcut="ctrl+S", triggered=self.save shortcut='ctrl+O',
), triggered=self.open),
QAction( QAction(icon('save'),
icon("save_as"), 'Save',
"Save as", self,
self, shortcut='ctrl+S',
shortcut="ctrl+shift+S", triggered=self.save),
triggered=self.save_as, QAction(icon('save_as'),
), 'Save as',
QAction( self,
icon("autoreload"), shortcut='ctrl+shift+S',
"Automatic reload and preview", triggered=self.save_as),
self, QAction(icon('autoreload'),
triggered=self.autoreload, 'Automatic reload and preview',
checkable=True, self,triggered=self.autoreload,
checked=False, checkable=True,
objectName="autoreload", checked=False,
), objectName='autoreload'),
] ]}
}
for a in self._actions.values(): for a in self._actions.values():
self.addActions(a) self.addActions(a)
self._fixContextMenu() self._fixContextMenu()
# autoreload support # autoreload support
self._file_watcher = QFileSystemWatcher(self) self._file_watcher = QFileSystemWatcher(self)
# we wait for 50ms after a file change for the file to be written completely # we wait for 50ms after a file change for the file to be written completely
self._file_watch_timer = QTimer(self) self._file_watch_timer = QTimer(self)
self._file_watch_timer.setInterval(self.preferences["Autoreload delay"]) self._file_watch_timer.setInterval(self.preferences['Autoreload delay'])
self._file_watch_timer.setSingleShot(True) self._file_watch_timer.setSingleShot(True)
self._file_watcher.fileChanged.connect( self._file_watcher.fileChanged.connect(
lambda val: self._file_watch_timer.start() lambda val: self._file_watch_timer.start())
)
self._file_watch_timer.timeout.connect(self._file_changed) self._file_watch_timer.timeout.connect(self._file_changed)
self.updatePreferences() self.updatePreferences()
def _fixContextMenu(self): def _fixContextMenu(self):
menu = self.menu menu = self.menu
menu.removeAction(self.run_cell_action) menu.removeAction(self.run_cell_action)
@@ -123,66 +108,68 @@ class Editor(CodeEditor, ComponentMixin):
menu.removeAction(self.run_selection_action) menu.removeAction(self.run_selection_action)
menu.removeAction(self.re_run_last_cell_action) menu.removeAction(self.re_run_last_cell_action)
def updatePreferences(self, *args): def updatePreferences(self,*args):
self.set_color_scheme(self.preferences["Color scheme"])
self.set_color_scheme(self.preferences['Color scheme'])
font = self.font() font = self.font()
font.setPointSize(self.preferences["Font size"]) font.setPointSize(self.preferences['Font size'])
self.set_font(font) self.set_font(font)
self.findChild(QAction, "autoreload").setChecked(self.preferences["Autoreload"]) self.findChild(QAction, 'autoreload') \
.setChecked(self.preferences['Autoreload'])
self._file_watch_timer.setInterval(self.preferences["Autoreload delay"]) self._file_watch_timer.setInterval(self.preferences['Autoreload delay'])
self.toggle_wrap_mode(self.preferences["Line wrap"]) self.toggle_wrap_mode(self.preferences['Line wrap'])
self._clear_watched_paths() self._clear_watched_paths()
self._watch_paths() self._watch_paths()
def confirm_discard(self): def confirm_discard(self):
if self.modified: if self.modified:
rv = confirm( rv = confirm(self,'Please confirm','Current document is not saved - do you want to continue?')
self,
"Please confirm",
"Current document is not saved - do you want to continue?",
)
else: else:
rv = True rv = True
return rv return rv
def new(self): def new(self):
if not self.confirm_discard():
return
self.set_text("") if not self.confirm_discard(): return
self.filename = ""
self.set_text('')
self.filename = ''
self.reset_modified() self.reset_modified()
def open(self): def open(self):
if not self.confirm_discard():
return if not self.confirm_discard(): return
curr_dir = Path(self.filename).abspath().dirname() curr_dir = Path(self.filename).absolute().dirname()
fname = get_open_filename(self.EXTENSIONS, curr_dir) fname = get_open_filename(self.EXTENSIONS, curr_dir)
if fname != "": if fname != '':
self.load_from_file(fname) self.load_from_file(fname)
def load_from_file(self, fname): def load_from_file(self,fname):
self.set_text_from_file(fname) self.set_text_from_file(fname)
self.filename = fname self.filename = fname
self.reset_modified() self.reset_modified()
def save(self): def save(self):
if self._filename != "":
if self.preferences["Autoreload"]: if self._filename != '':
if self.preferences['Autoreload']:
self._file_watcher.blockSignals(True) self._file_watcher.blockSignals(True)
self._file_watch_timer.stop() self._file_watch_timer.stop()
with open(self._filename, "w") as f: with open(self._filename, 'w') as f:
f.write(self.toPlainText()) f.write(self.toPlainText())
if self.preferences["Autoreload"]: if self.preferences['Autoreload']:
self._file_watcher.blockSignals(False) self._file_watcher.blockSignals(False)
self.triggerRerender.emit(True) self.triggerRerender.emit(True)
@@ -192,25 +179,20 @@ class Editor(CodeEditor, ComponentMixin):
self.save_as() self.save_as()
def save_as(self): def save_as(self):
fname = get_save_filename(self.EXTENSIONS) fname = get_save_filename(self.EXTENSIONS)
if fname != "": if fname != '':
with open(fname, "w") as f: with open(fname,'w') as f:
f.write(self.toPlainText()) f.write(self.toPlainText())
self.filename = fname self.filename = fname
self.reset_modified() self.reset_modified()
def _update_filewatcher(self): def _update_filewatcher(self):
if self._watched_file and ( if self._watched_file and (self._watched_file != self.filename or not self.preferences['Autoreload']):
self._watched_file != self.filename or not self.preferences["Autoreload"]
):
self._clear_watched_paths() self._clear_watched_paths()
self._watched_file = None self._watched_file = None
if ( if self.preferences['Autoreload'] and self.filename and self.filename != self._watched_file:
self.preferences["Autoreload"]
and self.filename
and self.filename != self._watched_file
):
self._watched_file = self._filename self._watched_file = self._filename
self._watch_paths() self._watch_paths()
@@ -232,8 +214,8 @@ class Editor(CodeEditor, ComponentMixin):
def _watch_paths(self): def _watch_paths(self):
if Path(self._filename).exists(): if Path(self._filename).exists():
self._file_watcher.addPath(self._filename) self._file_watcher.addPath(self._filename)
if self.preferences["Autoreload: watch imported modules"]: if self.preferences['Autoreload: watch imported modules']:
module_paths = self.get_imported_module_paths(self._filename) module_paths = self.get_imported_module_paths(self._filename)
if module_paths: if module_paths:
self._file_watcher.addPaths(module_paths) self._file_watcher.addPaths(module_paths)
@@ -246,45 +228,51 @@ class Editor(CodeEditor, ComponentMixin):
# Turn autoreload on/off. # Turn autoreload on/off.
def autoreload(self, enabled): def autoreload(self, enabled):
self.preferences["Autoreload"] = enabled self.preferences['Autoreload'] = enabled
self._update_filewatcher() self._update_filewatcher()
def reset_modified(self): def reset_modified(self):
self.document().setModified(False)
self.document().setModified(False)
@property @property
def modified(self): def modified(self):
return self.document().isModified() return self.document().isModified()
def saveComponentState(self, store): def saveComponentState(self,store):
if self.filename != "":
store.setValue(self.name + "/state", self.filename)
def restoreComponentState(self, store): if self.filename != '':
filename = store.value(self.name + "/state") store.setValue(self.name+'/state',self.filename)
if filename and self.filename == "": def restoreComponentState(self,store):
filename = store.value(self.name+'/state')
if filename and self.filename == '':
try: try:
self.load_from_file(filename) self.load_from_file(filename)
except IOError: except IOError:
self._logger.warning(f"could not open {filename}") self._logger.warning(f'could not open {filename}')
def get_imported_module_paths(self, module_path): def get_imported_module_paths(self, module_path):
finder = ModuleFinder([os.path.dirname(module_path)]) finder = ModuleFinder([os.path.dirname(module_path)])
imported_modules = [] imported_modules = []
try: try:
finder.run_script(module_path) finder.run_script(module_path)
except SyntaxError as err: except SyntaxError as err:
self._logger.warning(f"Syntax error in {module_path}: {err}") self._logger.warning(f'Syntax error in {module_path}: {err}')
except Exception as err: except Exception as err:
self._logger.warning( self._logger.warning(
f"Cannot determine imported modules in {module_path}: {type(err).__name__} {err}" f'Cannot determine imported modules in {module_path}: {type(err).__name__} {err}'
) )
else: else:
for module_name, module in finder.modules.items(): for module_name, module in finder.modules.items():
if module_name != "__main__": if module_name != '__main__':
path = getattr(module, "__file__", None) path = getattr(module, '__file__', None)
if path is not None and os.path.isfile(path): if path is not None and os.path.isfile(path):
imported_modules.append(path) imported_modules.append(path)
@@ -292,7 +280,8 @@ class Editor(CodeEditor, ComponentMixin):
if __name__ == "__main__": if __name__ == "__main__":
from PySide6.QtWidgets import QApplication
from PyQt5.QtWidgets import QApplication
app = QApplication(sys.argv) app = QApplication(sys.argv)
editor = Editor() editor = Editor()

View File

@@ -1,7 +1,7 @@
import logbook as logging import logbook as logging
from PySide6.QtWidgets import QPlainTextEdit from PyQt5.QtWidgets import QPlainTextEdit
from PySide6 import QtCore from PyQt5 import QtCore
from ..mixins import ComponentMixin from ..mixins import ComponentMixin

View File

@@ -1,12 +1,12 @@
from PySide6.QtWidgets import ( from PyQt5.QtWidgets import (
QTreeWidget, QTreeWidget,
QTreeWidgetItem, QTreeWidgetItem,
QAction,
QMenu, QMenu,
QWidget, QWidget,
QAbstractItemView, QAbstractItemView,
) )
from PySide6.QtGui import QAction from PyQt5.QtCore import Qt, pyqtSlot, pyqtSignal
from PySide6.QtCore import Qt, Slot as pyqtSlot, Signal as pyqtSignal
from pyqtgraph.parametertree import Parameter, ParameterTree from pyqtgraph.parametertree import Parameter, ParameterTree
from OCP.AIS import AIS_Line from OCP.AIS import AIS_Line
@@ -114,8 +114,7 @@ class ObjectTree(QWidget, ComponentMixin):
], ],
) )
sigObjectsAdded = pyqtSignal(list) sigObjectsAdded = pyqtSignal([list], [list, bool])
sigObjectsAdded2 = pyqtSignal(list, bool)
sigObjectsRemoved = pyqtSignal(list) sigObjectsRemoved = pyqtSignal(list)
sigCQObjectSelected = pyqtSignal(object) sigCQObjectSelected = pyqtSignal(object)
sigAISObjectsSelected = pyqtSignal(list) sigAISObjectsSelected = pyqtSignal(list)

View File

@@ -1,8 +1,8 @@
from sys import platform from sys import platform
from PySide6.QtWidgets import QWidget, QApplication from PyQt5.QtWidgets import QWidget, QApplication
from PySide6.QtCore import Slot as pyqtSlot, Signal as pyqtSignal, Qt, QEvent from PyQt5.QtCore import pyqtSlot, pyqtSignal, Qt, QEvent
import OCP import OCP
@@ -15,144 +15,161 @@ from OCP.Quantity import Quantity_Color, Quantity_TOC_RGB as TOC_RGB
ZOOM_STEP = 0.9 ZOOM_STEP = 0.9
class OCCTWidget(QWidget): class OCCTWidget(QWidget):
sigObjectSelected = pyqtSignal(list) sigObjectSelected = pyqtSignal(list)
def __init__(self, parent=None): def __init__(self,parent=None):
super(OCCTWidget, self).__init__(parent)
super(OCCTWidget,self).__init__(parent)
self.setAttribute(Qt.WA_NativeWindow) self.setAttribute(Qt.WA_NativeWindow)
self.setAttribute(Qt.WA_PaintOnScreen) self.setAttribute(Qt.WA_PaintOnScreen)
self.setAttribute(Qt.WA_NoSystemBackground) self.setAttribute(Qt.WA_NoSystemBackground)
self._initialized = False self._initialized = False
self._needs_update = False self._needs_update = False
# OCCT secific things #OCCT secific things
self.display_connection = Aspect_DisplayConnection() self.display_connection = Aspect_DisplayConnection()
self.graphics_driver = OpenGl_GraphicDriver(self.display_connection) self.graphics_driver = OpenGl_GraphicDriver(self.display_connection)
self.viewer = V3d_Viewer(self.graphics_driver) self.viewer = V3d_Viewer(self.graphics_driver)
self.view = self.viewer.CreateView() self.view = self.viewer.CreateView()
self.context = AIS_InteractiveContext(self.viewer) self.context = AIS_InteractiveContext(self.viewer)
# Trihedorn, lights, etc #Trihedorn, lights, etc
self.prepare_display() self.prepare_display()
def prepare_display(self): def prepare_display(self):
view = self.view view = self.view
params = view.ChangeRenderingParams() params = view.ChangeRenderingParams()
params.NbMsaaSamples = 8 params.NbMsaaSamples = 8
params.IsAntialiasingEnabled = True params.IsAntialiasingEnabled = True
view.TriedronDisplay( view.TriedronDisplay(
Aspect_TypeOfTriedronPosition.Aspect_TOTP_RIGHT_LOWER, Quantity_Color(), 0.1 Aspect_TypeOfTriedronPosition.Aspect_TOTP_RIGHT_LOWER,
) Quantity_Color(), 0.1)
view.ZBufferTriedronSetup(Quantity_Color(*(0.2, 0.0, 0.0), TOC_RGB)) view.ZBufferTriedronSetup(Quantity_Color(*(0.2, 0.0, 0.0), TOC_RGB))
viewer = self.viewer viewer = self.viewer
viewer.SetDefaultLights() viewer.SetDefaultLights()
viewer.SetLightOn() viewer.SetLightOn()
ctx = self.context ctx = self.context
ctx.SetDisplayMode(AIS_DisplayMode.AIS_Shaded, True) ctx.SetDisplayMode(AIS_DisplayMode.AIS_Shaded, True)
ctx.DefaultDrawer().SetFaceBoundaryDraw(True) ctx.DefaultDrawer().SetFaceBoundaryDraw(True)
def wheelEvent(self, event): def wheelEvent(self, event):
delta = event.angleDelta().y() delta = event.angleDelta().y()
factor = ZOOM_STEP if delta < 0 else 1 / ZOOM_STEP factor = ZOOM_STEP if delta<0 else 1/ZOOM_STEP
self.view.SetZoom(factor) self.view.SetZoom(factor)
def mousePressEvent(self, event): def mousePressEvent(self,event):
pos = event.pos() pos = event.pos()
if event.button() == Qt.LeftButton: if event.button() == Qt.LeftButton:
self.view.StartRotation(pos.x(), pos.y()) self.view.StartRotation(pos.x(), pos.y())
elif event.button() == Qt.RightButton: elif event.button() == Qt.RightButton:
self.view.StartZoomAtPoint(pos.x(), pos.y()) self.view.StartZoomAtPoint(pos.x(), pos.y())
self.old_pos = pos self.old_pos = pos
def mouseMoveEvent(self, event): def mouseMoveEvent(self,event):
pos = event.pos() pos = event.pos()
x, y = pos.x(), pos.y() x,y = pos.x(),pos.y()
if event.buttons() == Qt.LeftButton: if event.buttons() == Qt.LeftButton:
self.view.Rotation(x, y) self.view.Rotation(x,y)
elif event.buttons() == Qt.MiddleButton: elif event.buttons() == Qt.MiddleButton:
self.view.Pan(x - self.old_pos.x(), self.old_pos.y() - y, theToStart=True) self.view.Pan(x - self.old_pos.x(),
self.old_pos.y() - y, theToStart=True)
elif event.buttons() == Qt.RightButton: elif event.buttons() == Qt.RightButton:
self.view.Pan(x - self.old_pos.x(), self.old_pos.y() - y, theToStart=True) self.view.Pan(x - self.old_pos.x(),
self.old_pos.y() - y, theToStart=True)
self.old_pos = pos self.old_pos = pos
def mouseReleaseEvent(self, event): def mouseReleaseEvent(self,event):
if event.button() == Qt.LeftButton: if event.button() == Qt.LeftButton:
pos = event.pos() pos = event.pos()
x, y = pos.x(), pos.y() x,y = pos.x(),pos.y()
self.context.MoveTo(x, y, self.view, True) self.context.MoveTo(x,y,self.view,True)
self._handle_selection() self._handle_selection()
def _handle_selection(self): def _handle_selection(self):
self.context.Select(True) self.context.Select(True)
self.context.InitSelected() self.context.InitSelected()
selected = [] selected = []
if self.context.HasSelectedShape(): if self.context.HasSelectedShape():
selected.append(self.context.SelectedShape()) selected.append(self.context.SelectedShape())
self.sigObjectSelected.emit(selected) self.sigObjectSelected.emit(selected)
def paintEngine(self): def paintEngine(self):
return None return None
def paintEvent(self, event): def paintEvent(self, event):
if not self._initialized: if not self._initialized:
self._initialize() self._initialize()
else: else:
self.view.Redraw() self.view.Redraw()
def showEvent(self, event): def showEvent(self, event):
super(OCCTWidget, self).showEvent(event)
super(OCCTWidget,self).showEvent(event)
def resizeEvent(self, event): def resizeEvent(self, event):
super(OCCTWidget, self).resizeEvent(event)
super(OCCTWidget,self).resizeEvent(event)
self.view.MustBeResized() self.view.MustBeResized()
def _initialize(self): def _initialize(self):
wins = { wins = {
"darwin": self._get_window_osx, 'darwin' : self._get_window_osx,
"linux": self._get_window_linux, 'linux' : self._get_window_linux,
"win32": self._get_window_win, 'win32': self._get_window_win
} }
self.view.SetWindow(wins.get(platform, self._get_window_linux)(self.winId())) self.view.SetWindow(wins.get(platform,self._get_window_linux)(self.winId()))
self._initialized = True self._initialized = True
def _get_window_win(self, wid): def _get_window_win(self,wid):
from OCP.WNT import WNT_Window from OCP.WNT import WNT_Window
print(wid)
return WNT_Window(wid.ascapsule()) return WNT_Window(wid.ascapsule())
def _get_window_linux(self, wid): def _get_window_linux(self,wid):
from OCP.Xw import Xw_Window from OCP.Xw import Xw_Window
return Xw_Window(self.display_connection, int(wid)) return Xw_Window(self.display_connection,int(wid))
def _get_window_osx(self, wid): def _get_window_osx(self,wid):
from OCP.Cocoa import Cocoa_Window from OCP.Cocoa import Cocoa_Window
return Cocoa_Window(wid.ascapsule()) return Cocoa_Window(wid.ascapsule())

View File

@@ -1,89 +1,97 @@
from traceback import extract_tb, format_exception_only from traceback import extract_tb, format_exception_only
from PySide6.QtWidgets import QWidget, QTreeWidget, QTreeWidgetItem, QLabel from PyQt5.QtWidgets import (QWidget, QTreeWidget, QTreeWidgetItem, QAction,
from PySide6.QtGui import QAction QLabel)
from PySide6.QtCore import Qt, Slot as pyqtSlot, Signal as pyqtSignal from PyQt5.QtCore import Qt, pyqtSlot, pyqtSignal
from ..mixins import ComponentMixin from ..mixins import ComponentMixin
from ..utils import layout from ..utils import layout
class TracebackTree(QTreeWidget): class TracebackTree(QTreeWidget):
name = "Traceback Viewer"
def __init__(self, parent): name = 'Traceback Viewer'
super(TracebackTree, self).__init__(parent)
def __init__(self,parent):
super(TracebackTree,self).__init__(parent)
self.setHeaderHidden(False) self.setHeaderHidden(False)
self.setItemsExpandable(False) self.setItemsExpandable(False)
self.setRootIsDecorated(False) self.setRootIsDecorated(False)
self.setContextMenuPolicy(Qt.ActionsContextMenu) self.setContextMenuPolicy(Qt.ActionsContextMenu)
self.setColumnCount(3) self.setColumnCount(3)
self.setHeaderLabels(["File", "Line", "Code"]) self.setHeaderLabels(['File','Line','Code'])
self.root = self.invisibleRootItem() self.root = self.invisibleRootItem()
class TracebackPane(QWidget,ComponentMixin):
class TracebackPane(QWidget, ComponentMixin):
sigHighlightLine = pyqtSignal(int) sigHighlightLine = pyqtSignal(int)
def __init__(self, parent): def __init__(self,parent):
super(TracebackPane, self).__init__(parent)
super(TracebackPane,self).__init__(parent)
self.tree = TracebackTree(self) self.tree = TracebackTree(self)
self.current_exception = QLabel(self) self.current_exception = QLabel(self)
self.current_exception.setStyleSheet("QLabel {color : red; }") self.current_exception.setStyleSheet(\
"QLabel {color : red; }");
layout(self, (self.current_exception, self.tree), self)
layout(self,
(self.current_exception,
self.tree),
self)
self.tree.currentItemChanged.connect(self.handleSelection) self.tree.currentItemChanged.connect(self.handleSelection)
@pyqtSlot(object, str) @pyqtSlot(object,str)
def addTraceback(self, exc_info, code): def addTraceback(self,exc_info,code):
self.tree.clear() self.tree.clear()
if exc_info: if exc_info:
t, exc, tb = exc_info t,exc,tb = exc_info
root = self.tree.root root = self.tree.root
code = code.splitlines() code = code.splitlines()
tb = [ tb = [t for t in extract_tb(tb) if '<string>' in t.filename] #ignore highest frames (debug, exec)
t for t in extract_tb(tb) if "<string>" in t.filename
] # ignore highest frames (debug, exec)
for el in tb: for el in tb:
# workaround of the traceback module #workaround of the traceback module
if el.line == "": if el.line == '':
line = code[el.lineno - 1].strip() line = code[el.lineno-1].strip()
else: else:
line = el.line line = el.line
root.addChild(QTreeWidgetItem([el.filename, str(el.lineno), line])) root.addChild(QTreeWidgetItem([el.filename,
str(el.lineno),
line]))
exc_name = t.__name__ exc_name = t.__name__
exc_msg = str(exc) exc_msg = str(exc)
exc_msg = exc_msg.replace("<", "&lt;").replace(">", "&gt;") # replace <> exc_msg = exc_msg.replace('<', '&lt;').replace('>', '&gt;') #replace <>
self.current_exception.setText("<b>{}</b>: {}".format(exc_name, exc_msg))
self.current_exception.\
setText('<b>{}</b>: {}'.format(exc_name,exc_msg))
# handle the special case of a SyntaxError # handle the special case of a SyntaxError
if t is SyntaxError: if t is SyntaxError:
root.addChild( root.addChild(QTreeWidgetItem(
QTreeWidgetItem( [exc.filename,
[ str(exc.lineno),
exc.filename, exc.text.strip() if exc.text else '']
str(exc.lineno), ))
exc.text.strip() if exc.text else "",
]
)
)
else: else:
self.current_exception.setText("") self.current_exception.setText('')
@pyqtSlot(QTreeWidgetItem, QTreeWidgetItem) @pyqtSlot(QTreeWidgetItem,QTreeWidgetItem)
def handleSelection(self, item, *args): def handleSelection(self,item,*args):
if item: if item:
f, line = item.data(0, 0), int(item.data(1, 0)) f,line = item.data(0,0),int(item.data(1,0))
if "<string>" in f: if '<string>' in f:
self.sigHighlightLine.emit(line) self.sigHighlightLine.emit(line)

View File

@@ -1,21 +1,14 @@
from PySide6.QtWidgets import QWidget, QDialog, QTreeWidgetItem, QApplication from PyQt5.QtWidgets import QWidget, QDialog, QTreeWidgetItem, QApplication, QAction
from PySide6.QtCore import Slot as pyqtSlot, Signal as pyqtSignal from PyQt5.QtCore import pyqtSlot, pyqtSignal
from PySide6.QtGui import QIcon, QAction from PyQt5.QtGui import QIcon
from OCP.Graphic3d import ( from OCP.Graphic3d import Graphic3d_Camera, Graphic3d_StereoMode, Graphic3d_NOM_JADE,\
Graphic3d_Camera, Graphic3d_MaterialAspect
Graphic3d_StereoMode, from OCP.AIS import AIS_Shaded,AIS_WireFrame, AIS_ColoredShape, AIS_Axis
Graphic3d_NOM_JADE,
Graphic3d_MaterialAspect,
)
from OCP.AIS import AIS_Shaded, AIS_WireFrame, AIS_ColoredShape, AIS_Axis
from OCP.Aspect import Aspect_GDM_Lines, Aspect_GT_Rectangular from OCP.Aspect import Aspect_GDM_Lines, Aspect_GT_Rectangular
from OCP.Quantity import ( from OCP.Quantity import Quantity_NOC_BLACK as BLACK, Quantity_TOC_RGB as TOC_RGB,\
Quantity_NOC_BLACK as BLACK, Quantity_Color
Quantity_TOC_RGB as TOC_RGB,
Quantity_Color,
)
from OCP.Geom import Geom_Axis1Placement from OCP.Geom import Geom_Axis1Placement
from OCP.gp import gp_Ax3, gp_Dir, gp_Pnt, gp_Ax1 from OCP.gp import gp_Ax3, gp_Dir, gp_Pnt, gp_Ax1
@@ -32,66 +25,30 @@ import qtawesome as qta
DEFAULT_EDGE_COLOR = Quantity_Color(BLACK) DEFAULT_EDGE_COLOR = Quantity_Color(BLACK)
DEFAULT_EDGE_WIDTH = 2 DEFAULT_EDGE_WIDTH = 2
class OCCViewer(QWidget,ComponentMixin):
class OCCViewer(QWidget, ComponentMixin): name = '3D Viewer'
name = "3D Viewer"
preferences = Parameter.create( preferences = Parameter.create(name='Pref',children=[
name="Pref", {'name': 'Fit automatically', 'type': 'bool', 'value': True},
children=[ {'name': 'Use gradient', 'type': 'bool', 'value': False},
{"name": "Fit automatically", "type": "bool", "value": True}, {'name': 'Background color', 'type': 'color', 'value': (95,95,95)},
{"name": "Use gradient", "type": "bool", "value": False}, {'name': 'Background color (aux)', 'type': 'color', 'value': (30,30,30)},
{"name": "Background color", "type": "color", "value": (95, 95, 95)}, {'name': 'Default object color', 'type': 'color', 'value': "#FF0"},
{"name": "Background color (aux)", "type": "color", "value": (30, 30, 30)}, {'name': 'Deviation', 'type': 'float', 'value': 1e-5, 'dec': True, 'step': 1},
{"name": "Default object color", "type": "color", "value": "#FF0"}, {'name': 'Angular deviation', 'type': 'float', 'value': 0.1, 'dec': True, 'step': 1},
{ {'name': 'Projection Type', 'type': 'list', 'value': 'Orthographic',
"name": "Deviation", 'values': ['Orthographic', 'Perspective', 'Stereo', 'MonoLeftEye', 'MonoRightEye']},
"type": "float", {'name': 'Stereo Mode', 'type': 'list', 'value': 'QuadBuffer',
"value": 1e-5, 'values': ['QuadBuffer', 'Anaglyph', 'RowInterlaced', 'ColumnInterlaced',
"dec": True, 'ChessBoard', 'SideBySide', 'OverUnder']}])
"step": 1, IMAGE_EXTENSIONS = 'png'
},
{
"name": "Angular deviation",
"type": "float",
"value": 0.1,
"dec": True,
"step": 1,
},
{
"name": "Projection Type",
"type": "list",
"value": "Orthographic",
"values": [
"Orthographic",
"Perspective",
"Stereo",
"MonoLeftEye",
"MonoRightEye",
],
},
{
"name": "Stereo Mode",
"type": "list",
"value": "QuadBuffer",
"values": [
"QuadBuffer",
"Anaglyph",
"RowInterlaced",
"ColumnInterlaced",
"ChessBoard",
"SideBySide",
"OverUnder",
],
},
],
)
IMAGE_EXTENSIONS = "png"
sigObjectSelected = pyqtSignal(list) sigObjectSelected = pyqtSignal(list)
def __init__(self, parent=None): def __init__(self,parent=None):
super(OCCViewer, self).__init__(parent)
super(OCCViewer,self).__init__(parent)
ComponentMixin.__init__(self) ComponentMixin.__init__(self)
self.canvas = OCCTWidget() self.canvas = OCCTWidget()
@@ -99,18 +56,15 @@ class OCCViewer(QWidget, ComponentMixin):
self.create_actions(self) self.create_actions(self)
self.layout_ = layout( self.layout_ = layout(self,
self, [self.canvas,],
[ top_widget=self,
self.canvas, margin=0)
], self.setup_default_drawer() #misspelled in original
top_widget=self,
margin=0,
)
self.setup_default_drawer() # misspelled in original
self.updatePreferences() self.updatePreferences()
def setup_default_drawer(self): def setup_default_drawer(self):
# set the default color and material # set the default color and material
material = Graphic3d_MaterialAspect(Graphic3d_NOM_JADE) material = Graphic3d_MaterialAspect(Graphic3d_NOM_JADE)
@@ -122,129 +76,99 @@ class OCCViewer(QWidget, ComponentMixin):
line_aspect = self.canvas.context.DefaultDrawer().FaceBoundaryAspect() line_aspect = self.canvas.context.DefaultDrawer().FaceBoundaryAspect()
line_aspect.SetWidth(DEFAULT_EDGE_WIDTH) line_aspect.SetWidth(DEFAULT_EDGE_WIDTH)
line_aspect.SetColor(DEFAULT_EDGE_COLOR) line_aspect.SetColor(DEFAULT_EDGE_COLOR)
def updatePreferences(self,*args):
def updatePreferences(self, *args): color1 = to_occ_color(self.preferences['Background color'])
color1 = to_occ_color(self.preferences["Background color"]) color2 = to_occ_color(self.preferences['Background color (aux)'])
color2 = to_occ_color(self.preferences["Background color (aux)"])
if not self.preferences["Use gradient"]: if not self.preferences['Use gradient']:
color2 = color1 color2 = color1
self.canvas.view.SetBgGradientColors(color1, color2, theToUpdate=True) self.canvas.view.SetBgGradientColors(color1,color2,theToUpdate=True)
self.canvas.update() self.canvas.update()
ctx = self.canvas.context ctx = self.canvas.context
ctx.SetDeviationCoefficient(self.preferences["Deviation"]) ctx.SetDeviationCoefficient(self.preferences['Deviation'])
ctx.SetDeviationAngle(self.preferences["Angular deviation"]) ctx.SetDeviationAngle(self.preferences['Angular deviation'])
v = self._get_view() v = self._get_view()
camera = v.Camera() camera = v.Camera()
projection_type = self.preferences["Projection Type"] projection_type = self.preferences['Projection Type']
camera.SetProjectionType( camera.SetProjectionType(getattr(Graphic3d_Camera, f'Projection_{projection_type}',
getattr( Graphic3d_Camera.Projection_Orthographic))
Graphic3d_Camera,
f"Projection_{projection_type}",
Graphic3d_Camera.Projection_Orthographic,
)
)
# onle relevant for stereo projection # onle relevant for stereo projection
stereo_mode = self.preferences["Stereo Mode"] stereo_mode = self.preferences['Stereo Mode']
params = v.ChangeRenderingParams() params = v.ChangeRenderingParams()
params.StereoMode = getattr( params.StereoMode = getattr(Graphic3d_StereoMode, f'Graphic3d_StereoMode_{stereo_mode}',
Graphic3d_StereoMode, Graphic3d_StereoMode.Graphic3d_StereoMode_QuadBuffer)
f"Graphic3d_StereoMode_{stereo_mode}",
Graphic3d_StereoMode.Graphic3d_StereoMode_QuadBuffer,
)
def create_actions(self, parent): def create_actions(self,parent):
self._actions = {
"View": [ self._actions = \
QAction( {'View' : [QAction(qta.icon('fa.arrows-alt'),
qta.icon("fa.arrows-alt"), 'Fit (Shift+F1)',
"Fit (Shift+F1)", parent,
parent, shortcut='shift+F1',
shortcut="shift+F1", triggered=self.fit),
triggered=self.fit, QAction(QIcon(':/images/icons/isometric_view.svg'),
), 'Iso (Shift+F2)',
QAction( parent,
QIcon(":/images/icons/isometric_view.svg"), shortcut='shift+F2',
"Iso (Shift+F2)", triggered=self.iso_view),
parent, QAction(QIcon(':/images/icons/top_view.svg'),
shortcut="shift+F2", 'Top (Shift+F3)',
triggered=self.iso_view, parent,
), shortcut='shift+F3',
QAction( triggered=self.top_view),
QIcon(":/images/icons/top_view.svg"), QAction(QIcon(':/images/icons/bottom_view.svg'),
"Top (Shift+F3)", 'Bottom (Shift+F4)',
parent, parent,
shortcut="shift+F3", shortcut='shift+F4',
triggered=self.top_view, triggered=self.bottom_view),
), QAction(QIcon(':/images/icons/front_view.svg'),
QAction( 'Front (Shift+F5)',
QIcon(":/images/icons/bottom_view.svg"), parent,
"Bottom (Shift+F4)", shortcut='shift+F5',
parent, triggered=self.front_view),
shortcut="shift+F4", QAction(QIcon(':/images/icons/back_view.svg'),
triggered=self.bottom_view, 'Back (Shift+F6)',
), parent,
QAction( shortcut='shift+F6',
QIcon(":/images/icons/front_view.svg"), triggered=self.back_view),
"Front (Shift+F5)", QAction(QIcon(':/images/icons/left_side_view.svg'),
parent, 'Left (Shift+F7)',
shortcut="shift+F5", parent,
triggered=self.front_view, shortcut='shift+F7',
), triggered=self.left_view),
QAction( QAction(QIcon(':/images/icons/right_side_view.svg'),
QIcon(":/images/icons/back_view.svg"), 'Right (Shift+F8)',
"Back (Shift+F6)", parent,
parent, shortcut='shift+F8',
shortcut="shift+F6", triggered=self.right_view),
triggered=self.back_view, QAction(qta.icon('fa.square-o'),
), 'Wireframe (Shift+F9)',
QAction( parent,
QIcon(":/images/icons/left_side_view.svg"), shortcut='shift+F9',
"Left (Shift+F7)", triggered=self.wireframe_view),
parent, QAction(qta.icon('fa.square'),
shortcut="shift+F7", 'Shaded (Shift+F10)',
triggered=self.left_view, parent,
), shortcut='shift+F10',
QAction( triggered=self.shaded_view)],
QIcon(":/images/icons/right_side_view.svg"), 'Tools' : [QAction(icon('screenshot'),
"Right (Shift+F8)", 'Screenshot',
parent, parent,
shortcut="shift+F8", triggered=self.save_screenshot)]}
triggered=self.right_view,
),
QAction(
qta.icon("fa.square-o"),
"Wireframe (Shift+F9)",
parent,
shortcut="shift+F9",
triggered=self.wireframe_view,
),
QAction(
qta.icon("fa.square"),
"Shaded (Shift+F10)",
parent,
shortcut="shift+F10",
triggered=self.shaded_view,
),
],
"Tools": [
QAction(
icon("screenshot"),
"Screenshot",
parent,
triggered=self.save_screenshot,
)
],
}
def toolbarActions(self): def toolbarActions(self):
return self._actions["View"]
return self._actions['View']
def clear(self): def clear(self):
self.displayed_shapes = [] self.displayed_shapes = []
self.displayed_ais = [] self.displayed_ais = []
self.canvas.context.EraseAll(True) self.canvas.context.EraseAll(True)
@@ -252,167 +176,199 @@ class OCCViewer(QWidget, ComponentMixin):
context.PurgeDisplay() context.PurgeDisplay()
context.RemoveAll(True) context.RemoveAll(True)
def _display(self, shape): def _display(self,shape):
ais = make_AIS(shape) ais = make_AIS(shape)
self.canvas.context.Display(shape, True) self.canvas.context.Display(shape,True)
self.displayed_shapes.append(shape) self.displayed_shapes.append(shape)
self.displayed_ais.append(ais) self.displayed_ais.append(ais)
# self.canvas._display.Repaint() #self.canvas._display.Repaint()
@pyqtSlot(object) @pyqtSlot(object)
def display(self, ais): def display(self,ais):
context = self._get_context()
context.Display(ais, True)
if self.preferences["Fit automatically"]: context = self._get_context()
self.fit() context.Display(ais,True)
if self.preferences['Fit automatically']: self.fit()
@pyqtSlot(list) @pyqtSlot(list)
@pyqtSlot(list, bool) @pyqtSlot(list,bool)
def display_many(self, ais_list, fit=None): def display_many(self,ais_list,fit=None):
context = self._get_context() context = self._get_context()
for ais in ais_list: for ais in ais_list:
context.Display(ais, True) context.Display(ais,True)
if self.preferences["Fit automatically"] and fit is None: if self.preferences['Fit automatically'] and fit is None:
self.fit() self.fit()
elif fit: elif fit:
self.fit() self.fit()
@pyqtSlot(QTreeWidgetItem, int) @pyqtSlot(QTreeWidgetItem,int)
def update_item(self, item, col): def update_item(self,item,col):
ctx = self._get_context() ctx = self._get_context()
if item.checkState(0): if item.checkState(0):
ctx.Display(item.ais, True) ctx.Display(item.ais,True)
else: else:
ctx.Erase(item.ais, True) ctx.Erase(item.ais,True)
@pyqtSlot(list) @pyqtSlot(list)
def remove_items(self, ais_items): def remove_items(self,ais_items):
ctx = self._get_context() ctx = self._get_context()
for ais in ais_items: for ais in ais_items: ctx.Erase(ais,True)
ctx.Erase(ais, True)
@pyqtSlot() @pyqtSlot()
def redraw(self): def redraw(self):
self._get_viewer().Redraw() self._get_viewer().Redraw()
def fit(self): def fit(self):
self.canvas.view.FitAll() self.canvas.view.FitAll()
def iso_view(self): def iso_view(self):
v = self._get_view() v = self._get_view()
v.SetProj(1, -1, 1) v.SetProj(1,-1,1)
v.SetTwist(0) v.SetTwist(0)
def bottom_view(self): def bottom_view(self):
v = self._get_view() v = self._get_view()
v.SetProj(0, 0, -1) v.SetProj(0,0,-1)
v.SetTwist(0) v.SetTwist(0)
def top_view(self): def top_view(self):
v = self._get_view() v = self._get_view()
v.SetProj(0, 0, 1) v.SetProj(0,0,1)
v.SetTwist(0) v.SetTwist(0)
def front_view(self): def front_view(self):
v = self._get_view() v = self._get_view()
v.SetProj(0, -1, 0) v.SetProj(0,-1,0)
v.SetTwist(0) v.SetTwist(0)
def back_view(self): def back_view(self):
v = self._get_view() v = self._get_view()
v.SetProj(0, 1, 0) v.SetProj(0,1,0)
v.SetTwist(0) v.SetTwist(0)
def left_view(self): def left_view(self):
v = self._get_view() v = self._get_view()
v.SetProj(-1, 0, 0) v.SetProj(-1,0,0)
v.SetTwist(0) v.SetTwist(0)
def right_view(self): def right_view(self):
v = self._get_view() v = self._get_view()
v.SetProj(1, 0, 0) v.SetProj(1,0,0)
v.SetTwist(0) v.SetTwist(0)
def shaded_view(self): def shaded_view(self):
c = self._get_context() c = self._get_context()
c.SetDisplayMode(AIS_Shaded, True) c.SetDisplayMode(AIS_Shaded, True)
def wireframe_view(self): def wireframe_view(self):
c = self._get_context() c = self._get_context()
c.SetDisplayMode(AIS_WireFrame, True) c.SetDisplayMode(AIS_WireFrame, True)
def show_grid( def show_grid(self,
self, step=1.0, size=10.0 + 1e-6, color1=(0.7, 0.7, 0.7), color2=(0, 0, 0) step=1.,
): size=10.+1e-6,
color1=(.7,.7,.7),
color2=(0,0,0)):
viewer = self._get_viewer() viewer = self._get_viewer()
viewer.ActivateGrid(Aspect_GT_Rectangular, Aspect_GDM_Lines) viewer.ActivateGrid(Aspect_GT_Rectangular,
Aspect_GDM_Lines)
viewer.SetRectangularGridGraphicValues(size, size, 0) viewer.SetRectangularGridGraphicValues(size, size, 0)
viewer.SetRectangularGridValues(0, 0, step, step, 0) viewer.SetRectangularGridValues(0, 0, step, step, 0)
grid = viewer.Grid() grid = viewer.Grid()
grid.SetColors( grid.SetColors(Quantity_Color(*color1,TOC_RGB),
Quantity_Color(*color1, TOC_RGB), Quantity_Color(*color2, TOC_RGB) Quantity_Color(*color2,TOC_RGB))
)
def hide_grid(self): def hide_grid(self):
viewer = self._get_viewer() viewer = self._get_viewer()
viewer.DeactivateGrid() viewer.DeactivateGrid()
@pyqtSlot(bool, float) @pyqtSlot(bool,float)
@pyqtSlot(bool) @pyqtSlot(bool)
def toggle_grid(self, value: bool, dim: float = 10.0): def toggle_grid(self,
value : bool,
dim : float = 10.):
if value: if value:
self.show_grid(step=dim / 20, size=dim + 1e-9) self.show_grid(step=dim/20,size=dim+1e-9)
else: else:
self.hide_grid() self.hide_grid()
@pyqtSlot(gp_Ax3) @pyqtSlot(gp_Ax3)
def set_grid_orientation(self, orientation: gp_Ax3): def set_grid_orientation(self,orientation : gp_Ax3):
viewer = self._get_viewer() viewer = self._get_viewer()
viewer.SetPrivilegedPlane(orientation) viewer.SetPrivilegedPlane(orientation)
def show_axis(self, origin=(0, 0, 0), direction=(0, 0, 1)): def show_axis(self,origin = (0,0,0), direction=(0,0,1)):
ax_placement = Geom_Axis1Placement(gp_Ax1(gp_Pnt(*origin), gp_Dir(*direction)))
ax_placement = Geom_Axis1Placement(gp_Ax1(gp_Pnt(*origin),
gp_Dir(*direction)))
ax = AIS_Axis(ax_placement) ax = AIS_Axis(ax_placement)
self._display_ais(ax) self._display_ais(ax)
def save_screenshot(self): def save_screenshot(self):
fname = get_save_filename(self.IMAGE_EXTENSIONS)
if fname != "":
self._get_view().Dump(fname)
def _display_ais(self, ais): fname = get_save_filename(self.IMAGE_EXTENSIONS)
if fname != '':
self._get_view().Dump(fname)
def _display_ais(self,ais):
self._get_context().Display(ais) self._get_context().Display(ais)
def _get_view(self): def _get_view(self):
return self.canvas.view return self.canvas.view
def _get_viewer(self): def _get_viewer(self):
return self.canvas.viewer return self.canvas.viewer
def _get_context(self): def _get_context(self):
return self.canvas.context return self.canvas.context
@pyqtSlot(list) @pyqtSlot(list)
def handle_selection(self, obj): def handle_selection(self,obj):
self.sigObjectSelected.emit(obj) self.sigObjectSelected.emit(obj)
@pyqtSlot(list) @pyqtSlot(list)
def set_selected(self, ais): def set_selected(self,ais):
ctx = self._get_context() ctx = self._get_context()
ctx.ClearSelected(False) ctx.ClearSelected(False)
for obj in ais: for obj in ais:
ctx.AddOrRemoveSelected(obj, False) ctx.AddOrRemoveSelected(obj,False)
self.redraw() self.redraw()
if __name__ == "__main__": if __name__ == "__main__":
# pass
import sys import sys
from OCP.BRepPrimAPI import BRepPrimAPI_MakeBox from OCP.BRepPrimAPI import BRepPrimAPI_MakeBox
@@ -423,10 +379,10 @@ if __name__ == "__main__":
dlg.setFixedHeight(400) dlg.setFixedHeight(400)
dlg.setFixedWidth(600) dlg.setFixedWidth(600)
layout(dlg, (viewer,), dlg) layout(dlg,(viewer,),dlg)
dlg.show() dlg.show()
box = BRepPrimAPI_MakeBox(20, 20, 30) box = BRepPrimAPI_MakeBox(20,20,30)
box_ais = AIS_ColoredShape(box.Shape()) box_ais = AIS_ColoredShape(box.Shape())
viewer.display(box_ais) viewer.display(box_ais)

View File

@@ -5,7 +5,7 @@ channels:
dependencies: dependencies:
- pyqt=5 - pyqt=5
- pyqtgraph - pyqtgraph
- python=3.10 - python=3.11
- spyder=5 - spyder=5
- path - path
- logbook - logbook

View File

@@ -3,4 +3,4 @@ channels:
- conda-forge - conda-forge
- defaults - defaults
dependencies: dependencies:
- python=3.10 - python=3.11

View File

@@ -1,98 +0,0 @@
# -*- mode: python -*-
import sys, site, os
from path import Path
from PyInstaller.utils.hooks import collect_all, collect_submodules
block_cipher = None
spyder_data = Path(site.getsitepackages()[-1]) / 'spyder'
parso_grammar = (Path(site.getsitepackages()[-1]) / 'parso/python').glob('grammar*')
cqw_path = Path(site.getsitepackages()[-1]) / 'cq_warehouse'
cq_path = Path(site.getsitepackages()[-1]) / 'cadquery'
if sys.platform == 'linux':
occt_dir = os.path.join(Path(sys.prefix), 'share', 'opencascade')
ocp_path = (os.path.join(HOMEPATH, 'OCP.cpython-39-x86_64-linux-gnu.so'), '.')
elif sys.platform == 'darwin':
occt_dir = os.path.join(Path(sys.prefix), 'share', 'opencascade')
ocp_path = (os.path.join(HOMEPATH, 'OCP.cpython-39-darwin.so'), '.')
elif sys.platform == 'win32':
occt_dir = os.path.join(Path(sys.prefix), 'Library', 'share', 'opencascade')
ocp_path = [(os.path.join(HOMEPATH, 'OCP.cp39-win_amd64.pyd'), '.')]
cas_DLL = [(os.path.join(Path(sys.prefix), 'Library', 'bin', 'casadi_nlpsol_ipopt.dll'), '.')]
ipo_DLL = [(os.path.join(Path(sys.prefix), 'Library', 'bin', 'ipopt-3.dll'), '.')]
bla_DLL = [(os.path.join(Path(sys.prefix), 'Library', 'bin', 'libblas.dll'), '.')]
lap_DLL = [(os.path.join(Path(sys.prefix), 'Library', 'bin', 'liblapack.dll'), '.')]
mum_DLL = [(os.path.join(Path(sys.prefix), 'Library', 'bin', 'dmumps.dll'), '.')]
fla_DLL = [(os.path.join(Path(sys.prefix), 'Library', 'bin', 'flang.dll'), '.')]
flr_DLL = [(os.path.join(Path(sys.prefix), 'Library', 'bin', 'flangrti.dll'), '.')]
omp_DLL = [(os.path.join(Path(sys.prefix), 'Library', 'bin', 'libomp.dll'), '.')]
mkl_DLLs = [(os.path.join(Path(sys.prefix), 'Library', 'bin', 'mkl*.dll'), '.')]
#cas_dllA = os.path.join(HOMEPATH, 'casadi', 'libcasadi.dll')
#cas_dllB = os.path.join(HOMEPATH, 'casadi', 'libcasadi_nlpsol_ipopt.dll')
datas1, binaries1, hiddenimports1 = collect_all('debugpy')
hiddenimports2 = collect_submodules('xmlrpc')
a = Analysis(['run.py'],
pathex=['.'],
binaries=ocp_path + cas_DLL + ipo_DLL + bla_DLL + lap_DLL + mum_DLL + fla_DLL + flr_DLL + omp_DLL + mkl_DLLs + binaries1,
datas=[(spyder_data, 'spyder'),
(occt_dir, 'opencascade'),
(cqw_path, 'cq_warehouse'),
(cq_path, 'cadquery')] +
[(p, 'parso/python') for p in parso_grammar] + datas1,
hiddenimports=['ipykernel.datapub', 'debugpy', 'vtkmodules', 'vtkmodules.all',
'pyqtgraph.graphicsItems.ViewBox.axisCtrlTemplate_pyqt5',
'pyqtgraph.graphicsItems.PlotItem.plotConfigTemplate_pyqt5',
'pyqtgraph.imageview.ImageViewTemplate_pyqt5', 'xmlrpc', 'ipopt', 'casadi',
'zmq.backend', 'cq_warehouse', 'cq_warehouse.bearing', 'cq_warehouse.chain',
'cq_warehouse.drafting', 'cq_warehouse.extensions', 'cq_warehouse.fastener',
'cq_warehouse.sprocket', 'cq_warehouse.thread', 'cq_gears', 'cq_cache',
'build123d', 'cqmore'] + hiddenimports1 + hiddenimports2,
hookspath=[],
runtime_hooks=['pyinstaller/pyi_rth_occ.py',
'pyinstaller/pyi_rth_fontconfig.py'],
excludes=['_tkinter'],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False)
# There is an issue that keeps the OpenSSL libraries from being copied to the output directory.
# This should work if nothing else, but does not with GitHub Actions
if sys.platform == 'win32':
from PyInstaller.depend.bindepend import getfullnameof
rel_data_path = ['PyQt5', 'Qt', 'bin']
a.datas += [
(getfullnameof('libssl-1_1-x64.dll'), os.path.join(*rel_data_path), 'DATA'),
(getfullnameof('libcrypto-1_1-x64.dll'), os.path.join(*rel_data_path), 'DATA'),
]
pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher)
exe = EXE(pyz,
a.scripts,
[],
exclude_binaries=True,
name='CQ-editor',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
console=True,
icon='icons/cadquery_logo_dark.ico')
exclude = ()
#exclude = ('libGL','libEGL','libbsd')
a.binaries = TOC([x for x in a.binaries if not x[0].startswith(exclude)])
coll = COLLECT(exe,
a.binaries,
a.zipfiles,
a.datas,
strip=False,
upx=True,
name='CQ-editor')

View File

@@ -1,4 +1,4 @@
#!/bin/sh #!/bin/sh
export QT_MAC_WANTS_LAYER=1 export QT_MAC_WANTS_LAYER=1
chmod u+x ./CQ-editor/CQ-editor chmod u+x ./CQ-editor/CQ-editor
./CQ-editor/CQ-editor QT_QPA_PLATFORM=xcb PYOPENGL_PLATFORM=x11 ./CQ-editor/CQ-editor

View File

@@ -0,0 +1,4 @@
# hook-py_lib3mf.py
from PyInstaller.utils.hooks import collect_dynamic_libs
binaries = collect_dynamic_libs('py_lib3mf')

View File

@@ -9,17 +9,18 @@ block_cipher = None
spyder_data = Path(site.getsitepackages()[-1]) / 'spyder' spyder_data = Path(site.getsitepackages()[-1]) / 'spyder'
parso_grammar = (Path(site.getsitepackages()[-1]) / 'parso/python').glob('grammar*') parso_grammar = (Path(site.getsitepackages()[-1]) / 'parso/python').glob('grammar*')
cqw_path = Path(site.getsitepackages()[-1]) / 'cq_warehouse' cqw_path = Path(site.getsitepackages()[-1]) / 'cq_warehouse'
bdw_path = Path(site.getsitepackages()[-1]) / 'bd_warehouse'
cq_path = Path(site.getsitepackages()[-1]) / 'cadquery' cq_path = Path(site.getsitepackages()[-1]) / 'cadquery'
if sys.platform == 'linux': if sys.platform == 'linux':
occt_dir = os.path.join(Path(sys.prefix), 'share', 'opencascade') occt_dir = os.path.join(Path(sys.prefix), 'share', 'opencascade')
ocp_path = [(os.path.join(HOMEPATH, 'OCP.cpython-310-x86_64-linux-gnu.so'), '.')] ocp_path = [(os.path.join(HOMEPATH, 'OCP.cpython-311-x86_64-linux-gnu.so'), '.')]
elif sys.platform == 'darwin': elif sys.platform == 'darwin':
occt_dir = os.path.join(Path(sys.prefix), 'share', 'opencascade') occt_dir = os.path.join(Path(sys.prefix), 'share', 'opencascade')
ocp_path = [(os.path.join(HOMEPATH, 'OCP.cpython-310-darwin.so'), '.')] ocp_path = [(os.path.join(HOMEPATH, 'OCP.cpython-311-darwin.so'), '.')]
elif sys.platform == 'win32': elif sys.platform == 'win32':
occt_dir = os.path.join(Path(sys.prefix), 'Library', 'share', 'opencascade') occt_dir = os.path.join(Path(sys.prefix), 'Library', 'share', 'opencascade')
ocp_path = [(os.path.join(HOMEPATH, 'OCP.cp310-win_amd64.pyd'), '.')] ocp_path = [(os.path.join(HOMEPATH, 'OCP.cp311-win_amd64.pyd'), '.')]
datas1, binaries1, hiddenimports1 = collect_all('debugpy') datas1, binaries1, hiddenimports1 = collect_all('debugpy')
hiddenimports2 = collect_submodules('xmlrpc') hiddenimports2 = collect_submodules('xmlrpc')
@@ -29,6 +30,7 @@ a = Analysis(['run.py'],
binaries=ocp_path + binaries1, binaries=ocp_path + binaries1,
datas=[(spyder_data, 'spyder'), datas=[(spyder_data, 'spyder'),
(cqw_path, 'cq_warehouse'), (cqw_path, 'cq_warehouse'),
(bdw_path, 'bd_warehouse'),
(cq_path, 'cadquery')] + (cq_path, 'cadquery')] +
[(p, 'parso/python') for p in parso_grammar] + datas1, [(p, 'parso/python') for p in parso_grammar] + datas1,
hiddenimports=['ipykernel.datapub', 'debugpy', 'vtkmodules', 'vtkmodules.all', hiddenimports=['ipykernel.datapub', 'debugpy', 'vtkmodules', 'vtkmodules.all',
@@ -38,7 +40,8 @@ a = Analysis(['run.py'],
'zmq.backend', 'cq_warehouse', 'cq_warehouse.bearing', 'cq_warehouse.chain', 'zmq.backend', 'cq_warehouse', 'cq_warehouse.bearing', 'cq_warehouse.chain',
'cq_warehouse.drafting', 'cq_warehouse.extensions', 'cq_warehouse.fastener', 'cq_warehouse.drafting', 'cq_warehouse.extensions', 'cq_warehouse.fastener',
'cq_warehouse.sprocket', 'cq_warehouse.thread', 'cq_gears', 'cq_cache', 'cq_warehouse.sprocket', 'cq_warehouse.thread', 'cq_gears', 'cq_cache',
'build123d', 'cqmore'] + hiddenimports1 + hiddenimports2, 'build123d', 'cqmore', 'bd_warehouse', 'bd_warehouse.pipe', 'bd_warehouse.flange',
'bd_warehouse.thread', 'bd_warehouse.gears'] + hiddenimports1 + hiddenimports2,
hookspath=['pyinstaller/extrahooks/'], hookspath=['pyinstaller/extrahooks/'],
runtime_hooks=['pyinstaller/pyi_rth_occ.py', runtime_hooks=['pyinstaller/pyi_rth_occ.py',
'pyinstaller/pyi_rth_fontconfig.py'], 'pyinstaller/pyi_rth_fontconfig.py'],

View File

@@ -31,9 +31,10 @@ setup(
"logbook>=1", "logbook>=1",
"ipython", "ipython",
"path>=16", "path>=16",
"PySide6", "PyQt5>=5",
"requests>=2,<3", "requests>=2,<3",
"spyder>=5,<6", "spyder>=5,<6",
"pyqtgraph", "pyqtgraph",
"numpy >= 1.24.1, <2",
], ],
) )