Compare commits
59 Commits
mactrackpa
...
pyside6
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
960c9fe5ca | ||
|
|
8fca86e2c9 | ||
|
|
73199b6ec8 | ||
|
|
39e2f60b2e | ||
|
|
16d9609bb8 | ||
|
|
f3f6c9eb6b | ||
|
|
750ed4f4e8 | ||
|
|
127f7133d2 | ||
|
|
5ed12949d3 | ||
|
|
8f559821e7 | ||
|
|
4deb489cbd | ||
|
|
839b7f02ee | ||
|
|
aeea6cb0cb | ||
|
|
4e2d22108d | ||
|
|
71cf617f16 | ||
|
|
cfc03f94e7 | ||
|
|
81d84f0d0d | ||
|
|
1df1853a3f | ||
|
|
f47a9e1701 | ||
|
|
cfd34a8339 | ||
|
|
f52fc4dd42 | ||
|
|
25d4c2c45c | ||
|
|
e59c6ae7ba | ||
|
|
0c7237fecb | ||
|
|
a83d03c7ef | ||
|
|
1b75bf8fc0 | ||
|
|
21d729d758 | ||
|
|
1f546f4bc5 | ||
|
|
56ca315097 | ||
|
|
9a60cf7802 | ||
|
|
151b8b8e36 | ||
|
|
0ba64024e0 | ||
|
|
a4b86630dc | ||
|
|
55c23b19ff | ||
|
|
d9153abfca | ||
|
|
deca2f1b89 | ||
|
|
215c200aa1 | ||
|
|
c1f08af0de | ||
|
|
fba053d026 | ||
|
|
be2989c424 | ||
|
|
df863ae27c | ||
|
|
611d189051 | ||
|
|
653502e913 | ||
|
|
5e49adcf54 | ||
|
|
feef729143 | ||
|
|
3ef2ec4447 | ||
|
|
0d1ee5d5af | ||
|
|
c1e75a8b62 | ||
|
|
b408429405 | ||
|
|
6a762801c1 | ||
|
|
ba3f45ea25 | ||
|
|
9eca091fa3 | ||
|
|
43c4708847 | ||
|
|
e71a5f1c62 | ||
|
|
6b762baf41 | ||
|
|
758afb5f18 | ||
|
|
cf43cee874 | ||
|
|
29b2faeaa8 | ||
|
|
9506307367 |
@@ -12,31 +12,34 @@ jobs:
|
|||||||
build-linux:
|
build-linux:
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- uses: mamba-org/provision-with-micromamba@main
|
- uses: mamba-org/setup-micromamba@v1
|
||||||
with:
|
with:
|
||||||
#miniconda-version: "latest"
|
#miniconda-version: "latest"
|
||||||
#auto-update-conda: true
|
#auto-update-conda: true
|
||||||
environment-name: test
|
environment-name: test
|
||||||
environment-file: environment.yml
|
environment-file: environment.yml
|
||||||
extra-specs:
|
create-args: >-
|
||||||
python=3.9
|
python=3.10
|
||||||
- name: Mamba install CadQuery and pyinstaller
|
- 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
|
||||||
sudo apt install -y libxkbcommon-x11-0
|
sudo apt install -y libxkbcommon-x11-0
|
||||||
sudo apt install -y libxcb-xinerama0
|
sudo apt install -y libxcb-xinerama0
|
||||||
|
sudo apt install -y qtbase5-dev qt5-qmake
|
||||||
micromamba info
|
micromamba info
|
||||||
|
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==3.5.5
|
pip install -vvv --pre git+https://github.com/cadquery/cadquery casadi
|
||||||
pip install pyinstaller==4.10
|
pip install pyinstaller>=5.6 git+https://github.com/jdegenstein/pyinstaller-hooks-contrib
|
||||||
pip install path
|
pip install path
|
||||||
|
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/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.git#egg=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
|
||||||
@@ -45,29 +48,29 @@ jobs:
|
|||||||
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@v1
|
- uses: alehechka/upload-tartifact@v2
|
||||||
with:
|
with:
|
||||||
name: CQ-editor-Linux-x86_64
|
name: CQ-editor-Linux-x86_64
|
||||||
path: dist
|
path: dist
|
||||||
build-macos:
|
build-macos:
|
||||||
runs-on: macos-latest
|
runs-on: macos-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- uses: mamba-org/provision-with-micromamba@main
|
- uses: mamba-org/setup-micromamba@v1
|
||||||
with:
|
with:
|
||||||
#miniconda-version: "latest"
|
#miniconda-version: "latest"
|
||||||
#auto-update-conda: true
|
#auto-update-conda: true
|
||||||
environment-name: test
|
environment-name: test
|
||||||
environment-file: environment.yml
|
environment-file: environment.yml
|
||||||
extra-specs:
|
create-args: >-
|
||||||
python=3.9
|
python=3.10
|
||||||
- name: Mamba install CadQuery and pyinstaller
|
- 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==3.5.5
|
pip install --pre git+https://github.com/cadquery/cadquery casadi
|
||||||
pip install pyinstaller==4.10
|
pip install pyinstaller>=5.6 git+https://github.com/jdegenstein/pyinstaller-hooks-contrib
|
||||||
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
|
||||||
@@ -75,7 +78,7 @@ jobs:
|
|||||||
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/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.git#egg=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
|
||||||
@@ -91,36 +94,38 @@ jobs:
|
|||||||
build-windows:
|
build-windows:
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- uses: mamba-org/provision-with-micromamba@main
|
- uses: mamba-org/setup-micromamba@v1
|
||||||
with:
|
with:
|
||||||
#miniconda-version: "latest"
|
#miniconda-version: "latest"
|
||||||
#auto-update-conda: true
|
#auto-update-conda: true
|
||||||
environment-name: test
|
environment-name: test
|
||||||
environment-file: environment.yml
|
environment-file: environment.yml
|
||||||
extra-specs:
|
init-shell: >-
|
||||||
python=3.9
|
powershell
|
||||||
|
create-args: >-
|
||||||
|
python=3.10
|
||||||
- name: pip install cadquery CQ-editor ... etc
|
- name: pip install cadquery CQ-editor ... etc
|
||||||
shell: powershell
|
shell: powershell
|
||||||
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==3.5.5
|
pip install --pre git+https://github.com/cadquery/cadquery casadi
|
||||||
pip install pyinstaller==4.10
|
pip install pyinstaller>=5.6 git+https://github.com/jdegenstein/pyinstaller-hooks-contrib
|
||||||
pip install path
|
pip install path
|
||||||
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/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.git#egg=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: powershell
|
||||||
run: |
|
run: |
|
||||||
micromamba info
|
micromamba info
|
||||||
pyinstaller --debug all 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\
|
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
|
- uses: alehechka/upload-tartifact@v2
|
||||||
with:
|
with:
|
||||||
name: CQ-editor-Windows
|
name: CQ-editor-Windows
|
||||||
path: dist
|
path: dist
|
||||||
|
|||||||
126
.github/workflows/pyinstaller-builds-actions-build-PIP-TAR-pyinst5-6p.yml
vendored
Normal file
126
.github/workflows/pyinstaller-builds-actions-build-PIP-TAR-pyinst5-6p.yml
vendored
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
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
|
||||||
18
README.md
18
README.md
@@ -4,6 +4,8 @@ This is a fork of [jmwright's fork](https://github.com/jmwright/CQ-editor) of [C
|
|||||||
|
|
||||||
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.
|
||||||
|
|
||||||
|
Running into issues? Please click here to join the [***CadQuery, CQ-Editor, and build123d Discord***](https://discord.com/invite/Bj9AQPsCfx)
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
[](https://ci.appveyor.com/project/adam-urbanczyk/cq-editor/branch/master)
|
[](https://ci.appveyor.com/project/adam-urbanczyk/cq-editor/branch/master)
|
||||||
@@ -33,7 +35,7 @@ CadQuery GUI editor based on PyQT supports Linux, Windows and Mac.
|
|||||||
|
|
||||||
### Release Packages
|
### Release Packages
|
||||||
|
|
||||||
TBD
|
Stable release builds which do not require Anaconda are attached to the [latest release](https://github.com/jdegenstein/jmwright-CQ-editor/releases). Download the zip file for your operating system, extract it, and run the CQ-editor script for your OS (CQ-editor.cmd for Windows, CQ-editor.sh for Linux and MacOS). On Windows you should be able to simply double-click on CQ-editor.cmd. On Linux and MacOS you may need to make the script executable with `chmod +x CQ-editor.sh` and run the script from the command line. On later MacOS versions you may also need `xattr -r -d com.apple.quarantine path/to/CQ-editor-MacOS`. The script contains an environment variable export that may be required to get CQ-editor to launch correctly on MacOS Big Sur, so it is better to use the script than to launch CQ-editor directly.
|
||||||
|
|
||||||
### Development Packages
|
### Development Packages
|
||||||
|
|
||||||
@@ -41,19 +43,27 @@ Development builds are also available, but can be unstable and should be used at
|
|||||||
|
|
||||||
## Installation (pip)
|
## Installation (pip)
|
||||||
|
|
||||||
|
Additional packages for Linux (known as needed on Ubuntu 22.04):
|
||||||
|
```
|
||||||
|
sudo apt install qtbase5-dev qt5-qmake
|
||||||
|
```
|
||||||
|
All platforms (Windows/Mac/Linux):
|
||||||
```
|
```
|
||||||
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
|
pip install --pre "cadquery>=2.2"
|
||||||
|
pip install git+https://github.com/gumyr/build123d
|
||||||
```
|
```
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
### Showing Objects
|
### Showing Objects
|
||||||
|
|
||||||
By default, CQ-editor will display a 3D representation of all `Workplane` objects in a script with a default color and alpha (transparency). To have more control over what is shown, and what the color and alpha settings are, the `show_object` method can be used. `show_object` tells CQ-editor to explicity display an object, and accepts the `options` parameter. The `options` parameter is a dictionary of rendering options named `alpha` and `color`. `alpha` is scaled between 0.0 and 1.0, with 0.0 being completely opaque and 1.0 being completely transparent. The color is set using R (red), G (green) and B (blue) values, and each one is scaled from 0 to 255. Either option or both can be omitted.
|
By default, CQ-editor will display a 3D representation of all `Workplane` objects in a script with a default color and alpha (transparency). To have more control over what is shown, and what the color and alpha settings are, the `show_object` method can be used. `show_object` tells CQ-editor to explicity display an object, and accepts the `options` parameter. The `options` parameter is a dictionary of rendering options named `alpha` and `color`. `alpha` is scaled between 0.0 and 1.0, with 0.0 being completely opaque and 1.0 being completely transparent. The color is set using R (red), G (green) and B (blue) values, and each one is scaled from 0 to 255. Either option or both can be omitted. The `name` parameter can assign a custom name which will appear in the objects pane of CQ-editor.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
show_object(result, options={"alpha":0.5, "color": (64, 164, 223)})
|
show_object(result, name="somename", options={"alpha":0.5, "color": (64, 164, 223)})
|
||||||
|
# or using rand_color:
|
||||||
|
show_object(result, name="somename", options=rand_color(alpha=.5))
|
||||||
```
|
```
|
||||||
|
|
||||||
Note that `show_object` works for `Shape` and `TopoDS_Shape` objects too. In order to display objects from the embedded Python console use `show`.
|
Note that `show_object` works for `Shape` and `TopoDS_Shape` objects too. In order to display objects from the embedded Python console use `show`.
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import sys
|
import sys
|
||||||
import argparse
|
import argparse
|
||||||
|
|
||||||
from PyQt5.QtWidgets import QApplication
|
from PySide6.QtWidgets import QApplication
|
||||||
|
|
||||||
NAME = 'CQ-editor'
|
NAME = 'CQ-editor'
|
||||||
|
|
||||||
|
|||||||
@@ -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 PyQt5.QtGui import QColor
|
from PySide6.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)
|
||||||
|
|||||||
@@ -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 PyQt5.QtCore import pyqtSignal, QFileSystemWatcher, QTimer
|
from PySide6.QtCore import Signal as pyqtSignal, QFileSystemWatcher, QTimer
|
||||||
from PyQt5.QtWidgets import QAction, QFileDialog
|
from PySide6.QtWidgets import QAction, QFileDialog
|
||||||
from PyQt5.QtGui import QFontDatabase
|
from PySide6.QtGui import QFontDatabase
|
||||||
from path import Path
|
from path import Path
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
@@ -17,91 +17,104 @@ from ..utils import get_save_filename, get_open_filename, confirm
|
|||||||
|
|
||||||
from ..icons import icon
|
from ..icons import icon
|
||||||
|
|
||||||
class Editor(CodeEditor,ComponentMixin):
|
|
||||||
|
|
||||||
name = 'Code Editor'
|
class Editor(CodeEditor, ComponentMixin):
|
||||||
|
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(name='Preferences',children=[
|
preferences = Parameter.create(
|
||||||
{'name': 'Font size', 'type': 'int', 'value': 12},
|
name="Preferences",
|
||||||
{'name': 'Autoreload', 'type': 'bool', 'value': False},
|
children=[
|
||||||
{'name': 'Autoreload delay', 'type': 'int', 'value': 50},
|
{"name": "Font size", "type": "int", "value": 12},
|
||||||
{'name': 'Autoreload: watch imported modules', 'type': 'bool', 'value': False},
|
{"name": "Autoreload", "type": "bool", "value": False},
|
||||||
{'name': 'Line wrap', 'type': 'bool', 'value': False},
|
{"name": "Autoreload delay", "type": "int", "value": 50},
|
||||||
{'name': 'Color scheme', 'type': 'list',
|
{
|
||||||
'values': ['Spyder','Monokai','Zenburn'], 'value': 'Spyder'}])
|
"name": "Autoreload: watch imported modules",
|
||||||
|
"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(linenumbers=True,
|
self.setup_editor(
|
||||||
|
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' : [QAction(icon('new'),
|
"File": [
|
||||||
'New',
|
QAction(
|
||||||
|
icon("new"), "New", self, shortcut="ctrl+N", triggered=self.new
|
||||||
|
),
|
||||||
|
QAction(
|
||||||
|
icon("open"), "Open", self, shortcut="ctrl+O", triggered=self.open
|
||||||
|
),
|
||||||
|
QAction(
|
||||||
|
icon("save"), "Save", self, shortcut="ctrl+S", triggered=self.save
|
||||||
|
),
|
||||||
|
QAction(
|
||||||
|
icon("save_as"),
|
||||||
|
"Save as",
|
||||||
self,
|
self,
|
||||||
shortcut='ctrl+N',
|
shortcut="ctrl+shift+S",
|
||||||
triggered=self.new),
|
triggered=self.save_as,
|
||||||
QAction(icon('open'),
|
),
|
||||||
'Open',
|
QAction(
|
||||||
|
icon("autoreload"),
|
||||||
|
"Automatic reload and preview",
|
||||||
self,
|
self,
|
||||||
shortcut='ctrl+O',
|
triggered=self.autoreload,
|
||||||
triggered=self.open),
|
|
||||||
QAction(icon('save'),
|
|
||||||
'Save',
|
|
||||||
self,
|
|
||||||
shortcut='ctrl+S',
|
|
||||||
triggered=self.save),
|
|
||||||
QAction(icon('save_as'),
|
|
||||||
'Save as',
|
|
||||||
self,
|
|
||||||
shortcut='ctrl+shift+S',
|
|
||||||
triggered=self.save_as),
|
|
||||||
QAction(icon('autoreload'),
|
|
||||||
'Automatic reload and preview',
|
|
||||||
self,triggered=self.autoreload,
|
|
||||||
checkable=True,
|
checkable=True,
|
||||||
checked=False,
|
checked=False,
|
||||||
objectName='autoreload'),
|
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)
|
||||||
@@ -109,52 +122,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') \
|
self.findChild(QAction, "autoreload").setChecked(self.preferences["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(self,'Please confirm','Current document is not saved - do you want to continue?')
|
rv = confirm(
|
||||||
|
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
|
||||||
|
|
||||||
if not self.confirm_discard(): return
|
self.set_text("")
|
||||||
|
self.filename = ""
|
||||||
self.set_text('')
|
|
||||||
self.filename = ''
|
|
||||||
self.reset_modified()
|
self.reset_modified()
|
||||||
|
|
||||||
def open(self):
|
def open(self):
|
||||||
|
if not self.confirm_discard():
|
||||||
if not self.confirm_discard(): return
|
return
|
||||||
|
|
||||||
curr_dir = Path(self.filename).abspath().dirname()
|
curr_dir = Path(self.filename).abspath().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()
|
||||||
@@ -164,24 +177,22 @@ 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._filename != '':
|
if self.preferences["Autoreload"]:
|
||||||
|
|
||||||
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)
|
||||||
|
|
||||||
@@ -191,21 +202,26 @@ 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 (self._watched_file != self.filename or not self.preferences['Autoreload']):
|
if self._watched_file and (
|
||||||
|
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 self.preferences['Autoreload'] and self.filename and self.filename != self._watched_file:
|
if (
|
||||||
|
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()
|
||||||
|
|
||||||
@@ -227,7 +243,7 @@ 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)
|
||||||
@@ -241,51 +257,45 @@ 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)
|
||||||
|
|
||||||
if self.filename != '':
|
def restoreComponentState(self, store):
|
||||||
store.setValue(self.name+'/state',self.filename)
|
filename = store.value(self.name + "/state")
|
||||||
|
|
||||||
def restoreComponentState(self,store):
|
if filename and self.filename == "":
|
||||||
|
|
||||||
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)
|
||||||
|
|
||||||
@@ -293,8 +303,7 @@ 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()
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ Created on Fri May 25 14:47:10 2018
|
|||||||
@author: adam
|
@author: adam
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from PyQt5.QtGui import QIcon
|
from PySide6.QtGui import QIcon
|
||||||
|
|
||||||
from . import icons_res
|
from . import icons_res
|
||||||
_icons = {
|
_icons = {
|
||||||
|
|||||||
@@ -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 PyQt5 import QtCore
|
from PySide6 import QtCore
|
||||||
|
|
||||||
qt_resource_data = b"\
|
qt_resource_data = b"\
|
||||||
\x00\x00\x4e\x4c\
|
\x00\x00\x4e\x4c\
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
from PyQt5.QtWidgets import (QLabel, QMainWindow, QToolBar, QDockWidget, QAction)
|
from PySide6.QtWidgets import QLabel, QMainWindow, QToolBar, QDockWidget
|
||||||
|
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
|
||||||
|
|
||||||
@@ -14,16 +16,26 @@ 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 dock, add_actions, open_url, about_dialog, check_gtihub_for_updates, confirm
|
from .utils import (
|
||||||
|
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 PyQt5.QtCore import Qt
|
from PySide6.QtCore import Qt
|
||||||
from PyQt5.QtWidgets import QApplication
|
from PySide6.QtWidgets import QApplication
|
||||||
from PyQt5.QtGui import QPalette, QColor
|
from PySide6.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:
|
||||||
@@ -43,141 +55,131 @@ 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):
|
|
||||||
|
|
||||||
name = 'CQ-Editor'
|
class MainWindow(QMainWindow, MainMixin):
|
||||||
org = 'CadQuery'
|
name = "CQ-Editor"
|
||||||
|
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.restorePreferences()
|
||||||
self.restoreWindow()
|
# self.restoreWindow()
|
||||||
|
|
||||||
if filename:
|
# if filename:
|
||||||
self.components['editor'].load_from_file(filename)
|
# self.components["editor"].load_from_file(filename)
|
||||||
|
|
||||||
self.restoreComponentState()
|
# 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(
|
||||||
self.registerComponent('editor',
|
"editor",
|
||||||
Editor(self),
|
Editor(self),
|
||||||
lambda c : dock(c,
|
lambda c: dock(c, "Editor", self, defaultArea="left"),
|
||||||
'Editor',
|
)
|
||||||
self,
|
|
||||||
defaultArea='left'))
|
|
||||||
|
|
||||||
self.registerComponent('object_tree',
|
self.registerComponent(
|
||||||
|
"object_tree",
|
||||||
ObjectTree(self),
|
ObjectTree(self),
|
||||||
lambda c: dock(c,
|
lambda c: dock(c, "Objects", self, defaultArea="right"),
|
||||||
'Objects',
|
)
|
||||||
self,
|
|
||||||
defaultArea='right'))
|
|
||||||
|
|
||||||
self.registerComponent('console',
|
self.registerComponent(
|
||||||
|
"console",
|
||||||
ConsoleWidget(self),
|
ConsoleWidget(self),
|
||||||
lambda c: dock(c,
|
lambda c: dock(c, "Console", self, defaultArea="bottom"),
|
||||||
'Console',
|
)
|
||||||
self,
|
|
||||||
defaultArea='bottom'))
|
|
||||||
|
|
||||||
self.registerComponent('traceback_viewer',
|
self.registerComponent(
|
||||||
|
"traceback_viewer",
|
||||||
TracebackPane(self),
|
TracebackPane(self),
|
||||||
lambda c: dock(c,
|
lambda c: dock(c, "Current traceback", self, defaultArea="bottom"),
|
||||||
'Current traceback',
|
)
|
||||||
self,
|
|
||||||
defaultArea='bottom'))
|
|
||||||
|
|
||||||
self.registerComponent('debugger',Debugger(self))
|
self.registerComponent("debugger", Debugger(self))
|
||||||
|
|
||||||
self.registerComponent('variables_viewer',LocalsView(self),
|
self.registerComponent(
|
||||||
lambda c: dock(c,
|
"variables_viewer",
|
||||||
'Variables',
|
LocalsView(self),
|
||||||
self,
|
lambda c: dock(c, "Variables", self, defaultArea="right"),
|
||||||
defaultArea='right'))
|
)
|
||||||
|
|
||||||
self.registerComponent('cq_object_inspector',
|
self.registerComponent(
|
||||||
|
"cq_object_inspector",
|
||||||
CQObjectInspector(self),
|
CQObjectInspector(self),
|
||||||
lambda c: dock(c,
|
lambda c: dock(c, "CQ object inspector", self, defaultArea="right"),
|
||||||
'CQ object inspector',
|
)
|
||||||
self,
|
self.registerComponent(
|
||||||
defaultArea='right'))
|
"log",
|
||||||
self.registerComponent('log',
|
|
||||||
LogViewer(self),
|
LogViewer(self),
|
||||||
lambda c: dock(c,
|
lambda c: dock(c, "Log viewer", self, defaultArea="bottom"),
|
||||||
'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 = {'File' : menu_file,
|
menus = {
|
||||||
'Edit' : menu_edit,
|
"File": menu_file,
|
||||||
'Run' : menu_run,
|
"Edit": menu_edit,
|
||||||
'Tools': menu_tools,
|
"Run": menu_run,
|
||||||
'View' : menu_view,
|
"Tools": menu_tools,
|
||||||
'Help' : menu_help}
|
"View": menu_view,
|
||||||
|
"Help": menu_help,
|
||||||
|
}
|
||||||
|
|
||||||
for comp in self.components.values():
|
for comp in self.components.values():
|
||||||
self.prepare_menubar_component(menus,
|
self.prepare_menubar_component(menus, comp.menuActions())
|
||||||
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())
|
||||||
@@ -186,181 +188,200 @@ 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(icon('preferences'),
|
QAction(
|
||||||
'Preferences',
|
icon("preferences"),
|
||||||
self,triggered=self.edit_preferences))
|
"Preferences",
|
||||||
|
self,
|
||||||
|
triggered=self.edit_preferences,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
menu_help.addAction( \
|
menu_help.addAction(
|
||||||
QAction(icon('help'),
|
QAction(icon("help"), "Documentation", self, triggered=self.documentation)
|
||||||
'Documentation',
|
)
|
||||||
self,triggered=self.documentation))
|
|
||||||
|
|
||||||
menu_help.addAction( \
|
menu_help.addAction(
|
||||||
QAction('CQ documentation',
|
QAction("CQ documentation", self, triggered=self.cq_documentation)
|
||||||
self,triggered=self.cq_documentation))
|
)
|
||||||
|
|
||||||
menu_help.addAction( \
|
menu_help.addAction(QAction(icon("about"), "About", self, triggered=self.about))
|
||||||
QAction(icon('about'),
|
|
||||||
'About',
|
|
||||||
self,triggered=self.about))
|
|
||||||
|
|
||||||
menu_help.addAction( \
|
menu_help.addAction(
|
||||||
QAction('Check for CadQuery updates',
|
QAction(
|
||||||
self,triggered=self.check_for_cq_updates))
|
"Check for CadQuery updates", self, triggered=self.check_for_cq_updates
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
def prepare_menubar_component(self,menus,comp_menu_dict):
|
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['debugger'].sigRendered\
|
self.components["object_tree"].sigObjectsAdded[list].connect(
|
||||||
.connect(self.components['object_tree'].addObjects)
|
self.components["viewer"].display_many
|
||||||
self.components['debugger'].sigTraceback\
|
)
|
||||||
.connect(self.components['traceback_viewer'].addTraceback)
|
self.components["object_tree"].sigObjectsAdded2[list, bool].connect(
|
||||||
self.components['debugger'].sigLocals\
|
self.components["viewer"].display_many
|
||||||
.connect(self.components['variables_viewer'].update_frame)
|
)
|
||||||
self.components['debugger'].sigLocals\
|
self.components["object_tree"].sigItemChanged.connect(
|
||||||
.connect(self.components['console'].push_vars)
|
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['object_tree'].sigObjectsAdded[list]\
|
self.components["viewer"].sigObjectSelected.connect(
|
||||||
.connect(self.components['viewer'].display_many)
|
self.components["object_tree"].handleGraphicalSelection
|
||||||
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['viewer'].sigObjectSelected\
|
self.components["traceback_viewer"].sigHighlightLine.connect(
|
||||||
.connect(self.components['object_tree'].handleGraphicalSelection)
|
self.components["editor"].go_to_line
|
||||||
|
)
|
||||||
|
|
||||||
self.components['traceback_viewer'].sigHighlightLine\
|
self.components["cq_object_inspector"].sigDisplayObjects.connect(
|
||||||
.connect(self.components['editor'].go_to_line)
|
self.components["viewer"].display_many
|
||||||
|
)
|
||||||
|
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['cq_object_inspector'].sigDisplayObjects\
|
self.components["debugger"].sigLocalsChanged.connect(
|
||||||
.connect(self.components['viewer'].display_many)
|
self.components["variables_viewer"].update_frame
|
||||||
self.components['cq_object_inspector'].sigRemoveObjects\
|
)
|
||||||
.connect(self.components['viewer'].remove_items)
|
self.components["debugger"].sigLineChanged.connect(
|
||||||
self.components['cq_object_inspector'].sigShowPlane\
|
self.components["editor"].go_to_line
|
||||||
.connect(self.components['viewer'].toggle_grid)
|
)
|
||||||
self.components['cq_object_inspector'].sigShowPlane[bool,float]\
|
self.components["debugger"].sigDebugging.connect(
|
||||||
.connect(self.components['viewer'].toggle_grid)
|
self.components["object_tree"].stashObjects
|
||||||
self.components['cq_object_inspector'].sigChangePlane\
|
)
|
||||||
.connect(self.components['viewer'].set_grid_orientation)
|
self.components["debugger"].sigCQChanged.connect(
|
||||||
|
self.components["object_tree"].addObjects
|
||||||
self.components['debugger'].sigLocalsChanged\
|
)
|
||||||
.connect(self.components['variables_viewer'].update_frame)
|
self.components["debugger"].sigTraceback.connect(
|
||||||
self.components['debugger'].sigLineChanged\
|
self.components["traceback_viewer"].addTraceback
|
||||||
.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 \
|
self.components["editor"].triggerRerender.connect(
|
||||||
.connect(self.components['debugger'].render)
|
self.components["debugger"].render
|
||||||
self.components['editor'].sigFilenameChanged\
|
)
|
||||||
.connect(self.handle_filename_change)
|
self.components["editor"].sigFilenameChanged.connect(
|
||||||
|
self.handle_filename_change
|
||||||
|
)
|
||||||
|
|
||||||
def prepare_console(self):
|
def prepare_console(self):
|
||||||
|
console = self.components["console"]
|
||||||
|
obj_tree = self.components["object_tree"]
|
||||||
|
|
||||||
console = self.components['console']
|
# application related items
|
||||||
obj_tree = self.components['object_tree']
|
console.push_vars({"self": self})
|
||||||
|
|
||||||
#application related items
|
# CQ related items
|
||||||
console.push_vars({'self' : self})
|
console.push_vars(
|
||||||
|
{
|
||||||
#CQ related items
|
"show": obj_tree.addObject,
|
||||||
console.push_vars({'show' : obj_tree.addObject,
|
"show_object": obj_tree.addObject,
|
||||||
'show_object' : obj_tree.addObject,
|
"rand_color": self.components["debugger"]._rand_color,
|
||||||
'rand_color' : self.components['debugger']._rand_color,
|
"cq": cq,
|
||||||
'cq' : cq,
|
"log": Logger(self.name).info,
|
||||||
'log' : Logger(self.name).info})
|
}
|
||||||
|
)
|
||||||
|
|
||||||
def fill_dummy(self):
|
def fill_dummy(self):
|
||||||
|
self.components["editor"].set_text(
|
||||||
self.components['editor']\
|
'import cadquery as cq\nresult = cq.Workplane("XY" ).box(3, 3, 0.5).edges("|Z").fillet(0.125)'
|
||||||
.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("Uncaught exception occurred",
|
self._logger.error(
|
||||||
exc_info=(exc_type, exc_value, exc_traceback))
|
"Uncaught exception occurred",
|
||||||
|
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
|
||||||
|
|||||||
@@ -10,72 +10,67 @@ from functools import reduce
|
|||||||
from operator import add
|
from operator import add
|
||||||
from logbook import Logger
|
from logbook import Logger
|
||||||
|
|
||||||
from PyQt5.QtCore import pyqtSlot, QSettings
|
from PySide6.QtCore import QSettings
|
||||||
|
from PySide6.QtCore import Slot as pyqtSlot
|
||||||
|
|
||||||
|
|
||||||
class MainMixin(object):
|
class MainMixin(object):
|
||||||
|
name = "Main"
|
||||||
name = 'Main'
|
org = "Unknown"
|
||||||
org = 'Unknown'
|
|
||||||
|
|
||||||
components = {}
|
components = {}
|
||||||
docks = {}
|
docks = {}
|
||||||
preferences = None
|
preferences = None
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
self.settings = QSettings(self.org, self.name)
|
||||||
|
|
||||||
self.settings = QSettings(self.org,self.name)
|
def registerComponent(self, name, component, dock=None):
|
||||||
|
|
||||||
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('geometry',self.saveGeometry())
|
self.settings.setValue("windowState", self.saveState())
|
||||||
self.settings.setValue('windowState',self.saveState())
|
|
||||||
|
|
||||||
def restoreWindow(self):
|
def restoreWindow(self):
|
||||||
|
if self.settings.value("geometry"):
|
||||||
if self.settings.value('geometry'):
|
self.restoreGeometry(self.settings.value("geometry"))
|
||||||
self.restoreGeometry(self.settings.value('geometry'))
|
if self.settings.value("windowState"):
|
||||||
if self.settings.value('windowState'):
|
self.restoreState(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(settings.value('General'),
|
self.preferences.restoreState(
|
||||||
removeChildren=False)
|
settings.value("General"), 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(settings.value(comp.name),
|
comp.preferences.restoreState(
|
||||||
removeChildren=False)
|
settings.value(comp.name), 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():
|
||||||
@@ -83,42 +78,32 @@ class MainMixin(object):
|
|||||||
|
|
||||||
|
|
||||||
class ComponentMixin(object):
|
class ComponentMixin(object):
|
||||||
|
name = "Component"
|
||||||
|
|
||||||
name = 'Component'
|
|
||||||
preferences = None
|
preferences = None
|
||||||
|
|
||||||
_actions = {}
|
_actions = {}
|
||||||
|
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|
||||||
if self.preferences:
|
if self.preferences:
|
||||||
self.preferences.sigTreeStateChanged.\
|
self.preferences.sigTreeStateChanged.connect(self.updatePreferences)
|
||||||
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
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
from PyQt5.QtWidgets import (QTreeWidget, QTreeWidgetItem,
|
from PySide6.QtWidgets import QTreeWidget, QTreeWidgetItem, QStackedWidget, QDialog
|
||||||
QStackedWidget, QDialog)
|
from PySide6.QtCore import Slot as pyqtSlot, Qt
|
||||||
from PyQt5.QtCore import pyqtSlot, Qt
|
|
||||||
|
|
||||||
from pyqtgraph.parametertree import ParameterTree
|
from pyqtgraph.parametertree import ParameterTree
|
||||||
|
|
||||||
@@ -8,55 +7,54 @@ from .utils import splitter, layout
|
|||||||
|
|
||||||
|
|
||||||
class PreferencesTreeItem(QTreeWidgetItem):
|
class PreferencesTreeItem(QTreeWidgetItem):
|
||||||
|
def __init__(
|
||||||
def __init__(self,name,widget,):
|
self,
|
||||||
|
name,
|
||||||
super(PreferencesTreeItem,self).__init__(name)
|
widget,
|
||||||
|
):
|
||||||
|
super(PreferencesTreeItem, self).__init__(name)
|
||||||
self.widget = widget
|
self.widget = widget
|
||||||
|
|
||||||
|
|
||||||
class PreferencesWidget(QDialog):
|
class PreferencesWidget(QDialog):
|
||||||
|
def __init__(self, parent, components):
|
||||||
def __init__(self,parent,components):
|
super(PreferencesWidget, self).__init__(
|
||||||
|
|
||||||
super(PreferencesWidget,self).__init__(
|
|
||||||
parent,
|
parent,
|
||||||
Qt.Window | Qt.CustomizeWindowHint | Qt.WindowCloseButtonHint,
|
Qt.Window | Qt.CustomizeWindowHint | Qt.WindowCloseButtonHint,
|
||||||
windowTitle='Preferences')
|
windowTitle="Preferences",
|
||||||
|
)
|
||||||
|
|
||||||
self.stacked = QStackedWidget(self)
|
self.stacked = QStackedWidget(self)
|
||||||
self.preferences_tree = QTreeWidget(self,
|
self.preferences_tree = QTreeWidget(
|
||||||
|
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',
|
self.add("General", parent)
|
||||||
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,),
|
self.root.addChild(PreferencesTreeItem((name,), widget))
|
||||||
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)
|
||||||
|
|
||||||
|
|||||||
@@ -2,22 +2,27 @@ import requests
|
|||||||
|
|
||||||
from pkg_resources import parse_version
|
from pkg_resources import parse_version
|
||||||
|
|
||||||
from PyQt5 import QtCore, QtWidgets
|
from PySide6 import QtCore, QtWidgets
|
||||||
from PyQt5.QtGui import QDesktopServices
|
from PySide6.QtGui import QDesktopServices
|
||||||
from PyQt5.QtCore import QUrl
|
from PySide6.QtCore import QUrl
|
||||||
from PyQt5.QtWidgets import QFileDialog, QMessageBox
|
from PySide6.QtWidgets import QFileDialog, QMessageBox
|
||||||
|
|
||||||
DOCK_POSITIONS = {'right' : QtCore.Qt.RightDockWidgetArea,
|
DOCK_POSITIONS = {
|
||||||
'left' : QtCore.Qt.LeftDockWidgetArea,
|
"right": QtCore.Qt.RightDockWidgetArea,
|
||||||
'top' : QtCore.Qt.TopDockWidgetArea,
|
"left": QtCore.Qt.LeftDockWidgetArea,
|
||||||
'bottom' : QtCore.Qt.BottomDockWidgetArea}
|
"top": QtCore.Qt.TopDockWidgetArea,
|
||||||
|
"bottom": QtCore.Qt.BottomDockWidgetArea,
|
||||||
|
}
|
||||||
|
|
||||||
def layout(parent,items,
|
|
||||||
top_widget = None,
|
|
||||||
layout_type = QtWidgets.QVBoxLayout,
|
|
||||||
margin = 2,
|
|
||||||
spacing = 0):
|
|
||||||
|
|
||||||
|
def layout(
|
||||||
|
parent,
|
||||||
|
items,
|
||||||
|
top_widget=None,
|
||||||
|
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
|
||||||
@@ -26,109 +31,123 @@ def layout(parent,items,
|
|||||||
layout = layout_type(top_widget)
|
layout = layout_type(top_widget)
|
||||||
top_widget.setLayout(layout)
|
top_widget.setLayout(layout)
|
||||||
|
|
||||||
for item in items: layout.addWidget(item)
|
for item in items:
|
||||||
|
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,
|
|
||||||
stretch_factors = None,
|
|
||||||
orientation=QtCore.Qt.Horizontal):
|
|
||||||
|
|
||||||
|
def splitter(items, stretch_factors=None, orientation=QtCore.Qt.Horizontal):
|
||||||
sp = QtWidgets.QSplitter(orientation)
|
sp = QtWidgets.QSplitter(orientation)
|
||||||
|
|
||||||
for item in items: sp.addWidget(item)
|
for item in items:
|
||||||
|
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(
|
||||||
|
widget,
|
||||||
title,
|
title,
|
||||||
parent,
|
parent,
|
||||||
allowedAreas = QtCore.Qt.AllDockWidgetAreas,
|
allowedAreas=QtCore.Qt.AllDockWidgetAreas,
|
||||||
defaultArea = 'right',
|
defaultArea="right",
|
||||||
name=None,
|
name=None,
|
||||||
icon = None):
|
icon=None,
|
||||||
|
):
|
||||||
|
dock = QtWidgets.QDockWidget(title, parent, objectName=title)
|
||||||
|
|
||||||
dock = QtWidgets.QDockWidget(title,parent,objectName=title)
|
if name:
|
||||||
|
dock.setObjectName(name)
|
||||||
if name: dock.setObjectName(name)
|
if icon:
|
||||||
if icon: dock.toggleViewAction().setIcon(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(QtWidgets.QDockWidget.DockWidgetFeatures(\
|
dock.setFeatures(
|
||||||
QtWidgets.QDockWidget.AllDockWidgetFeatures))
|
QtWidgets.QDockWidget.DockWidgetFeatures(
|
||||||
|
QtWidgets.QDockWidget.DockWidgetFloatable
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
parent.addDockWidget(DOCK_POSITIONS[defaultArea],
|
parent.addDockWidget(DOCK_POSITIONS[defaultArea], dock)
|
||||||
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):
|
|
||||||
|
|
||||||
QtWidgets.QMessageBox.about(parent,title,text)
|
def about_dialog(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))
|
||||||
rv,_ = QFileDialog.getSaveFileName(filter='*.{}'.format(suffix))
|
if rv != "" and not rv.endswith(suffix):
|
||||||
if rv != '' and not rv.endswith(suffix): rv += '.'+suffix
|
rv += "." + suffix
|
||||||
|
|
||||||
return rv
|
return rv
|
||||||
|
|
||||||
|
|
||||||
def get_open_filename(suffix, curr_dir):
|
def get_open_filename(suffix, curr_dir):
|
||||||
|
rv, _ = QFileDialog.getOpenFileName(
|
||||||
rv,_ = QFileDialog.getOpenFileName(directory=curr_dir, filter='*.{}'.format(suffix))
|
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,
|
|
||||||
mod,
|
|
||||||
github_org='cadquery',
|
|
||||||
github_proj='cadquery'):
|
|
||||||
|
|
||||||
url = f'https://api.github.com/repos/{github_org}/{github_proj}/releases'
|
def check_gtihub_for_updates(
|
||||||
|
parent, mod, github_org="cadquery", github_proj="cadquery"
|
||||||
|
):
|
||||||
|
url = f"https://api.github.com/repos/{github_org}/{github_proj}/releases"
|
||||||
resp = requests.get(url).json()
|
resp = requests.get(url).json()
|
||||||
|
|
||||||
newer = [el['tag_name'] for el in resp if not el['draft'] and \
|
newer = [
|
||||||
parse_version(el['tag_name']) > parse_version(mod.__version__)]
|
el["tag_name"]
|
||||||
|
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=f'There are newer versions of {github_proj} ' \
|
text = (
|
||||||
f'available on github:\n' + '\n'.join(newer)
|
f"There are newer versions of {github_proj} "
|
||||||
|
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
|
||||||
|
|||||||
@@ -1,23 +1,23 @@
|
|||||||
from PyQt5.QtWidgets import QApplication
|
from PySide6.QtWidgets import QApplication
|
||||||
from PyQt5.QtCore import pyqtSlot
|
from PySide6.QtCore import Slot as 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):
|
|
||||||
|
|
||||||
name = 'Console'
|
class ConsoleWidget(RichJupyterWidget, ComponentMixin):
|
||||||
|
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,12 +34,12 @@ 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()
|
||||||
@@ -70,7 +70,6 @@ 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
|
||||||
@@ -84,18 +83,15 @@ class ConsoleWidget(RichJupyterWidget,ComponentMixin):
|
|||||||
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_())
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
from PyQt5.QtWidgets import QTreeWidget, QTreeWidgetItem, QAction
|
from PySide6.QtWidgets import QTreeWidget, QTreeWidgetItem
|
||||||
from PyQt5.QtCore import Qt, pyqtSlot, pyqtSignal
|
from PySide6.QtCore import Qt, Slot as pyqtSlot, Signal as 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,63 +10,62 @@ from ..mixins import ComponentMixin
|
|||||||
from ..icons import icon
|
from ..icons import icon
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class CQChildItem(QTreeWidgetItem):
|
class CQChildItem(QTreeWidgetItem):
|
||||||
|
def __init__(self, cq_item, **kwargs):
|
||||||
def __init__(self,cq_item,**kwargs):
|
super(CQChildItem, self).__init__(
|
||||||
|
[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):
|
||||||
def __init__(self,name,workplane=None,**kwargs):
|
super(CQStackItem, self).__init__([name, ""], **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],[bool,float])
|
sigShowPlane = pyqtSignal(bool)
|
||||||
|
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(icon('inspect'),'Inspect CQ object',self,\
|
QAction(
|
||||||
toggled=self.inspect,checkable=True)]
|
icon("inspect"),
|
||||||
|
"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()
|
||||||
@@ -77,7 +76,6 @@ class CQObjectInspector(QTreeWidget,ComponentMixin):
|
|||||||
|
|
||||||
@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()
|
||||||
@@ -90,40 +88,37 @@ class CQObjectInspector(QTreeWidget,ComponentMixin):
|
|||||||
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(cq_plane.origin.toPnt(),
|
plane = gp_Ax3(
|
||||||
cq_plane.zDir.toDir(),
|
cq_plane.origin.toPnt(), cq_plane.zDir.toDir(), cq_plane.xDir.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
|
||||||
|
|
||||||
|
|
||||||
@@ -5,10 +5,17 @@ from types import SimpleNamespace, FrameType, ModuleType
|
|||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
import cadquery as cq
|
import cadquery as cq
|
||||||
from PyQt5 import QtCore
|
from PySide6 import QtCore
|
||||||
from PyQt5.QtCore import Qt, QObject, pyqtSlot, pyqtSignal, QEventLoop, QAbstractTableModel
|
from PySide6.QtCore import (
|
||||||
from PyQt5.QtWidgets import (QAction,
|
Qt,
|
||||||
QTableView)
|
QObject,
|
||||||
|
Slot as pyqtSlot,
|
||||||
|
Signal as pyqtSignal,
|
||||||
|
QEventLoop,
|
||||||
|
QAbstractTableModel,
|
||||||
|
)
|
||||||
|
from PySide6.QtWidgets import 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
|
||||||
@@ -18,46 +25,43 @@ from random import randrange as rrr, seed
|
|||||||
from ..cq_utils import find_cq_objects, reload_cq
|
from ..cq_utils import find_cq_objects, reload_cq
|
||||||
from ..mixins import ComponentMixin
|
from ..mixins import ComponentMixin
|
||||||
|
|
||||||
DUMMY_FILE = '<string>'
|
DUMMY_FILE = "<string>"
|
||||||
|
|
||||||
|
|
||||||
class DbgState(Enum):
|
class DbgState(Enum):
|
||||||
|
|
||||||
STEP = auto()
|
STEP = auto()
|
||||||
CONT = auto()
|
CONT = auto()
|
||||||
STEP_IN = auto()
|
STEP_IN = auto()
|
||||||
RETURN = auto()
|
RETURN = auto()
|
||||||
|
|
||||||
class DbgEevent(object):
|
|
||||||
|
|
||||||
LINE = 'line'
|
class DbgEevent(object):
|
||||||
CALL = 'call'
|
LINE = "line"
|
||||||
RETURN = 'return'
|
CALL = "call"
|
||||||
|
RETURN = "return"
|
||||||
|
|
||||||
|
|
||||||
class LocalsModel(QAbstractTableModel):
|
class LocalsModel(QAbstractTableModel):
|
||||||
|
HEADER = ("Name", "Type", "Value")
|
||||||
|
|
||||||
HEADER = ('Name','Type', 'Value')
|
def __init__(self, parent):
|
||||||
|
super(LocalsModel, self).__init__(parent)
|
||||||
def __init__(self,parent):
|
|
||||||
|
|
||||||
super(LocalsModel,self).__init__(parent)
|
|
||||||
self.frame = None
|
self.frame = None
|
||||||
|
|
||||||
def update_frame(self,frame):
|
def update_frame(self, frame):
|
||||||
|
self.frame = [
|
||||||
self.frame = \
|
(k, type(v).__name__, str(v))
|
||||||
[(k,type(v).__name__, str(v)) for k,v in frame.items() if not k.startswith('_')]
|
for k, v in frame.items()
|
||||||
|
if not k.startswith("_")
|
||||||
|
]
|
||||||
def rowCount(self,parent=QtCore.QModelIndex()):
|
|
||||||
|
|
||||||
|
def rowCount(self, parent=QtCore.QModelIndex()):
|
||||||
if self.frame:
|
if self.frame:
|
||||||
return len(self.frame)
|
return len(self.frame)
|
||||||
else:
|
else:
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
def columnCount(self,parent=QtCore.QModelIndex()):
|
def columnCount(self, parent=QtCore.QModelIndex()):
|
||||||
|
|
||||||
return 3
|
return 3
|
||||||
|
|
||||||
def headerData(self, section, orientation, role=Qt.DisplayRole):
|
def headerData(self, section, orientation, role=Qt.DisplayRole):
|
||||||
@@ -74,13 +78,11 @@ class LocalsModel(QAbstractTableModel):
|
|||||||
return QtCore.QVariant()
|
return QtCore.QVariant()
|
||||||
|
|
||||||
|
|
||||||
class LocalsView(QTableView,ComponentMixin):
|
class LocalsView(QTableView, ComponentMixin):
|
||||||
|
name = "Variables"
|
||||||
|
|
||||||
name = 'Variables'
|
def __init__(self, parent):
|
||||||
|
super(LocalsView, self).__init__(parent)
|
||||||
def __init__(self,parent):
|
|
||||||
|
|
||||||
super(LocalsView,self).__init__(parent)
|
|
||||||
ComponentMixin.__init__(self)
|
ComponentMixin.__init__(self)
|
||||||
|
|
||||||
header = self.horizontalHeader()
|
header = self.horizontalHeader()
|
||||||
@@ -90,180 +92,213 @@ class LocalsView(QTableView,ComponentMixin):
|
|||||||
vheader.setVisible(False)
|
vheader.setVisible(False)
|
||||||
|
|
||||||
@pyqtSlot(dict)
|
@pyqtSlot(dict)
|
||||||
def update_frame(self,frame):
|
def update_frame(self, frame):
|
||||||
|
|
||||||
model = LocalsModel(self)
|
model = LocalsModel(self)
|
||||||
model.update_frame(frame)
|
model.update_frame(frame)
|
||||||
|
|
||||||
self.setModel(model)
|
self.setModel(model)
|
||||||
|
|
||||||
class Debugger(QObject,ComponentMixin):
|
|
||||||
|
|
||||||
name = 'Debugger'
|
class Debugger(QObject, ComponentMixin):
|
||||||
|
name = "Debugger"
|
||||||
preferences = Parameter.create(name='Preferences',children=[
|
|
||||||
{'name': 'Reload CQ', 'type': 'bool', 'value': False},
|
|
||||||
{'name': 'Add script dir to path','type': 'bool', 'value': True},
|
|
||||||
{'name': 'Change working dir to script dir','type': 'bool', 'value': True},
|
|
||||||
{'name': 'Reload imported modules', 'type': 'bool', 'value': True},
|
|
||||||
])
|
|
||||||
|
|
||||||
|
preferences = Parameter.create(
|
||||||
|
name="Preferences",
|
||||||
|
children=[
|
||||||
|
{"name": "Reload CQ", "type": "bool", "value": False},
|
||||||
|
{"name": "Add script dir to path", "type": "bool", "value": True},
|
||||||
|
{"name": "Change working dir to script dir", "type": "bool", "value": True},
|
||||||
|
{"name": "Reload imported modules", "type": "bool", "value": True},
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
sigRendered = pyqtSignal(dict)
|
sigRendered = pyqtSignal(dict)
|
||||||
sigLocals = pyqtSignal(dict)
|
sigLocals = pyqtSignal(dict)
|
||||||
sigTraceback = pyqtSignal(object,str)
|
sigTraceback = pyqtSignal(object, str)
|
||||||
|
|
||||||
sigFrameChanged = pyqtSignal(object)
|
sigFrameChanged = pyqtSignal(object)
|
||||||
sigLineChanged = pyqtSignal(int)
|
sigLineChanged = pyqtSignal(int)
|
||||||
sigLocalsChanged = pyqtSignal(dict)
|
sigLocalsChanged = pyqtSignal(dict)
|
||||||
sigCQChanged = pyqtSignal(dict,bool)
|
sigCQChanged = pyqtSignal(dict, bool)
|
||||||
sigDebugging = pyqtSignal(bool)
|
sigDebugging = pyqtSignal(bool)
|
||||||
|
|
||||||
_frames : List[FrameType]
|
_frames: List[FrameType]
|
||||||
|
|
||||||
def __init__(self,parent):
|
def __init__(self, parent):
|
||||||
|
super(Debugger, self).__init__(parent)
|
||||||
super(Debugger,self).__init__(parent)
|
|
||||||
ComponentMixin.__init__(self)
|
ComponentMixin.__init__(self)
|
||||||
|
|
||||||
self.inner_event_loop = QEventLoop(self)
|
self.inner_event_loop = QEventLoop(self)
|
||||||
|
|
||||||
self._actions = \
|
self._actions = {
|
||||||
{'Run' : [QAction(icon('run'),
|
"Run": [
|
||||||
'Render',
|
QAction(
|
||||||
self,
|
icon("run"), "Render", self, shortcut="F5", triggered=self.render
|
||||||
shortcut='F5',
|
),
|
||||||
triggered=self.render),
|
QAction(
|
||||||
QAction(icon('debug'),
|
icon("debug"),
|
||||||
'Debug',
|
"Debug",
|
||||||
self,
|
self,
|
||||||
checkable=True,
|
checkable=True,
|
||||||
shortcut='ctrl+F5',
|
shortcut="ctrl+F5",
|
||||||
triggered=self.debug),
|
triggered=self.debug,
|
||||||
QAction(icon('arrow-step-over'),
|
),
|
||||||
'Step',
|
QAction(
|
||||||
|
icon("arrow-step-over"),
|
||||||
|
"Step",
|
||||||
self,
|
self,
|
||||||
shortcut='ctrl+F10',
|
shortcut="ctrl+F10",
|
||||||
triggered=lambda: self.debug_cmd(DbgState.STEP)),
|
triggered=lambda: self.debug_cmd(DbgState.STEP),
|
||||||
QAction(icon('arrow-step-in'),
|
),
|
||||||
'Step in',
|
QAction(
|
||||||
|
icon("arrow-step-in"),
|
||||||
|
"Step in",
|
||||||
self,
|
self,
|
||||||
shortcut='ctrl+F11',
|
shortcut="ctrl+F11",
|
||||||
triggered=lambda: self.debug_cmd(DbgState.STEP_IN)),
|
triggered=lambda: self.debug_cmd(DbgState.STEP_IN),
|
||||||
QAction(icon('arrow-continue'),
|
),
|
||||||
'Continue',
|
QAction(
|
||||||
|
icon("arrow-continue"),
|
||||||
|
"Continue",
|
||||||
self,
|
self,
|
||||||
shortcut='ctrl+F12',
|
shortcut="ctrl+F12",
|
||||||
triggered=lambda: self.debug_cmd(DbgState.CONT))
|
triggered=lambda: self.debug_cmd(DbgState.CONT),
|
||||||
]}
|
),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
self._frames = []
|
self._frames = []
|
||||||
|
|
||||||
def get_current_script(self):
|
def get_current_script(self):
|
||||||
|
return self.parent().components["editor"].get_text_with_eol()
|
||||||
return self.parent().components['editor'].get_text_with_eol()
|
|
||||||
|
|
||||||
def get_breakpoints(self):
|
def get_breakpoints(self):
|
||||||
|
return self.parent().components["editor"].debugger.get_breakpoints()
|
||||||
return self.parent().components['editor'].debugger.get_breakpoints()
|
|
||||||
|
|
||||||
def compile_code(self, cq_script):
|
def compile_code(self, cq_script):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
module = ModuleType('temp')
|
module = ModuleType("temp")
|
||||||
cq_code = compile(cq_script, '<string>', 'exec')
|
cq_code = compile(cq_script, "<string>", "exec")
|
||||||
return cq_code, module
|
return cq_code, module
|
||||||
except Exception:
|
except Exception:
|
||||||
self.sigTraceback.emit(sys.exc_info(), cq_script)
|
self.sigTraceback.emit(sys.exc_info(), cq_script)
|
||||||
return None, None
|
return None, None
|
||||||
|
|
||||||
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 "").abspath().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)
|
||||||
stack.callback(sys.path.remove, p)
|
stack.callback(sys.path.remove, p)
|
||||||
if self.preferences['Change working dir to script dir'] and p.exists():
|
if self.preferences["Change working dir to script dir"] and p.exists():
|
||||||
stack.enter_context(p)
|
stack.enter_context(p)
|
||||||
if self.preferences['Reload imported modules']:
|
if self.preferences["Reload imported modules"]:
|
||||||
stack.enter_context(module_manager())
|
stack.enter_context(module_manager())
|
||||||
|
|
||||||
exec(code, locals_dict, globals_dict)
|
exec(code, locals_dict, globals_dict)
|
||||||
|
|
||||||
def _rand_color(self, alpha = 0., cfloat=False):
|
def _rand_color(self, alpha=0.0, cfloat=False):
|
||||||
#helper function to generate a random color dict
|
# helper function to generate a random color dict
|
||||||
#for CQ-editor's show_object function
|
# for CQ-editor's show_object function
|
||||||
lower = 10
|
lower = 10
|
||||||
upper = 100 #not too high to keep color brightness in check
|
upper = 100 # not too high to keep color brightness in check
|
||||||
if cfloat: #for two output types depending on need
|
if cfloat: # for two output types depending on need
|
||||||
return (
|
return (
|
||||||
(rrr(lower,upper)/255),
|
(rrr(lower, upper) / 255),
|
||||||
(rrr(lower,upper)/255),
|
(rrr(lower, upper) / 255),
|
||||||
(rrr(lower,upper)/255),
|
(rrr(lower, upper) / 255),
|
||||||
alpha,
|
alpha,
|
||||||
)
|
)
|
||||||
return {"alpha": alpha,
|
return {
|
||||||
|
"alpha": alpha,
|
||||||
"color": (
|
"color": (
|
||||||
rrr(lower,upper),
|
rrr(lower, upper),
|
||||||
rrr(lower,upper),
|
rrr(lower, upper),
|
||||||
rrr(lower,upper),
|
rrr(lower, upper),
|
||||||
)}
|
),
|
||||||
|
}
|
||||||
def _inject_locals(self,module):
|
|
||||||
|
|
||||||
|
def _inject_locals(self, module):
|
||||||
cq_objects = {}
|
cq_objects = {}
|
||||||
|
|
||||||
def _show_object(obj,name=None, options={}):
|
def _show_object(
|
||||||
|
obj,
|
||||||
|
name=None,
|
||||||
|
options={}, # all following inputs are ignored by cq-editor
|
||||||
|
parent=1,
|
||||||
|
clear=True,
|
||||||
|
port=3939,
|
||||||
|
axes=False,
|
||||||
|
axes0=False,
|
||||||
|
grid=False,
|
||||||
|
ticks=10,
|
||||||
|
ortho=True,
|
||||||
|
transparent=False,
|
||||||
|
default_color=(232, 176, 36),
|
||||||
|
reset_camera=True,
|
||||||
|
zoom=1.0,
|
||||||
|
default_edgecolor=(128, 128, 128),
|
||||||
|
render_edges=True,
|
||||||
|
render_normals=False,
|
||||||
|
render_mates=False,
|
||||||
|
mate_scale=1.0,
|
||||||
|
deviation=0.1,
|
||||||
|
angular_tolerance=0.2,
|
||||||
|
edge_accuracy=5.0,
|
||||||
|
ambient_intensity=1.0,
|
||||||
|
direct_intensity=0.12,
|
||||||
|
):
|
||||||
if name:
|
if name:
|
||||||
cq_objects.update({name : SimpleNamespace(shape=obj,options=options)})
|
cq_objects.update({name: SimpleNamespace(shape=obj, options=options)})
|
||||||
else:
|
else:
|
||||||
cq_objects.update({str(id(obj)) : SimpleNamespace(shape=obj,options=options)})
|
cq_objects.update(
|
||||||
|
{str(id(obj)): SimpleNamespace(shape=obj, options=options)}
|
||||||
|
)
|
||||||
|
|
||||||
def _debug(obj,name=None):
|
def _debug(obj, name=None):
|
||||||
|
_show_object(obj, name, options=dict(color="red", alpha=0.2))
|
||||||
|
|
||||||
_show_object(obj,name,options=dict(color='red',alpha=0.2))
|
module.__dict__["show_object"] = _show_object
|
||||||
|
module.__dict__["debug"] = _debug
|
||||||
|
module.__dict__["rand_color"] = self._rand_color
|
||||||
|
module.__dict__["log"] = lambda x: info(str(x))
|
||||||
|
module.__dict__["cq"] = cq
|
||||||
|
|
||||||
module.__dict__['show_object'] = _show_object
|
return cq_objects, set(module.__dict__) - {"cq"}
|
||||||
module.__dict__['debug'] = _debug
|
|
||||||
module.__dict__['rand_color'] = self._rand_color
|
|
||||||
module.__dict__['log'] = lambda x: info(str(x))
|
|
||||||
module.__dict__['cq'] = cq
|
|
||||||
|
|
||||||
return cq_objects, set(module.__dict__)-{'cq'}
|
def _cleanup_locals(self, module, injected_names):
|
||||||
|
for name in injected_names:
|
||||||
def _cleanup_locals(self,module,injected_names):
|
module.__dict__.pop(name)
|
||||||
|
|
||||||
for name in injected_names: module.__dict__.pop(name)
|
|
||||||
|
|
||||||
@pyqtSlot(bool)
|
@pyqtSlot(bool)
|
||||||
def render(self):
|
def render(self):
|
||||||
seed(371353) #reset the seed every time render is called (preserves colors run to run)
|
seed(
|
||||||
if self.preferences['Reload CQ']:
|
59798267586177
|
||||||
|
) # reset the seed every time render is called (preserves colors run to run)
|
||||||
|
if self.preferences["Reload CQ"]:
|
||||||
reload_cq()
|
reload_cq()
|
||||||
|
|
||||||
cq_script = self.get_current_script()
|
cq_script = self.get_current_script()
|
||||||
cq_code,module = self.compile_code(cq_script)
|
cq_code, module = self.compile_code(cq_script)
|
||||||
|
|
||||||
if cq_code is None: return
|
if cq_code is None:
|
||||||
|
return
|
||||||
|
|
||||||
cq_objects,injected_names = self._inject_locals(module)
|
cq_objects, injected_names = self._inject_locals(module)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self._exec(cq_code, module.__dict__, module.__dict__)
|
self._exec(cq_code, module.__dict__, module.__dict__)
|
||||||
|
|
||||||
#remove the special methods
|
# remove the special methods
|
||||||
self._cleanup_locals(module,injected_names)
|
self._cleanup_locals(module, injected_names)
|
||||||
|
|
||||||
#collect all CQ objects if no explicit show_object was called
|
# collect all CQ objects if no explicit show_object was called
|
||||||
if len(cq_objects) == 0:
|
if len(cq_objects) == 0:
|
||||||
cq_objects = find_cq_objects(module.__dict__)
|
cq_objects = find_cq_objects(module.__dict__)
|
||||||
self.sigRendered.emit(cq_objects)
|
self.sigRendered.emit(cq_objects)
|
||||||
self.sigTraceback.emit(None,
|
self.sigTraceback.emit(None, cq_script)
|
||||||
cq_script)
|
|
||||||
self.sigLocals.emit(module.__dict__)
|
self.sigLocals.emit(module.__dict__)
|
||||||
except Exception:
|
except Exception:
|
||||||
exc_info = sys.exc_info()
|
exc_info = sys.exc_info()
|
||||||
@@ -272,11 +307,10 @@ class Debugger(QObject,ComponentMixin):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def breakpoints(self):
|
def breakpoints(self):
|
||||||
return [ el[0] for el in self.get_breakpoints()]
|
return [el[0] for el in self.get_breakpoints()]
|
||||||
|
|
||||||
@pyqtSlot(bool)
|
@pyqtSlot(bool)
|
||||||
def debug(self,value):
|
def debug(self, value):
|
||||||
|
|
||||||
previous_trace = sys.gettrace()
|
previous_trace = sys.gettrace()
|
||||||
|
|
||||||
if value:
|
if value:
|
||||||
@@ -284,37 +318,35 @@ class Debugger(QObject,ComponentMixin):
|
|||||||
self.state = DbgState.STEP
|
self.state = DbgState.STEP
|
||||||
|
|
||||||
self.script = self.get_current_script()
|
self.script = self.get_current_script()
|
||||||
code,module = self.compile_code(self.script)
|
code, module = self.compile_code(self.script)
|
||||||
|
|
||||||
if code is None:
|
if code is None:
|
||||||
self.sigDebugging.emit(False)
|
self.sigDebugging.emit(False)
|
||||||
self._actions['Run'][1].setChecked(False)
|
self._actions["Run"][1].setChecked(False)
|
||||||
return
|
return
|
||||||
|
|
||||||
cq_objects,injected_names = self._inject_locals(module)
|
cq_objects, injected_names = self._inject_locals(module)
|
||||||
|
|
||||||
#clear possible traceback
|
# clear possible traceback
|
||||||
self.sigTraceback.emit(None,
|
self.sigTraceback.emit(None, self.script)
|
||||||
self.script)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
sys.settrace(self.trace_callback)
|
sys.settrace(self.trace_callback)
|
||||||
exec(code,module.__dict__,module.__dict__)
|
exec(code, module.__dict__, module.__dict__)
|
||||||
except Exception:
|
except Exception:
|
||||||
exc_info = sys.exc_info()
|
exc_info = sys.exc_info()
|
||||||
sys.last_traceback = exc_info[-1]
|
sys.last_traceback = exc_info[-1]
|
||||||
self.sigTraceback.emit(exc_info,
|
self.sigTraceback.emit(exc_info, self.script)
|
||||||
self.script)
|
|
||||||
finally:
|
finally:
|
||||||
sys.settrace(previous_trace)
|
sys.settrace(previous_trace)
|
||||||
self.sigDebugging.emit(False)
|
self.sigDebugging.emit(False)
|
||||||
self._actions['Run'][1].setChecked(False)
|
self._actions["Run"][1].setChecked(False)
|
||||||
|
|
||||||
if len(cq_objects) == 0:
|
if len(cq_objects) == 0:
|
||||||
cq_objects = find_cq_objects(module.__dict__)
|
cq_objects = find_cq_objects(module.__dict__)
|
||||||
self.sigRendered.emit(cq_objects)
|
self.sigRendered.emit(cq_objects)
|
||||||
|
|
||||||
self._cleanup_locals(module,injected_names)
|
self._cleanup_locals(module, injected_names)
|
||||||
self.sigLocals.emit(module.__dict__)
|
self.sigLocals.emit(module.__dict__)
|
||||||
|
|
||||||
self._frames = []
|
self._frames = []
|
||||||
@@ -322,41 +354,37 @@ class Debugger(QObject,ComponentMixin):
|
|||||||
sys.settrace(previous_trace)
|
sys.settrace(previous_trace)
|
||||||
self.inner_event_loop.exit(0)
|
self.inner_event_loop.exit(0)
|
||||||
|
|
||||||
|
def debug_cmd(self, state=DbgState.STEP):
|
||||||
def debug_cmd(self,state=DbgState.STEP):
|
|
||||||
|
|
||||||
self.state = state
|
self.state = state
|
||||||
self.inner_event_loop.exit(0)
|
self.inner_event_loop.exit(0)
|
||||||
|
|
||||||
|
def trace_callback(self, frame, event, arg):
|
||||||
def trace_callback(self,frame,event,arg):
|
|
||||||
|
|
||||||
filename = frame.f_code.co_filename
|
filename = frame.f_code.co_filename
|
||||||
|
|
||||||
if filename==DUMMY_FILE:
|
if filename == DUMMY_FILE:
|
||||||
if not self._frames:
|
if not self._frames:
|
||||||
self._frames.append(frame)
|
self._frames.append(frame)
|
||||||
self.trace_local(frame,event,arg)
|
self.trace_local(frame, event, arg)
|
||||||
return self.trace_callback
|
return self.trace_callback
|
||||||
|
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def trace_local(self,frame,event,arg):
|
def trace_local(self, frame, event, arg):
|
||||||
|
|
||||||
lineno = frame.f_lineno
|
lineno = frame.f_lineno
|
||||||
|
|
||||||
if event in (DbgEevent.LINE,):
|
if event in (DbgEevent.LINE,):
|
||||||
if (self.state in (DbgState.STEP, DbgState.STEP_IN) and frame is self._frames[-1]) \
|
if (
|
||||||
or (lineno in self.breakpoints):
|
self.state in (DbgState.STEP, DbgState.STEP_IN)
|
||||||
|
and frame is self._frames[-1]
|
||||||
|
) or (lineno in self.breakpoints):
|
||||||
if lineno in self.breakpoints:
|
if lineno in self.breakpoints:
|
||||||
self._frames.append(frame)
|
self._frames.append(frame)
|
||||||
|
|
||||||
self.sigLineChanged.emit(lineno)
|
self.sigLineChanged.emit(lineno)
|
||||||
self.sigFrameChanged.emit(frame)
|
self.sigFrameChanged.emit(frame)
|
||||||
self.sigLocalsChanged.emit(frame.f_locals)
|
self.sigLocalsChanged.emit(frame.f_locals)
|
||||||
self.sigCQChanged.emit(find_cq_objects(frame.f_locals),True)
|
self.sigCQChanged.emit(find_cq_objects(frame.f_locals), True)
|
||||||
|
|
||||||
self.inner_event_loop.exec_()
|
self.inner_event_loop.exec_()
|
||||||
|
|
||||||
@@ -375,7 +403,7 @@ class Debugger(QObject,ComponentMixin):
|
|||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
def module_manager():
|
def module_manager():
|
||||||
""" unloads any modules loaded while the context manager is active """
|
"""unloads any modules loaded while the context manager is active"""
|
||||||
loaded_modules = set(sys.modules.keys())
|
loaded_modules = set(sys.modules.keys())
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -2,9 +2,11 @@ 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 PyQt5.QtCore import pyqtSignal, QFileSystemWatcher, QTimer
|
from PySide6.QtCore import Signal as pyqtSignal
|
||||||
from PyQt5.QtWidgets import QAction, QFileDialog
|
from PySide6.QtCore import QFileSystemWatcher, QTimerEvent, QTimer
|
||||||
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
|
||||||
@@ -16,91 +18,104 @@ from ..utils import get_save_filename, get_open_filename, confirm
|
|||||||
|
|
||||||
from ..icons import icon
|
from ..icons import icon
|
||||||
|
|
||||||
class Editor(CodeEditor,ComponentMixin):
|
|
||||||
|
|
||||||
name = 'Code Editor'
|
class Editor(CodeEditor, ComponentMixin):
|
||||||
|
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(name='Preferences',children=[
|
preferences = Parameter.create(
|
||||||
{'name': 'Font size', 'type': 'int', 'value': 12},
|
name="Preferences",
|
||||||
{'name': 'Autoreload', 'type': 'bool', 'value': False},
|
children=[
|
||||||
{'name': 'Autoreload delay', 'type': 'int', 'value': 50},
|
{"name": "Font size", "type": "int", "value": 12},
|
||||||
{'name': 'Autoreload: watch imported modules', 'type': 'bool', 'value': False},
|
{"name": "Autoreload", "type": "bool", "value": False},
|
||||||
{'name': 'Line wrap', 'type': 'bool', 'value': False},
|
{"name": "Autoreload delay", "type": "int", "value": 50},
|
||||||
{'name': 'Color scheme', 'type': 'list',
|
{
|
||||||
'values': ['Spyder','Monokai','Zenburn'], 'value': 'Spyder'}])
|
"name": "Autoreload: watch imported modules",
|
||||||
|
"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(linenumbers=True,
|
self.setup_editor(
|
||||||
|
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' : [QAction(icon('new'),
|
"File": [
|
||||||
'New',
|
QAction(
|
||||||
|
icon("new"), "New", self, shortcut="ctrl+N", triggered=self.new
|
||||||
|
),
|
||||||
|
QAction(
|
||||||
|
icon("open"), "Open", self, shortcut="ctrl+O", triggered=self.open
|
||||||
|
),
|
||||||
|
QAction(
|
||||||
|
icon("save"), "Save", self, shortcut="ctrl+S", triggered=self.save
|
||||||
|
),
|
||||||
|
QAction(
|
||||||
|
icon("save_as"),
|
||||||
|
"Save as",
|
||||||
self,
|
self,
|
||||||
shortcut='ctrl+N',
|
shortcut="ctrl+shift+S",
|
||||||
triggered=self.new),
|
triggered=self.save_as,
|
||||||
QAction(icon('open'),
|
),
|
||||||
'Open',
|
QAction(
|
||||||
|
icon("autoreload"),
|
||||||
|
"Automatic reload and preview",
|
||||||
self,
|
self,
|
||||||
shortcut='ctrl+O',
|
triggered=self.autoreload,
|
||||||
triggered=self.open),
|
|
||||||
QAction(icon('save'),
|
|
||||||
'Save',
|
|
||||||
self,
|
|
||||||
shortcut='ctrl+S',
|
|
||||||
triggered=self.save),
|
|
||||||
QAction(icon('save_as'),
|
|
||||||
'Save as',
|
|
||||||
self,
|
|
||||||
shortcut='ctrl+shift+S',
|
|
||||||
triggered=self.save_as),
|
|
||||||
QAction(icon('autoreload'),
|
|
||||||
'Automatic reload and preview',
|
|
||||||
self,triggered=self.autoreload,
|
|
||||||
checkable=True,
|
checkable=True,
|
||||||
checked=False,
|
checked=False,
|
||||||
objectName='autoreload'),
|
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)
|
||||||
@@ -108,68 +123,66 @@ 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') \
|
self.findChild(QAction, "autoreload").setChecked(self.preferences["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(self,'Please confirm','Current document is not saved - do you want to continue?')
|
rv = confirm(
|
||||||
|
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
|
||||||
|
|
||||||
if not self.confirm_discard(): return
|
self.set_text("")
|
||||||
|
self.filename = ""
|
||||||
self.set_text('')
|
|
||||||
self.filename = ''
|
|
||||||
self.reset_modified()
|
self.reset_modified()
|
||||||
|
|
||||||
def open(self):
|
def open(self):
|
||||||
|
if not self.confirm_discard():
|
||||||
if not self.confirm_discard(): return
|
return
|
||||||
|
|
||||||
curr_dir = Path(self.filename).abspath().dirname()
|
curr_dir = Path(self.filename).abspath().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._filename != '':
|
if self.preferences["Autoreload"]:
|
||||||
|
|
||||||
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)
|
||||||
|
|
||||||
@@ -179,20 +192,25 @@ 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 (self._watched_file != self.filename or not self.preferences['Autoreload']):
|
if self._watched_file and (
|
||||||
|
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 self.preferences['Autoreload'] and self.filename and self.filename != self._watched_file:
|
if (
|
||||||
|
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()
|
||||||
|
|
||||||
@@ -214,7 +232,7 @@ 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)
|
||||||
@@ -228,51 +246,45 @@ 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)
|
||||||
|
|
||||||
if self.filename != '':
|
def restoreComponentState(self, store):
|
||||||
store.setValue(self.name+'/state',self.filename)
|
filename = store.value(self.name + "/state")
|
||||||
|
|
||||||
def restoreComponentState(self,store):
|
if filename and self.filename == "":
|
||||||
|
|
||||||
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)
|
||||||
|
|
||||||
@@ -280,8 +292,7 @@ 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()
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import logbook as logging
|
import logbook as logging
|
||||||
|
|
||||||
from PyQt5.QtWidgets import QPlainTextEdit
|
from PySide6.QtWidgets import QPlainTextEdit
|
||||||
from PyQt5 import QtCore
|
from PySide6 import QtCore
|
||||||
|
|
||||||
from ..mixins import ComponentMixin
|
from ..mixins import ComponentMixin
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,12 @@
|
|||||||
from PyQt5.QtWidgets import QTreeWidget, QTreeWidgetItem, QAction, QMenu, QWidget, QAbstractItemView
|
from PySide6.QtWidgets import (
|
||||||
from PyQt5.QtCore import Qt, pyqtSlot, pyqtSignal
|
QTreeWidget,
|
||||||
|
QTreeWidgetItem,
|
||||||
|
QMenu,
|
||||||
|
QWidget,
|
||||||
|
QAbstractItemView,
|
||||||
|
)
|
||||||
|
from PySide6.QtGui import QAction
|
||||||
|
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
|
||||||
@@ -8,107 +15,119 @@ from OCP.gp import gp_Dir, gp_Pnt, gp_Ax1
|
|||||||
|
|
||||||
from ..mixins import ComponentMixin
|
from ..mixins import ComponentMixin
|
||||||
from ..icons import icon
|
from ..icons import icon
|
||||||
from ..cq_utils import make_AIS, export, to_occ_color, is_obj_empty, get_occ_color, set_color
|
from ..cq_utils import (
|
||||||
|
make_AIS,
|
||||||
|
export,
|
||||||
|
to_occ_color,
|
||||||
|
is_obj_empty,
|
||||||
|
get_occ_color,
|
||||||
|
set_color,
|
||||||
|
)
|
||||||
from .viewer import DEFAULT_FACE_COLOR
|
from .viewer import DEFAULT_FACE_COLOR
|
||||||
from ..utils import splitter, layout, get_save_filename
|
from ..utils import splitter, layout, get_save_filename
|
||||||
|
|
||||||
|
|
||||||
class TopTreeItem(QTreeWidgetItem):
|
class TopTreeItem(QTreeWidgetItem):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(TopTreeItem, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
def __init__(self,*args,**kwargs):
|
|
||||||
|
|
||||||
super(TopTreeItem,self).__init__(*args,**kwargs)
|
|
||||||
|
|
||||||
class ObjectTreeItem(QTreeWidgetItem):
|
class ObjectTreeItem(QTreeWidgetItem):
|
||||||
|
props = [
|
||||||
|
{"name": "Name", "type": "str", "value": ""},
|
||||||
|
{"name": "Color", "type": "color", "value": "#f4a824"},
|
||||||
|
{"name": "Alpha", "type": "float", "value": 0, "limits": (0, 1), "step": 1e-1},
|
||||||
|
{"name": "Visible", "type": "bool", "value": True},
|
||||||
|
]
|
||||||
|
|
||||||
props = [{'name': 'Name', 'type': 'str', 'value': ''},
|
def __init__(
|
||||||
{'name': 'Color', 'type': 'color', 'value': "#f4a824"},
|
self,
|
||||||
{'name': 'Alpha', 'type': 'float', 'value': 0, 'limits': (0,1), 'step': 1e-1},
|
|
||||||
{'name': 'Visible', 'type': 'bool','value': True}]
|
|
||||||
|
|
||||||
def __init__(self,
|
|
||||||
name,
|
name,
|
||||||
ais=None,
|
ais=None,
|
||||||
shape=None,
|
shape=None,
|
||||||
shape_display=None,
|
shape_display=None,
|
||||||
sig=None,
|
sig=None,
|
||||||
alpha=0.,
|
alpha=0.0,
|
||||||
color='#f4a824',
|
color="#f4a824",
|
||||||
**kwargs):
|
**kwargs
|
||||||
|
):
|
||||||
super(ObjectTreeItem,self).__init__([name],**kwargs)
|
super(ObjectTreeItem, self).__init__([name], **kwargs)
|
||||||
self.setFlags( self.flags() | Qt.ItemIsUserCheckable)
|
self.setFlags(self.flags() | Qt.ItemIsUserCheckable)
|
||||||
self.setCheckState(0,Qt.Checked)
|
self.setCheckState(0, Qt.Checked)
|
||||||
|
|
||||||
self.ais = ais
|
self.ais = ais
|
||||||
self.shape = shape
|
self.shape = shape
|
||||||
self.shape_display = shape_display
|
self.shape_display = shape_display
|
||||||
self.sig = sig
|
self.sig = sig
|
||||||
|
|
||||||
self.properties = Parameter.create(name='Properties',
|
self.properties = Parameter.create(name="Properties", children=self.props)
|
||||||
children=self.props)
|
|
||||||
|
|
||||||
self.properties['Name'] = name
|
self.properties["Name"] = name
|
||||||
self.properties['Alpha'] = ais.Transparency()
|
self.properties["Alpha"] = ais.Transparency()
|
||||||
self.properties['Color'] = get_occ_color(ais) if ais and ais.HasColor() else get_occ_color(DEFAULT_FACE_COLOR)
|
self.properties["Color"] = (
|
||||||
|
get_occ_color(ais)
|
||||||
|
if ais and ais.HasColor()
|
||||||
|
else get_occ_color(DEFAULT_FACE_COLOR)
|
||||||
|
)
|
||||||
self.properties.sigTreeStateChanged.connect(self.propertiesChanged)
|
self.properties.sigTreeStateChanged.connect(self.propertiesChanged)
|
||||||
|
|
||||||
def propertiesChanged(self, properties, changed):
|
def propertiesChanged(self, properties, changed):
|
||||||
|
|
||||||
changed_prop = changed[0][0]
|
changed_prop = changed[0][0]
|
||||||
|
|
||||||
self.setData(0,0,self.properties['Name'])
|
self.setData(0, 0, self.properties["Name"])
|
||||||
self.ais.SetTransparency(self.properties['Alpha'])
|
self.ais.SetTransparency(self.properties["Alpha"])
|
||||||
|
|
||||||
if changed_prop.name() == 'Color':
|
if changed_prop.name() == "Color":
|
||||||
set_color(self.ais, to_occ_color(self.properties['Color']))
|
set_color(self.ais, to_occ_color(self.properties["Color"]))
|
||||||
|
|
||||||
self.ais.Redisplay()
|
self.ais.Redisplay()
|
||||||
|
|
||||||
if self.properties['Visible']:
|
if self.properties["Visible"]:
|
||||||
self.setCheckState(0,Qt.Checked)
|
self.setCheckState(0, Qt.Checked)
|
||||||
else:
|
else:
|
||||||
self.setCheckState(0,Qt.Unchecked)
|
self.setCheckState(0, Qt.Unchecked)
|
||||||
|
|
||||||
if self.sig:
|
if self.sig:
|
||||||
self.sig.emit()
|
self.sig.emit()
|
||||||
|
|
||||||
|
|
||||||
class CQRootItem(TopTreeItem):
|
class CQRootItem(TopTreeItem):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
def __init__(self,*args,**kwargs):
|
super(CQRootItem, self).__init__(["CQ models"], *args, **kwargs)
|
||||||
|
|
||||||
super(CQRootItem,self).__init__(['CQ models'],*args,**kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
class HelpersRootItem(TopTreeItem):
|
class HelpersRootItem(TopTreeItem):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
def __init__(self,*args,**kwargs):
|
super(HelpersRootItem, self).__init__(["Helpers"], *args, **kwargs)
|
||||||
|
|
||||||
super(HelpersRootItem,self).__init__(['Helpers'],*args,**kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
class ObjectTree(QWidget,ComponentMixin):
|
class ObjectTree(QWidget, ComponentMixin):
|
||||||
|
name = "Object Tree"
|
||||||
name = 'Object Tree'
|
|
||||||
_stash = []
|
_stash = []
|
||||||
|
|
||||||
preferences = Parameter.create(name='Preferences',children=[
|
preferences = Parameter.create(
|
||||||
{'name': 'Preserve properties on reload', 'type': 'bool', 'value': False},
|
name="Preferences",
|
||||||
{'name': 'Clear all before each run', 'type': 'bool', 'value': True},
|
children=[
|
||||||
{'name': 'STL precision','type': 'float', 'value': .1}])
|
{"name": "Preserve properties on reload", "type": "bool", "value": False},
|
||||||
|
{"name": "Clear all before each run", "type": "bool", "value": True},
|
||||||
|
{"name": "STL precision", "type": "float", "value": 0.1},
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
sigObjectsAdded = pyqtSignal([list],[list,bool])
|
sigObjectsAdded = pyqtSignal(list)
|
||||||
|
sigObjectsAdded2 = pyqtSignal(list, bool)
|
||||||
sigObjectsRemoved = pyqtSignal(list)
|
sigObjectsRemoved = pyqtSignal(list)
|
||||||
sigCQObjectSelected = pyqtSignal(object)
|
sigCQObjectSelected = pyqtSignal(object)
|
||||||
sigAISObjectsSelected = pyqtSignal(list)
|
sigAISObjectsSelected = pyqtSignal(list)
|
||||||
sigItemChanged = pyqtSignal(QTreeWidgetItem,int)
|
sigItemChanged = pyqtSignal(QTreeWidgetItem, int)
|
||||||
sigObjectPropertiesChanged = pyqtSignal()
|
sigObjectPropertiesChanged = pyqtSignal()
|
||||||
|
|
||||||
def __init__(self,parent):
|
def __init__(self, parent):
|
||||||
|
super(ObjectTree, self).__init__(parent)
|
||||||
|
|
||||||
super(ObjectTree,self).__init__(parent)
|
self.tree = tree = QTreeWidget(
|
||||||
|
self, selectionMode=QAbstractItemView.ExtendedSelection
|
||||||
self.tree = tree = QTreeWidget(self,
|
)
|
||||||
selectionMode=QAbstractItemView.ExtendedSelection)
|
|
||||||
self.properties_editor = ParameterTree(self)
|
self.properties_editor = ParameterTree(self)
|
||||||
|
|
||||||
tree.setHeaderHidden(True)
|
tree.setHeaderHidden(True)
|
||||||
@@ -116,10 +135,9 @@ class ObjectTree(QWidget,ComponentMixin):
|
|||||||
tree.setRootIsDecorated(False)
|
tree.setRootIsDecorated(False)
|
||||||
tree.setContextMenuPolicy(Qt.ActionsContextMenu)
|
tree.setContextMenuPolicy(Qt.ActionsContextMenu)
|
||||||
|
|
||||||
#forward itemChanged singal
|
# forward itemChanged singal
|
||||||
tree.itemChanged.connect(\
|
tree.itemChanged.connect(lambda item, col: self.sigItemChanged.emit(item, col))
|
||||||
lambda item,col: self.sigItemChanged.emit(item,col))
|
# handle visibility changes form tree
|
||||||
#handle visibility changes form tree
|
|
||||||
tree.itemChanged.connect(self.handleChecked)
|
tree.itemChanged.connect(self.handleChecked)
|
||||||
|
|
||||||
self.CQ = CQRootItem()
|
self.CQ = CQRootItem()
|
||||||
@@ -131,30 +149,31 @@ class ObjectTree(QWidget,ComponentMixin):
|
|||||||
|
|
||||||
tree.expandToDepth(1)
|
tree.expandToDepth(1)
|
||||||
|
|
||||||
self._export_STL_action = \
|
self._export_STL_action = QAction(
|
||||||
QAction('Export as STL',
|
"Export as STL",
|
||||||
self,
|
self,
|
||||||
enabled=False,
|
enabled=False,
|
||||||
triggered=lambda: \
|
triggered=lambda: self.export("stl", self.preferences["STL precision"]),
|
||||||
self.export('stl',
|
)
|
||||||
self.preferences['STL precision']))
|
|
||||||
|
|
||||||
self._export_STEP_action = \
|
self._export_STEP_action = QAction(
|
||||||
QAction('Export as STEP',
|
"Export as STEP", self, enabled=False, triggered=lambda: self.export("step")
|
||||||
|
)
|
||||||
|
|
||||||
|
self._clear_current_action = QAction(
|
||||||
|
icon("delete"),
|
||||||
|
"Clear current",
|
||||||
self,
|
self,
|
||||||
enabled=False,
|
enabled=False,
|
||||||
triggered=lambda: \
|
triggered=self.removeSelected,
|
||||||
self.export('step'))
|
)
|
||||||
|
|
||||||
self._clear_current_action = QAction(icon('delete'),
|
self._toolbar_actions = [
|
||||||
'Clear current',
|
QAction(
|
||||||
self,
|
icon("delete-many"), "Clear all", self, triggered=self.removeObjects
|
||||||
enabled=False,
|
),
|
||||||
triggered=self.removeSelected)
|
self._clear_current_action,
|
||||||
|
]
|
||||||
self._toolbar_actions = \
|
|
||||||
[QAction(icon('delete-many'),'Clear all',self,triggered=self.removeObjects),
|
|
||||||
self._clear_current_action,]
|
|
||||||
|
|
||||||
self.prepareMenu()
|
self.prepareMenu()
|
||||||
|
|
||||||
@@ -163,135 +182,157 @@ class ObjectTree(QWidget,ComponentMixin):
|
|||||||
|
|
||||||
self.prepareLayout()
|
self.prepareLayout()
|
||||||
|
|
||||||
|
|
||||||
def prepareMenu(self):
|
def prepareMenu(self):
|
||||||
|
|
||||||
self.tree.setContextMenuPolicy(Qt.CustomContextMenu)
|
self.tree.setContextMenuPolicy(Qt.CustomContextMenu)
|
||||||
|
|
||||||
self._context_menu = QMenu(self)
|
self._context_menu = QMenu(self)
|
||||||
self._context_menu.addActions(self._toolbar_actions)
|
self._context_menu.addActions(self._toolbar_actions)
|
||||||
self._context_menu.addActions((self._export_STL_action,
|
self._context_menu.addActions(
|
||||||
self._export_STEP_action))
|
(self._export_STL_action, self._export_STEP_action)
|
||||||
|
)
|
||||||
|
|
||||||
def prepareLayout(self):
|
def prepareLayout(self):
|
||||||
|
self._splitter = splitter(
|
||||||
self._splitter = splitter((self.tree,self.properties_editor),
|
(self.tree, self.properties_editor),
|
||||||
stretch_factors = (2,1),
|
stretch_factors=(2, 1),
|
||||||
orientation=Qt.Vertical)
|
orientation=Qt.Vertical,
|
||||||
layout(self,(self._splitter,),top_widget=self)
|
)
|
||||||
|
layout(self, (self._splitter,), top_widget=self)
|
||||||
|
|
||||||
self._splitter.show()
|
self._splitter.show()
|
||||||
|
|
||||||
def showMenu(self,position):
|
def showMenu(self, position):
|
||||||
|
|
||||||
self._context_menu.exec_(self.tree.viewport().mapToGlobal(position))
|
self._context_menu.exec_(self.tree.viewport().mapToGlobal(position))
|
||||||
|
|
||||||
|
|
||||||
def menuActions(self):
|
def menuActions(self):
|
||||||
|
return {"Tools": [self._export_STL_action, self._export_STEP_action]}
|
||||||
return {'Tools' : [self._export_STL_action,
|
|
||||||
self._export_STEP_action]}
|
|
||||||
|
|
||||||
def toolbarActions(self):
|
def toolbarActions(self):
|
||||||
|
|
||||||
return self._toolbar_actions
|
return self._toolbar_actions
|
||||||
|
|
||||||
def addLines(self):
|
def addLines(self):
|
||||||
|
origin = (0, 0, 0)
|
||||||
origin = (0,0,0)
|
|
||||||
ais_list = []
|
ais_list = []
|
||||||
|
|
||||||
for name,color,direction in zip(('X','Y','Z'),
|
for name, color, direction in zip(
|
||||||
('red','lawngreen','blue'),
|
("X", "Y", "Z"),
|
||||||
((1,0,0),(0,1,0),(0,0,1))):
|
((0.2, 0, 0), "lawngreen", "blue"),
|
||||||
line_placement = Geom_Line(gp_Ax1(gp_Pnt(*origin),
|
((1, 0, 0), (0, 1, 0), (0, 0, 1)),
|
||||||
gp_Dir(*direction)))
|
):
|
||||||
|
line_placement = Geom_Line(gp_Ax1(gp_Pnt(*origin), gp_Dir(*direction)))
|
||||||
line = AIS_Line(line_placement)
|
line = AIS_Line(line_placement)
|
||||||
line.SetColor(to_occ_color(color))
|
line.SetColor(to_occ_color(color))
|
||||||
|
|
||||||
self.Helpers.addChild(ObjectTreeItem(name,
|
self.Helpers.addChild(ObjectTreeItem(name, ais=line))
|
||||||
ais=line))
|
|
||||||
|
|
||||||
ais_list.append(line)
|
ais_list.append(line)
|
||||||
|
|
||||||
self.sigObjectsAdded.emit(ais_list)
|
self.sigObjectsAdded.emit(ais_list)
|
||||||
|
|
||||||
def _current_properties(self):
|
def _current_properties(self):
|
||||||
|
|
||||||
current_params = {}
|
current_params = {}
|
||||||
for i in range(self.CQ.childCount()):
|
for i in range(self.CQ.childCount()):
|
||||||
child = self.CQ.child(i)
|
child = self.CQ.child(i)
|
||||||
current_params[child.properties['Name']] = child.properties
|
current_params[child.properties["Name"]] = child.properties
|
||||||
|
|
||||||
return current_params
|
return current_params
|
||||||
|
|
||||||
def _restore_properties(self,obj,properties):
|
def _restore_properties(self, obj, properties):
|
||||||
|
for p in properties[obj.properties["Name"]]:
|
||||||
for p in properties[obj.properties['Name']]:
|
|
||||||
obj.properties[p.name()] = p.value()
|
obj.properties[p.name()] = p.value()
|
||||||
|
|
||||||
@pyqtSlot(dict,bool)
|
@pyqtSlot(dict, bool)
|
||||||
@pyqtSlot(dict)
|
@pyqtSlot(dict)
|
||||||
def addObjects(self,objects,clean=False,root=None):
|
def addObjects(self, objects, clean=False, root=None):
|
||||||
|
|
||||||
if root is None:
|
if root is None:
|
||||||
root = self.CQ
|
root = self.CQ
|
||||||
|
|
||||||
request_fit_view = True if root.childCount() == 0 else False
|
request_fit_view = True if root.childCount() == 0 else False
|
||||||
preserve_props = self.preferences['Preserve properties on reload']
|
preserve_props = self.preferences["Preserve properties on reload"]
|
||||||
|
|
||||||
if preserve_props:
|
if preserve_props:
|
||||||
current_props = self._current_properties()
|
current_props = self._current_properties()
|
||||||
|
|
||||||
if clean or self.preferences['Clear all before each run']:
|
if clean or self.preferences["Clear all before each run"]:
|
||||||
self.removeObjects()
|
self.removeObjects()
|
||||||
|
|
||||||
ais_list = []
|
ais_list = []
|
||||||
|
|
||||||
#remove empty objects
|
# remove empty objects
|
||||||
objects_f = {k:v for k,v in objects.items() if not is_obj_empty(v.shape)}
|
objects_f = {k: v for k, v in objects.items() if not is_obj_empty(v.shape)}
|
||||||
|
|
||||||
for name,obj in objects_f.items():
|
for name, obj in objects_f.items():
|
||||||
ais,shape_display = make_AIS(obj.shape,obj.options)
|
ais, shape_display = make_AIS(obj.shape, obj.options)
|
||||||
|
|
||||||
child = ObjectTreeItem(name,
|
child = ObjectTreeItem(
|
||||||
|
name,
|
||||||
shape=obj.shape,
|
shape=obj.shape,
|
||||||
shape_display=shape_display,
|
shape_display=shape_display,
|
||||||
ais=ais,
|
ais=ais,
|
||||||
sig=self.sigObjectPropertiesChanged)
|
sig=self.sigObjectPropertiesChanged,
|
||||||
|
)
|
||||||
|
|
||||||
if preserve_props and name in current_props:
|
if preserve_props and name in current_props:
|
||||||
self._restore_properties(child,current_props)
|
self._restore_properties(child, current_props)
|
||||||
|
|
||||||
if child.properties['Visible']:
|
if child.properties["Visible"]:
|
||||||
ais_list.append(ais)
|
ais_list.append(ais)
|
||||||
|
|
||||||
root.addChild(child)
|
root.addChild(child)
|
||||||
|
|
||||||
if request_fit_view:
|
if request_fit_view:
|
||||||
self.sigObjectsAdded[list,bool].emit(ais_list,True)
|
self.sigObjectsAdded[list, bool].emit(ais_list, True)
|
||||||
else:
|
else:
|
||||||
self.sigObjectsAdded[list].emit(ais_list)
|
self.sigObjectsAdded[list].emit(ais_list)
|
||||||
|
|
||||||
@pyqtSlot(object,str,object)
|
@pyqtSlot(object, str, object)
|
||||||
def addObject(self,obj,name='',options={}):
|
def addObject(
|
||||||
|
self,
|
||||||
|
obj,
|
||||||
|
name="",
|
||||||
|
options={}, # all following inputs are ignored by cq-editor
|
||||||
|
parent=1,
|
||||||
|
clear=True,
|
||||||
|
port=3939,
|
||||||
|
axes=False,
|
||||||
|
axes0=False,
|
||||||
|
grid=False,
|
||||||
|
ticks=10,
|
||||||
|
ortho=True,
|
||||||
|
transparent=False,
|
||||||
|
default_color=(232, 176, 36),
|
||||||
|
reset_camera=True,
|
||||||
|
zoom=1.0,
|
||||||
|
default_edgecolor=(128, 128, 128),
|
||||||
|
render_edges=True,
|
||||||
|
render_normals=False,
|
||||||
|
render_mates=False,
|
||||||
|
mate_scale=1.0,
|
||||||
|
deviation=0.1,
|
||||||
|
angular_tolerance=0.2,
|
||||||
|
edge_accuracy=5.0,
|
||||||
|
ambient_intensity=1.0,
|
||||||
|
direct_intensity=0.12,
|
||||||
|
):
|
||||||
root = self.CQ
|
root = self.CQ
|
||||||
|
|
||||||
ais,shape_display = make_AIS(obj, options)
|
ais, shape_display = make_AIS(obj, options)
|
||||||
|
|
||||||
root.addChild(ObjectTreeItem(name,
|
root.addChild(
|
||||||
|
ObjectTreeItem(
|
||||||
|
name,
|
||||||
shape=obj,
|
shape=obj,
|
||||||
shape_display=shape_display,
|
shape_display=shape_display,
|
||||||
ais=ais,
|
ais=ais,
|
||||||
sig=self.sigObjectPropertiesChanged))
|
sig=self.sigObjectPropertiesChanged,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
self.sigObjectsAdded.emit([ais])
|
self.sigObjectsAdded.emit([ais])
|
||||||
|
|
||||||
@pyqtSlot(list)
|
@pyqtSlot(list)
|
||||||
@pyqtSlot()
|
@pyqtSlot()
|
||||||
def removeObjects(self,objects=None):
|
def removeObjects(self, objects=None):
|
||||||
|
|
||||||
if objects:
|
if objects:
|
||||||
removed_items_ais = [self.CQ.takeChild(i).ais for i in objects]
|
removed_items_ais = [self.CQ.takeChild(i).ais for i in objects]
|
||||||
else:
|
else:
|
||||||
@@ -300,8 +341,7 @@ class ObjectTree(QWidget,ComponentMixin):
|
|||||||
self.sigObjectsRemoved.emit(removed_items_ais)
|
self.sigObjectsRemoved.emit(removed_items_ais)
|
||||||
|
|
||||||
@pyqtSlot(bool)
|
@pyqtSlot(bool)
|
||||||
def stashObjects(self,action : bool):
|
def stashObjects(self, action: bool):
|
||||||
|
|
||||||
if action:
|
if action:
|
||||||
self._stash = self.CQ.takeChildren()
|
self._stash = self.CQ.takeChildren()
|
||||||
removed_items_ais = [ch.ais for ch in self._stash]
|
removed_items_ais = [ch.ais for ch in self._stash]
|
||||||
@@ -314,14 +354,12 @@ class ObjectTree(QWidget,ComponentMixin):
|
|||||||
|
|
||||||
@pyqtSlot()
|
@pyqtSlot()
|
||||||
def removeSelected(self):
|
def removeSelected(self):
|
||||||
|
|
||||||
ixs = self.tree.selectedIndexes()
|
ixs = self.tree.selectedIndexes()
|
||||||
rows = [ix.row() for ix in ixs]
|
rows = [ix.row() for ix in ixs]
|
||||||
|
|
||||||
self.removeObjects(rows)
|
self.removeObjects(rows)
|
||||||
|
|
||||||
def export(self,export_type,precision=None):
|
def export(self, export_type, precision=None):
|
||||||
|
|
||||||
items = self.tree.selectedItems()
|
items = self.tree.selectedItems()
|
||||||
|
|
||||||
# if CQ models is selected get all children
|
# if CQ models is selected get all children
|
||||||
@@ -333,13 +371,12 @@ class ObjectTree(QWidget,ComponentMixin):
|
|||||||
shapes = [item.shape for item in items if item.parent() is self.CQ]
|
shapes = [item.shape for item in items if item.parent() is self.CQ]
|
||||||
|
|
||||||
fname = get_save_filename(export_type)
|
fname = get_save_filename(export_type)
|
||||||
if fname != '':
|
if fname != "":
|
||||||
export(shapes,export_type,fname,precision)
|
export(shapes, export_type, fname, precision)
|
||||||
|
|
||||||
@pyqtSlot()
|
@pyqtSlot()
|
||||||
def handleSelection(self):
|
def handleSelection(self):
|
||||||
|
items = self.tree.selectedItems()
|
||||||
items =self.tree.selectedItems()
|
|
||||||
if len(items) == 0:
|
if len(items) == 0:
|
||||||
self._export_STL_action.setEnabled(False)
|
self._export_STL_action.setEnabled(False)
|
||||||
self._export_STEP_action.setEnabled(False)
|
self._export_STEP_action.setEnabled(False)
|
||||||
@@ -356,10 +393,9 @@ class ObjectTree(QWidget,ComponentMixin):
|
|||||||
self._export_STEP_action.setEnabled(True)
|
self._export_STEP_action.setEnabled(True)
|
||||||
self._clear_current_action.setEnabled(True)
|
self._clear_current_action.setEnabled(True)
|
||||||
self.sigCQObjectSelected.emit(item.shape)
|
self.sigCQObjectSelected.emit(item.shape)
|
||||||
self.properties_editor.setParameters(item.properties,
|
self.properties_editor.setParameters(item.properties, showTop=False)
|
||||||
showTop=False)
|
|
||||||
self.properties_editor.setEnabled(True)
|
self.properties_editor.setEnabled(True)
|
||||||
elif item is self.CQ and item.childCount()>0:
|
elif item is self.CQ and item.childCount() > 0:
|
||||||
self._export_STL_action.setEnabled(True)
|
self._export_STL_action.setEnabled(True)
|
||||||
self._export_STEP_action.setEnabled(True)
|
self._export_STEP_action.setEnabled(True)
|
||||||
else:
|
else:
|
||||||
@@ -370,8 +406,7 @@ class ObjectTree(QWidget,ComponentMixin):
|
|||||||
self.properties_editor.clear()
|
self.properties_editor.clear()
|
||||||
|
|
||||||
@pyqtSlot(list)
|
@pyqtSlot(list)
|
||||||
def handleGraphicalSelection(self,shapes):
|
def handleGraphicalSelection(self, shapes):
|
||||||
|
|
||||||
self.tree.clearSelection()
|
self.tree.clearSelection()
|
||||||
|
|
||||||
CQ = self.CQ
|
CQ = self.CQ
|
||||||
@@ -381,11 +416,10 @@ class ObjectTree(QWidget,ComponentMixin):
|
|||||||
if item.ais.Shape().IsEqual(shape):
|
if item.ais.Shape().IsEqual(shape):
|
||||||
item.setSelected(True)
|
item.setSelected(True)
|
||||||
|
|
||||||
@pyqtSlot(QTreeWidgetItem,int)
|
@pyqtSlot(QTreeWidgetItem, int)
|
||||||
def handleChecked(self,item,col):
|
def handleChecked(self, item, col):
|
||||||
|
|
||||||
if type(item) is ObjectTreeItem:
|
if type(item) is ObjectTreeItem:
|
||||||
if item.checkState(0):
|
if item.checkState(0):
|
||||||
item.properties['Visible'] = True
|
item.properties["Visible"] = True
|
||||||
else:
|
else:
|
||||||
item.properties['Visible'] = False
|
item.properties["Visible"] = False
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
from sys import platform
|
from sys import platform
|
||||||
|
|
||||||
|
|
||||||
from PyQt5.QtWidgets import QWidget, QApplication
|
from PySide6.QtWidgets import QWidget, QApplication
|
||||||
from PyQt5.QtCore import pyqtSlot, pyqtSignal, Qt, QEvent
|
from PySide6.QtCore import Slot as pyqtSlot, Signal as pyqtSignal, Qt, QEvent
|
||||||
|
|
||||||
import OCP
|
import OCP
|
||||||
|
|
||||||
@@ -10,19 +10,17 @@ from OCP.Aspect import Aspect_DisplayConnection, Aspect_TypeOfTriedronPosition
|
|||||||
from OCP.OpenGl import OpenGl_GraphicDriver
|
from OCP.OpenGl import OpenGl_GraphicDriver
|
||||||
from OCP.V3d import V3d_Viewer
|
from OCP.V3d import V3d_Viewer
|
||||||
from OCP.AIS import AIS_InteractiveContext, AIS_DisplayMode
|
from OCP.AIS import AIS_InteractiveContext, AIS_DisplayMode
|
||||||
from OCP.Quantity import Quantity_Color
|
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)
|
||||||
@@ -31,7 +29,7 @@ class OCCTWidget(QWidget):
|
|||||||
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)
|
||||||
|
|
||||||
@@ -39,11 +37,10 @@ class OCCTWidget(QWidget):
|
|||||||
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()
|
||||||
@@ -51,8 +48,10 @@ class OCCTWidget(QWidget):
|
|||||||
params.IsAntialiasingEnabled = True
|
params.IsAntialiasingEnabled = True
|
||||||
|
|
||||||
view.TriedronDisplay(
|
view.TriedronDisplay(
|
||||||
Aspect_TypeOfTriedronPosition.Aspect_TOTP_RIGHT_LOWER,
|
Aspect_TypeOfTriedronPosition.Aspect_TOTP_RIGHT_LOWER, Quantity_Color(), 0.1
|
||||||
Quantity_Color(), 0.1)
|
)
|
||||||
|
|
||||||
|
view.ZBufferTriedronSetup(Quantity_Color(*(0.2, 0.0, 0.0), TOC_RGB))
|
||||||
|
|
||||||
viewer = self.viewer
|
viewer = self.viewer
|
||||||
|
|
||||||
@@ -65,14 +64,12 @@ class OCCTWidget(QWidget):
|
|||||||
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:
|
||||||
@@ -82,36 +79,31 @@ class OCCTWidget(QWidget):
|
|||||||
|
|
||||||
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.view.Pan(x - self.old_pos.x(), self.old_pos.y() - y, theToStart=True)
|
||||||
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.view.Pan(x - self.old_pos.x(), self.old_pos.y() - y, theToStart=True)
|
||||||
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()
|
||||||
|
|
||||||
@@ -122,52 +114,45 @@ class OCCTWidget(QWidget):
|
|||||||
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())
|
||||||
|
|||||||
@@ -1,97 +1,89 @@
|
|||||||
from traceback import extract_tb, format_exception_only
|
from traceback import extract_tb, format_exception_only
|
||||||
|
|
||||||
from PyQt5.QtWidgets import (QWidget, QTreeWidget, QTreeWidgetItem, QAction,
|
from PySide6.QtWidgets import QWidget, QTreeWidget, QTreeWidgetItem, QLabel
|
||||||
QLabel)
|
from PySide6.QtGui import QAction
|
||||||
from PyQt5.QtCore import Qt, pyqtSlot, pyqtSignal
|
from PySide6.QtCore import Qt, Slot as pyqtSlot, Signal as 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"
|
||||||
|
|
||||||
name = 'Traceback Viewer'
|
def __init__(self, parent):
|
||||||
|
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(\
|
self.current_exception.setStyleSheet("QLabel {color : red; }")
|
||||||
"QLabel {color : red; }");
|
|
||||||
|
|
||||||
layout(self,
|
layout(self, (self.current_exception, self.tree), 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 = [t for t in extract_tb(tb) if '<string>' in t.filename] #ignore highest frames (debug, exec)
|
tb = [
|
||||||
|
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,
|
root.addChild(QTreeWidgetItem([el.filename, str(el.lineno), line]))
|
||||||
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('<', '<').replace('>', '>') #replace <>
|
exc_msg = exc_msg.replace("<", "<").replace(">", ">") # replace <>
|
||||||
|
|
||||||
self.current_exception.\
|
self.current_exception.setText("<b>{}</b>: {}".format(exc_name, exc_msg))
|
||||||
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(QTreeWidgetItem(
|
root.addChild(
|
||||||
[exc.filename,
|
QTreeWidgetItem(
|
||||||
|
[
|
||||||
|
exc.filename,
|
||||||
str(exc.lineno),
|
str(exc.lineno),
|
||||||
exc.text.strip() if exc.text else '']
|
exc.text.strip() if exc.text else "",
|
||||||
))
|
]
|
||||||
|
)
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
self.current_exception.setText('')
|
self.current_exception.setText("")
|
||||||
|
|
||||||
@pyqtSlot(QTreeWidgetItem,QTreeWidgetItem)
|
|
||||||
def handleSelection(self,item,*args):
|
|
||||||
|
|
||||||
|
@pyqtSlot(QTreeWidgetItem, QTreeWidgetItem)
|
||||||
|
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)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,21 @@
|
|||||||
from PyQt5.QtWidgets import QWidget, QDialog, QTreeWidgetItem, QApplication, QAction
|
from PySide6.QtWidgets import QWidget, QDialog, QTreeWidgetItem, QApplication
|
||||||
|
|
||||||
from PyQt5.QtCore import pyqtSlot, pyqtSignal
|
from PySide6.QtCore import Slot as pyqtSlot, Signal as pyqtSignal
|
||||||
from PyQt5.QtGui import QIcon
|
from PySide6.QtGui import QIcon, QAction
|
||||||
|
|
||||||
from OCP.Graphic3d import Graphic3d_Camera, Graphic3d_StereoMode, Graphic3d_NOM_JADE,\
|
from OCP.Graphic3d import (
|
||||||
Graphic3d_MaterialAspect
|
Graphic3d_Camera,
|
||||||
from OCP.AIS import AIS_Shaded,AIS_WireFrame, AIS_ColoredShape, AIS_Axis
|
Graphic3d_StereoMode,
|
||||||
|
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 Quantity_NOC_BLACK as BLACK, Quantity_TOC_RGB as TOC_RGB,\
|
from OCP.Quantity import (
|
||||||
Quantity_Color
|
Quantity_NOC_BLACK as BLACK,
|
||||||
|
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
|
||||||
|
|
||||||
@@ -25,30 +32,66 @@ 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):
|
|
||||||
|
|
||||||
name = '3D Viewer'
|
class OCCViewer(QWidget, ComponentMixin):
|
||||||
|
name = "3D Viewer"
|
||||||
|
|
||||||
preferences = Parameter.create(name='Pref',children=[
|
preferences = Parameter.create(
|
||||||
{'name': 'Fit automatically', 'type': 'bool', 'value': True},
|
name="Pref",
|
||||||
{'name': 'Use gradient', 'type': 'bool', 'value': False},
|
children=[
|
||||||
{'name': 'Background color', 'type': 'color', 'value': (95,95,95)},
|
{"name": "Fit automatically", "type": "bool", "value": True},
|
||||||
{'name': 'Background color (aux)', 'type': 'color', 'value': (30,30,30)},
|
{"name": "Use gradient", "type": "bool", "value": False},
|
||||||
{'name': 'Default object color', 'type': 'color', 'value': "#FF0"},
|
{"name": "Background color", "type": "color", "value": (95, 95, 95)},
|
||||||
{'name': 'Deviation', 'type': 'float', 'value': 1e-5, 'dec': True, 'step': 1},
|
{"name": "Background color (aux)", "type": "color", "value": (30, 30, 30)},
|
||||||
{'name': 'Angular deviation', 'type': 'float', 'value': 0.1, 'dec': True, 'step': 1},
|
{"name": "Default object color", "type": "color", "value": "#FF0"},
|
||||||
{'name': 'Projection Type', 'type': 'list', 'value': 'Orthographic',
|
{
|
||||||
'values': ['Orthographic', 'Perspective', 'Stereo', 'MonoLeftEye', 'MonoRightEye']},
|
"name": "Deviation",
|
||||||
{'name': 'Stereo Mode', 'type': 'list', 'value': 'QuadBuffer',
|
"type": "float",
|
||||||
'values': ['QuadBuffer', 'Anaglyph', 'RowInterlaced', 'ColumnInterlaced',
|
"value": 1e-5,
|
||||||
'ChessBoard', 'SideBySide', 'OverUnder']}])
|
"dec": True,
|
||||||
IMAGE_EXTENSIONS = 'png'
|
"step": 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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()
|
||||||
@@ -56,15 +99,18 @@ class OCCViewer(QWidget,ComponentMixin):
|
|||||||
|
|
||||||
self.create_actions(self)
|
self.create_actions(self)
|
||||||
|
|
||||||
self.layout_ = layout(self,
|
self.layout_ = layout(
|
||||||
[self.canvas,],
|
self,
|
||||||
|
[
|
||||||
|
self.canvas,
|
||||||
|
],
|
||||||
top_widget=self,
|
top_widget=self,
|
||||||
margin=0)
|
margin=0,
|
||||||
self.setup_default_drawer() #misspelled in original
|
)
|
||||||
|
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)
|
||||||
|
|
||||||
@@ -77,98 +123,128 @@ class OCCViewer(QWidget,ComponentMixin):
|
|||||||
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"])
|
||||||
|
color2 = to_occ_color(self.preferences["Background color (aux)"])
|
||||||
|
|
||||||
color1 = to_occ_color(self.preferences['Background color'])
|
if not self.preferences["Use gradient"]:
|
||||||
color2 = to_occ_color(self.preferences['Background color (aux)'])
|
|
||||||
|
|
||||||
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(getattr(Graphic3d_Camera, f'Projection_{projection_type}',
|
camera.SetProjectionType(
|
||||||
Graphic3d_Camera.Projection_Orthographic))
|
getattr(
|
||||||
|
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(Graphic3d_StereoMode, f'Graphic3d_StereoMode_{stereo_mode}',
|
params.StereoMode = getattr(
|
||||||
Graphic3d_StereoMode.Graphic3d_StereoMode_QuadBuffer)
|
Graphic3d_StereoMode,
|
||||||
|
f"Graphic3d_StereoMode_{stereo_mode}",
|
||||||
|
Graphic3d_StereoMode.Graphic3d_StereoMode_QuadBuffer,
|
||||||
|
)
|
||||||
|
|
||||||
def create_actions(self,parent):
|
def create_actions(self, parent):
|
||||||
|
self._actions = {
|
||||||
self._actions = \
|
"View": [
|
||||||
{'View' : [QAction(qta.icon('fa.arrows-alt'),
|
QAction(
|
||||||
'Fit (Shift+F1)',
|
qta.icon("fa.arrows-alt"),
|
||||||
|
"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(
|
||||||
|
QIcon(":/images/icons/isometric_view.svg"),
|
||||||
|
"Iso (Shift+F2)",
|
||||||
parent,
|
parent,
|
||||||
shortcut='shift+F2',
|
shortcut="shift+F2",
|
||||||
triggered=self.iso_view),
|
triggered=self.iso_view,
|
||||||
QAction(QIcon(':/images/icons/top_view.svg'),
|
),
|
||||||
'Top (Shift+F3)',
|
QAction(
|
||||||
|
QIcon(":/images/icons/top_view.svg"),
|
||||||
|
"Top (Shift+F3)",
|
||||||
parent,
|
parent,
|
||||||
shortcut='shift+F3',
|
shortcut="shift+F3",
|
||||||
triggered=self.top_view),
|
triggered=self.top_view,
|
||||||
QAction(QIcon(':/images/icons/bottom_view.svg'),
|
),
|
||||||
'Bottom (Shift+F4)',
|
QAction(
|
||||||
|
QIcon(":/images/icons/bottom_view.svg"),
|
||||||
|
"Bottom (Shift+F4)",
|
||||||
parent,
|
parent,
|
||||||
shortcut='shift+F4',
|
shortcut="shift+F4",
|
||||||
triggered=self.bottom_view),
|
triggered=self.bottom_view,
|
||||||
QAction(QIcon(':/images/icons/front_view.svg'),
|
),
|
||||||
'Front (Shift+F5)',
|
QAction(
|
||||||
|
QIcon(":/images/icons/front_view.svg"),
|
||||||
|
"Front (Shift+F5)",
|
||||||
parent,
|
parent,
|
||||||
shortcut='shift+F5',
|
shortcut="shift+F5",
|
||||||
triggered=self.front_view),
|
triggered=self.front_view,
|
||||||
QAction(QIcon(':/images/icons/back_view.svg'),
|
),
|
||||||
'Back (Shift+F6)',
|
QAction(
|
||||||
|
QIcon(":/images/icons/back_view.svg"),
|
||||||
|
"Back (Shift+F6)",
|
||||||
parent,
|
parent,
|
||||||
shortcut='shift+F6',
|
shortcut="shift+F6",
|
||||||
triggered=self.back_view),
|
triggered=self.back_view,
|
||||||
QAction(QIcon(':/images/icons/left_side_view.svg'),
|
),
|
||||||
'Left (Shift+F7)',
|
QAction(
|
||||||
|
QIcon(":/images/icons/left_side_view.svg"),
|
||||||
|
"Left (Shift+F7)",
|
||||||
parent,
|
parent,
|
||||||
shortcut='shift+F7',
|
shortcut="shift+F7",
|
||||||
triggered=self.left_view),
|
triggered=self.left_view,
|
||||||
QAction(QIcon(':/images/icons/right_side_view.svg'),
|
),
|
||||||
'Right (Shift+F8)',
|
QAction(
|
||||||
|
QIcon(":/images/icons/right_side_view.svg"),
|
||||||
|
"Right (Shift+F8)",
|
||||||
parent,
|
parent,
|
||||||
shortcut='shift+F8',
|
shortcut="shift+F8",
|
||||||
triggered=self.right_view),
|
triggered=self.right_view,
|
||||||
QAction(qta.icon('fa.square-o'),
|
),
|
||||||
'Wireframe (Shift+F9)',
|
QAction(
|
||||||
|
qta.icon("fa.square-o"),
|
||||||
|
"Wireframe (Shift+F9)",
|
||||||
parent,
|
parent,
|
||||||
shortcut='shift+F9',
|
shortcut="shift+F9",
|
||||||
triggered=self.wireframe_view),
|
triggered=self.wireframe_view,
|
||||||
QAction(qta.icon('fa.square'),
|
),
|
||||||
'Shaded (Shift+F10)',
|
QAction(
|
||||||
|
qta.icon("fa.square"),
|
||||||
|
"Shaded (Shift+F10)",
|
||||||
parent,
|
parent,
|
||||||
shortcut='shift+F10',
|
shortcut="shift+F10",
|
||||||
triggered=self.shaded_view)],
|
triggered=self.shaded_view,
|
||||||
'Tools' : [QAction(icon('screenshot'),
|
),
|
||||||
'Screenshot',
|
],
|
||||||
|
"Tools": [
|
||||||
|
QAction(
|
||||||
|
icon("screenshot"),
|
||||||
|
"Screenshot",
|
||||||
parent,
|
parent,
|
||||||
triggered=self.save_screenshot)]}
|
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)
|
||||||
@@ -176,199 +252,167 @@ 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 = self._get_context()
|
||||||
context.Display(ais,True)
|
context.Display(ais, True)
|
||||||
|
|
||||||
if self.preferences['Fit automatically']: self.fit()
|
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: ctx.Erase(ais,True)
|
for ais in ais_items:
|
||||||
|
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(self,
|
def show_grid(
|
||||||
step=1.,
|
self, step=1.0, size=10.0 + 1e-6, color1=(0.7, 0.7, 0.7), color2=(0, 0, 0)
|
||||||
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,
|
viewer.ActivateGrid(Aspect_GT_Rectangular, Aspect_GDM_Lines)
|
||||||
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(Quantity_Color(*color1,TOC_RGB),
|
grid.SetColors(
|
||||||
Quantity_Color(*color2,TOC_RGB))
|
Quantity_Color(*color1, 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,
|
def toggle_grid(self, value: bool, dim: float = 10.0):
|
||||||
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)
|
fname = get_save_filename(self.IMAGE_EXTENSIONS)
|
||||||
if fname != '':
|
if fname != "":
|
||||||
self._get_view().Dump(fname)
|
self._get_view().Dump(fname)
|
||||||
|
|
||||||
def _display_ais(self,ais):
|
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
|
||||||
|
|
||||||
@@ -379,10 +423,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)
|
||||||
|
|
||||||
|
|||||||
@@ -3,4 +3,4 @@ channels:
|
|||||||
- conda-forge
|
- conda-forge
|
||||||
- defaults
|
- defaults
|
||||||
dependencies:
|
dependencies:
|
||||||
- python=3.9
|
- python=3.10
|
||||||
|
|||||||
@@ -13,13 +13,13 @@ 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-39-x86_64-linux-gnu.so'), '.')]
|
ocp_path = [(os.path.join(HOMEPATH, 'OCP.cpython-310-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-39-darwin.so'), '.')]
|
ocp_path = [(os.path.join(HOMEPATH, 'OCP.cpython-310-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.cp39-win_amd64.pyd'), '.')]
|
ocp_path = [(os.path.join(HOMEPATH, 'OCP.cp310-win_amd64.pyd'), '.')]
|
||||||
|
|
||||||
datas1, binaries1, hiddenimports1 = collect_all('debugpy')
|
datas1, binaries1, hiddenimports1 = collect_all('debugpy')
|
||||||
hiddenimports2 = collect_submodules('xmlrpc')
|
hiddenimports2 = collect_submodules('xmlrpc')
|
||||||
@@ -50,13 +50,13 @@ a = Analysis(['run.py'],
|
|||||||
|
|
||||||
# There is an issue that keeps the OpenSSL libraries from being copied to the output directory.
|
# 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
|
# This should work if nothing else, but does not with GitHub Actions
|
||||||
if sys.platform == 'win32':
|
# if sys.platform == 'win32':
|
||||||
from PyInstaller.depend.bindepend import getfullnameof
|
# from PyInstaller.depend.bindepend import getfullnameof
|
||||||
rel_data_path = ['PyQt5', 'Qt', 'bin']
|
# rel_data_path = ['PyQt5', 'Qt', 'bin']
|
||||||
a.datas += [
|
# a.datas += [
|
||||||
(getfullnameof('libssl-1_1-x64.dll'), os.path.join(*rel_data_path), 'DATA'),
|
# (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'),
|
# (getfullnameof('libcrypto-1_1-x64.dll'), os.path.join(*rel_data_path), 'DATA'),
|
||||||
]
|
# ]
|
||||||
|
|
||||||
|
|
||||||
pyz = PYZ(a.pure, a.zipped_data,
|
pyz = PYZ(a.pure, a.zipped_data,
|
||||||
|
|||||||
9
setup.py
9
setup.py
@@ -26,15 +26,14 @@ setup(
|
|||||||
"CQ-editor = cq_editor.__main__:main",
|
"CQ-editor = cq_editor.__main__:main",
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
python_requires=">=3.8,<3.11",
|
python_requires=">=3.8,<3.12",
|
||||||
install_requires=[
|
install_requires=[
|
||||||
"logbook>=1",
|
"logbook>=1",
|
||||||
"ipython==8.4.0",
|
"ipython",
|
||||||
"jedi==0.17.2",
|
|
||||||
"path>=16",
|
"path>=16",
|
||||||
"PyQt5>=5",
|
"PySide6",
|
||||||
"requests>=2,<3",
|
"requests>=2,<3",
|
||||||
"spyder>=5,<6",
|
"spyder>=5,<6",
|
||||||
"pyqtgraph==0.12.4",
|
"pyqtgraph",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user