2 Commits

Author SHA1 Message Date
jdegenstein
4669eef9a2 revert and keep win32 asyncio 2023-02-11 16:48:14 -06:00
jdegenstein
8f62f098ee Incorporate run.py into __main__.py for setuptools
setuptools creates a broken cq-editor.exe that has a non-working ipython console
2023-02-11 16:03:47 -06:00
15 changed files with 1075 additions and 693 deletions

View File

@@ -10,37 +10,33 @@ on:
default: 'dir'
jobs:
build-linux:
runs-on: ubuntu-22.04
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v3
- uses: mamba-org/setup-micromamba@v1
- 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
create-args: >-
python=3.10
- name: pip install cadquery CQ-editor ... etc
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
sudo apt install -y qtbase5-dev qt5-qmake
micromamba info
pip install pyopengl
pip install git+https://github.com/jdegenstein/jmwright-CQ-Editor
pip install -vvv --pre git+https://github.com/cadquery/cadquery casadi
pip install pyinstaller>=5.6 git+https://github.com/jdegenstein/pyinstaller-hooks-contrib
pip install -vvv --pre git+https://github.com/cadquery/cadquery casadi==3.5.5
pip install pyinstaller==4.10
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/bd_warehouse
pip install git+https://github.com/meadiode/cq_gears.git@main
pip install -e "git+https://github.com/CadQuery/cadquery-plugins.git#egg=cq_cache&subdirectory=plugins/cq_cache"
pip install git+https://github.com/gumyr/build123d
pip install git+https://github.com/gumyr/build123d.git#egg=build123d
pip install git+https://github.com/JustinSDK/cqMore
pip list
- name: Run build
@@ -49,38 +45,37 @@ jobs:
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@v2
- uses: alehechka/upload-tartifact@v1
with:
name: CQ-editor-Linux-x86_64
path: dist
build-macos:
runs-on: macos-latest
steps:
- uses: actions/checkout@v3
- uses: mamba-org/setup-micromamba@v1
- 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
create-args: >-
python=3.10
- name: pip install cadquery CQ-editor ... etc
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
pip install pyinstaller>=5.6 git+https://github.com/jdegenstein/pyinstaller-hooks-contrib
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/gumyr/bd_warehouse
pip install git+https://github.com/meadiode/cq_gears.git@main
pip install -e "git+https://github.com/CadQuery/cadquery-plugins.git#egg=cq_cache&subdirectory=plugins/cq_cache"
pip install git+https://github.com/gumyr/build123d
pip install git+https://github.com/gumyr/build123d.git#egg=build123d
pip install git+https://github.com/JustinSDK/cqMore
pip list
- name: Run build
@@ -96,39 +91,36 @@ jobs:
build-windows:
runs-on: windows-latest
steps:
- uses: actions/checkout@v3
- uses: mamba-org/setup-micromamba@v1
- 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
init-shell: >-
powershell
create-args: >-
python=3.10
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
pip install pyinstaller>=5.6 git+https://github.com/jdegenstein/pyinstaller-hooks-contrib
pip install --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/gumyr/bd_warehouse
pip install git+https://github.com/meadiode/cq_gears.git@main
pip install -e "git+https://github.com/CadQuery/cadquery-plugins.git#egg=cq_cache&subdirectory=plugins/cq_cache"
pip install git+https://github.com/gumyr/build123d
pip install git+https://github.com/gumyr/build123d.git#egg=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 }}
pyinstaller --debug all 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@v2
- uses: alehechka/upload-tartifact@v1
with:
name: CQ-editor-Windows
path: dist

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
# CadQuery editor
This is a fork of [jmwright's fork](https://github.com/jmwright/CQ-editor) of [CadQuery/CQ-editor](https://github.com/CadQuery/CQ-editor). This fork includes changes that enable dark mode for CQ-editor (see screenshot below). Under the GitHub Actions menu this fork also contains static builds of CQ-editor for Linux/MacOS/Windows that include the [cq_gears](https://github.com/meadiode/cq_gears), [cq_cache](https://github.com/CadQuery/cadquery-plugins/tree/main/plugins/cq_cache), [cq_more](https://github.com/JustinSDK/cqMore), [cq_warehouse](https://github.com/gumyr/cq_warehouse), [bd_warehouse](https://github.com/gumyr/bd_warehouse), and [build123d](https://github.com/gumyr/build123d) libraries. Note you need to change color preferences to enable dark mode for all panes (see Edit -> Preferences).
This is a fork of [jmwright's fork](https://github.com/jmwright/CQ-editor) of [CadQuery/CQ-editor](https://github.com/CadQuery/CQ-editor). This fork includes changes that enable dark mode for CQ-editor (see screenshot below). Under the GitHub Actions menu this fork also contains static builds of CQ-editor for Linux/MacOS/Windows that include the [cq_gears](https://github.com/meadiode/cq_gears), [cq_cache](https://github.com/CadQuery/cadquery-plugins/tree/main/plugins/cq_cache), [cq_more](https://github.com/JustinSDK/cqMore), [cq_warehouse](https://github.com/gumyr/cq_warehouse), and [build123d](https://github.com/gumyr/build123d) libraries. Note you need to change color preferences to enable dark mode for all panes (see Edit -> Preferences).
This 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.
@@ -35,7 +35,7 @@ CadQuery GUI editor based on PyQT supports Linux, Windows and Mac.
### Release Packages
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.
TBD
### Development Packages
@@ -43,14 +43,9 @@ Development builds are also available, but can be unstable and should be used at
## 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 --pre "cadquery>=2.2"
pip install --pre git+https://github.com/cadquery/cadquery
pip install git+https://github.com/gumyr/build123d
```
@@ -58,12 +53,10 @@ pip install git+https://github.com/gumyr/build123d
### 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. The `name` parameter can assign a custom name which will appear in the objects pane of CQ-editor.
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.
```python
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))
show_object(result, options={"alpha":0.5, "color": (64, 164, 223)})
```
Note that `show_object` works for `Shape` and `TopoDS_Shape` objects too. In order to display objects from the embedded Python console use `show`.

View File

@@ -1,6 +1,10 @@
import sys
import asyncio
import argparse
if sys.platform == 'win32':
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
from PyQt5.QtWidgets import QApplication
NAME = 'CQ-editor'

View File

@@ -6,15 +6,9 @@ from typing import List
import cadquery as cq
from PyQt5 import QtCore
from PyQt5.QtCore import (
Qt,
QObject,
pyqtSlot,
pyqtSignal,
QEventLoop,
QAbstractTableModel,
)
from PyQt5.QtWidgets import QAction, QTableView
from PyQt5.QtCore import Qt, QObject, pyqtSlot, pyqtSignal, QEventLoop, QAbstractTableModel
from PyQt5.QtWidgets import (QAction,
QTableView)
from logbook import info
from path import Path
from pyqtgraph.parametertree import Parameter
@@ -24,43 +18,46 @@ from random import randrange as rrr, seed
from ..cq_utils import find_cq_objects, reload_cq
from ..mixins import ComponentMixin
DUMMY_FILE = "<string>"
DUMMY_FILE = '<string>'
class DbgState(Enum):
STEP = auto()
CONT = auto()
STEP_IN = auto()
RETURN = auto()
class DbgEevent(object):
LINE = "line"
CALL = "call"
RETURN = "return"
LINE = 'line'
CALL = 'call'
RETURN = 'return'
class LocalsModel(QAbstractTableModel):
HEADER = ("Name", "Type", "Value")
def __init__(self, parent):
super(LocalsModel, self).__init__(parent)
HEADER = ('Name','Type', 'Value')
def __init__(self,parent):
super(LocalsModel,self).__init__(parent)
self.frame = None
def update_frame(self, frame):
self.frame = [
(k, type(v).__name__, str(v))
for k, v in frame.items()
if not k.startswith("_")
]
def update_frame(self,frame):
self.frame = \
[(k,type(v).__name__, str(v)) 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:
return len(self.frame)
else:
return 0
def columnCount(self, parent=QtCore.QModelIndex()):
def columnCount(self,parent=QtCore.QModelIndex()):
return 3
def headerData(self, section, orientation, role=Qt.DisplayRole):
@@ -77,11 +74,13 @@ class LocalsModel(QAbstractTableModel):
return QtCore.QVariant()
class LocalsView(QTableView, ComponentMixin):
name = "Variables"
class LocalsView(QTableView,ComponentMixin):
def __init__(self, parent):
super(LocalsView, self).__init__(parent)
name = 'Variables'
def __init__(self,parent):
super(LocalsView,self).__init__(parent)
ComponentMixin.__init__(self)
header = self.horizontalHeader()
@@ -91,213 +90,180 @@ class LocalsView(QTableView, ComponentMixin):
vheader.setVisible(False)
@pyqtSlot(dict)
def update_frame(self, frame):
def update_frame(self,frame):
model = LocalsModel(self)
model.update_frame(frame)
self.setModel(model)
class Debugger(QObject,ComponentMixin):
class Debugger(QObject, ComponentMixin):
name = "Debugger"
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)
sigLocals = pyqtSignal(dict)
sigTraceback = pyqtSignal(object, str)
sigTraceback = pyqtSignal(object,str)
sigFrameChanged = pyqtSignal(object)
sigLineChanged = pyqtSignal(int)
sigLocalsChanged = pyqtSignal(dict)
sigCQChanged = pyqtSignal(dict, bool)
sigCQChanged = pyqtSignal(dict,bool)
sigDebugging = pyqtSignal(bool)
_frames: List[FrameType]
_frames : List[FrameType]
def __init__(self, parent):
super(Debugger, self).__init__(parent)
def __init__(self,parent):
super(Debugger,self).__init__(parent)
ComponentMixin.__init__(self)
self.inner_event_loop = QEventLoop(self)
self._actions = {
"Run": [
QAction(
icon("run"), "Render", self, shortcut="F5", triggered=self.render
),
QAction(
icon("debug"),
"Debug",
self,
checkable=True,
shortcut="ctrl+F5",
triggered=self.debug,
),
QAction(
icon("arrow-step-over"),
"Step",
self,
shortcut="ctrl+F10",
triggered=lambda: self.debug_cmd(DbgState.STEP),
),
QAction(
icon("arrow-step-in"),
"Step in",
self,
shortcut="ctrl+F11",
triggered=lambda: self.debug_cmd(DbgState.STEP_IN),
),
QAction(
icon("arrow-continue"),
"Continue",
self,
shortcut="ctrl+F12",
triggered=lambda: self.debug_cmd(DbgState.CONT),
),
]
}
self._actions = \
{'Run' : [QAction(icon('run'),
'Render',
self,
shortcut='F5',
triggered=self.render),
QAction(icon('debug'),
'Debug',
self,
checkable=True,
shortcut='ctrl+F5',
triggered=self.debug),
QAction(icon('arrow-step-over'),
'Step',
self,
shortcut='ctrl+F10',
triggered=lambda: self.debug_cmd(DbgState.STEP)),
QAction(icon('arrow-step-in'),
'Step in',
self,
shortcut='ctrl+F11',
triggered=lambda: self.debug_cmd(DbgState.STEP_IN)),
QAction(icon('arrow-continue'),
'Continue',
self,
shortcut='ctrl+F12',
triggered=lambda: self.debug_cmd(DbgState.CONT))
]}
self._frames = []
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):
return self.parent().components["editor"].debugger.get_breakpoints()
return self.parent().components['editor'].debugger.get_breakpoints()
def compile_code(self, cq_script):
try:
module = ModuleType("temp")
cq_code = compile(cq_script, "<string>", "exec")
module = ModuleType('temp')
cq_code = compile(cq_script, '<string>', 'exec')
return cq_code, module
except Exception:
self.sigTraceback.emit(sys.exc_info(), cq_script)
return None, None
def _exec(self, code, locals_dict, globals_dict):
with ExitStack() as stack:
fname = self.parent().components["editor"].filename
p = Path(fname if fname else "").abspath().dirname()
if self.preferences["Add script dir to path"] and p.exists():
sys.path.insert(0, p)
with ExitStack() as stack:
fname = self.parent().components['editor'].filename
p = Path(fname if fname else '').abspath().dirname()
if self.preferences['Add script dir to path'] and p.exists():
sys.path.insert(0,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)
if self.preferences["Reload imported modules"]:
if self.preferences['Reload imported modules']:
stack.enter_context(module_manager())
exec(code, locals_dict, globals_dict)
def _rand_color(self, alpha=0.0, cfloat=False):
# helper function to generate a random color dict
# for CQ-editor's show_object function
def _rand_color(self, alpha = 0., cfloat=False):
#helper function to generate a random color dict
#for CQ-editor's show_object function
lower = 10
upper = 100 # not too high to keep color brightness in check
if cfloat: # for two output types depending on need
upper = 100 #not too high to keep color brightness in check
if cfloat: #for two output types depending on need
return (
(rrr(lower, upper) / 255),
(rrr(lower, upper) / 255),
(rrr(lower, upper) / 255),
alpha,
)
return {
"alpha": alpha,
"color": (
rrr(lower, upper),
rrr(lower, upper),
rrr(lower, upper),
),
}
(rrr(lower,upper)/255),
(rrr(lower,upper)/255),
(rrr(lower,upper)/255),
alpha,
)
return {"alpha": alpha,
"color": (
rrr(lower,upper),
rrr(lower,upper),
rrr(lower,upper),
)}
def _inject_locals(self,module):
def _inject_locals(self, module):
cq_objects = {}
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,
):
def _show_object(obj,name=None, options={}):
if name:
cq_objects.update({name: SimpleNamespace(shape=obj, options=options)})
cq_objects.update({name : SimpleNamespace(shape=obj,options=options)})
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):
_show_object(obj, name, options=dict(color="red", alpha=0.2))
def _debug(obj,name=None):
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
_show_object(obj,name,options=dict(color='red',alpha=0.2))
return cq_objects, set(module.__dict__) - {"cq"}
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
def _cleanup_locals(self, module, injected_names):
for name in injected_names:
module.__dict__.pop(name)
return cq_objects, set(module.__dict__)-{'cq'}
def _cleanup_locals(self,module,injected_names):
for name in injected_names: module.__dict__.pop(name)
@pyqtSlot(bool)
def render(self):
seed(
59798267586177
) # reset the seed every time render is called (preserves colors run to run)
if self.preferences["Reload CQ"]:
seed(59798267586177) #reset the seed every time render is called (preserves colors run to run)
if self.preferences['Reload CQ']:
reload_cq()
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:
self._exec(cq_code, module.__dict__, module.__dict__)
# remove the special methods
self._cleanup_locals(module, injected_names)
#remove the special methods
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:
cq_objects = find_cq_objects(module.__dict__)
self.sigRendered.emit(cq_objects)
self.sigTraceback.emit(None, cq_script)
self.sigTraceback.emit(None,
cq_script)
self.sigLocals.emit(module.__dict__)
except Exception:
exc_info = sys.exc_info()
@@ -306,10 +272,11 @@ class Debugger(QObject, ComponentMixin):
@property
def breakpoints(self):
return [el[0] for el in self.get_breakpoints()]
return [ el[0] for el in self.get_breakpoints()]
@pyqtSlot(bool)
def debug(self, value):
def debug(self,value):
previous_trace = sys.gettrace()
if value:
@@ -317,35 +284,37 @@ class Debugger(QObject, ComponentMixin):
self.state = DbgState.STEP
self.script = self.get_current_script()
code, module = self.compile_code(self.script)
code,module = self.compile_code(self.script)
if code is None:
self.sigDebugging.emit(False)
self._actions["Run"][1].setChecked(False)
self._actions['Run'][1].setChecked(False)
return
cq_objects, injected_names = self._inject_locals(module)
cq_objects,injected_names = self._inject_locals(module)
# clear possible traceback
self.sigTraceback.emit(None, self.script)
#clear possible traceback
self.sigTraceback.emit(None,
self.script)
try:
sys.settrace(self.trace_callback)
exec(code, module.__dict__, module.__dict__)
exec(code,module.__dict__,module.__dict__)
except Exception:
exc_info = sys.exc_info()
sys.last_traceback = exc_info[-1]
self.sigTraceback.emit(exc_info, self.script)
self.sigTraceback.emit(exc_info,
self.script)
finally:
sys.settrace(previous_trace)
self.sigDebugging.emit(False)
self._actions["Run"][1].setChecked(False)
self._actions['Run'][1].setChecked(False)
if len(cq_objects) == 0:
cq_objects = find_cq_objects(module.__dict__)
self.sigRendered.emit(cq_objects)
self._cleanup_locals(module, injected_names)
self._cleanup_locals(module,injected_names)
self.sigLocals.emit(module.__dict__)
self._frames = []
@@ -353,37 +322,41 @@ class Debugger(QObject, ComponentMixin):
sys.settrace(previous_trace)
self.inner_event_loop.exit(0)
def debug_cmd(self, state=DbgState.STEP):
def debug_cmd(self,state=DbgState.STEP):
self.state = state
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
if filename == DUMMY_FILE:
if filename==DUMMY_FILE:
if not self._frames:
self._frames.append(frame)
self.trace_local(frame, event, arg)
self.trace_local(frame,event,arg)
return self.trace_callback
else:
return None
def trace_local(self, frame, event, arg):
def trace_local(self,frame,event,arg):
lineno = frame.f_lineno
if event in (DbgEevent.LINE,):
if (
self.state in (DbgState.STEP, DbgState.STEP_IN)
and frame is self._frames[-1]
) or (lineno in self.breakpoints):
if (self.state in (DbgState.STEP, DbgState.STEP_IN) and frame is self._frames[-1]) \
or (lineno in self.breakpoints):
if lineno in self.breakpoints:
self._frames.append(frame)
self.sigLineChanged.emit(lineno)
self.sigFrameChanged.emit(frame)
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_()
@@ -402,7 +375,7 @@ class Debugger(QObject, ComponentMixin):
@contextmanager
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())
try:

View File

@@ -1,11 +1,4 @@
from PyQt5.QtWidgets import (
QTreeWidget,
QTreeWidgetItem,
QAction,
QMenu,
QWidget,
QAbstractItemView,
)
from PyQt5.QtWidgets import QTreeWidget, QTreeWidgetItem, QAction, QMenu, QWidget, QAbstractItemView
from PyQt5.QtCore import Qt, pyqtSlot, pyqtSignal
from pyqtgraph.parametertree import Parameter, ParameterTree
@@ -15,118 +8,107 @@ from OCP.gp import gp_Dir, gp_Pnt, gp_Ax1
from ..mixins import ComponentMixin
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 ..utils import splitter, layout, get_save_filename
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):
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},
]
def __init__(
self,
name,
ais=None,
shape=None,
shape_display=None,
sig=None,
alpha=0.0,
color="#f4a824",
**kwargs
):
super(ObjectTreeItem, self).__init__([name], **kwargs)
self.setFlags(self.flags() | Qt.ItemIsUserCheckable)
self.setCheckState(0, Qt.Checked)
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}]
def __init__(self,
name,
ais=None,
shape=None,
shape_display=None,
sig=None,
alpha=0.,
color='#f4a824',
**kwargs):
super(ObjectTreeItem,self).__init__([name],**kwargs)
self.setFlags( self.flags() | Qt.ItemIsUserCheckable)
self.setCheckState(0,Qt.Checked)
self.ais = ais
self.shape = shape
self.shape_display = shape_display
self.sig = sig
self.properties = Parameter.create(name="Properties", children=self.props)
self.properties = Parameter.create(name='Properties',
children=self.props)
self.properties["Name"] = name
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['Name'] = name
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.sigTreeStateChanged.connect(self.propertiesChanged)
def propertiesChanged(self, properties, changed):
changed_prop = changed[0][0]
self.setData(0, 0, self.properties["Name"])
self.ais.SetTransparency(self.properties["Alpha"])
self.setData(0,0,self.properties['Name'])
self.ais.SetTransparency(self.properties['Alpha'])
if changed_prop.name() == "Color":
set_color(self.ais, to_occ_color(self.properties["Color"]))
if changed_prop.name() == 'Color':
set_color(self.ais, to_occ_color(self.properties['Color']))
self.ais.Redisplay()
if self.properties["Visible"]:
self.setCheckState(0, Qt.Checked)
if self.properties['Visible']:
self.setCheckState(0,Qt.Checked)
else:
self.setCheckState(0, Qt.Unchecked)
self.setCheckState(0,Qt.Unchecked)
if self.sig:
self.sig.emit()
class CQRootItem(TopTreeItem):
def __init__(self, *args, **kwargs):
super(CQRootItem, self).__init__(["CQ models"], *args, **kwargs)
def __init__(self,*args,**kwargs):
super(CQRootItem,self).__init__(['CQ models'],*args,**kwargs)
class HelpersRootItem(TopTreeItem):
def __init__(self, *args, **kwargs):
super(HelpersRootItem, self).__init__(["Helpers"], *args, **kwargs)
def __init__(self,*args,**kwargs):
super(HelpersRootItem,self).__init__(['Helpers'],*args,**kwargs)
class ObjectTree(QWidget, ComponentMixin):
name = "Object Tree"
class ObjectTree(QWidget,ComponentMixin):
name = 'Object Tree'
_stash = []
preferences = Parameter.create(
name="Preferences",
children=[
{"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},
],
)
preferences = Parameter.create(name='Preferences',children=[
{'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': .1}])
sigObjectsAdded = pyqtSignal([list], [list, bool])
sigObjectsAdded = pyqtSignal([list],[list,bool])
sigObjectsRemoved = pyqtSignal(list)
sigCQObjectSelected = pyqtSignal(object)
sigAISObjectsSelected = pyqtSignal(list)
sigItemChanged = pyqtSignal(QTreeWidgetItem, int)
sigItemChanged = pyqtSignal(QTreeWidgetItem,int)
sigObjectPropertiesChanged = pyqtSignal()
def __init__(self, parent):
super(ObjectTree, self).__init__(parent)
def __init__(self,parent):
self.tree = tree = QTreeWidget(
self, selectionMode=QAbstractItemView.ExtendedSelection
)
super(ObjectTree,self).__init__(parent)
self.tree = tree = QTreeWidget(self,
selectionMode=QAbstractItemView.ExtendedSelection)
self.properties_editor = ParameterTree(self)
tree.setHeaderHidden(True)
@@ -134,9 +116,10 @@ class ObjectTree(QWidget, ComponentMixin):
tree.setRootIsDecorated(False)
tree.setContextMenuPolicy(Qt.ActionsContextMenu)
# forward itemChanged singal
tree.itemChanged.connect(lambda item, col: self.sigItemChanged.emit(item, col))
# handle visibility changes form tree
#forward itemChanged singal
tree.itemChanged.connect(\
lambda item,col: self.sigItemChanged.emit(item,col))
#handle visibility changes form tree
tree.itemChanged.connect(self.handleChecked)
self.CQ = CQRootItem()
@@ -148,31 +131,30 @@ class ObjectTree(QWidget, ComponentMixin):
tree.expandToDepth(1)
self._export_STL_action = QAction(
"Export as STL",
self,
enabled=False,
triggered=lambda: self.export("stl", self.preferences["STL precision"]),
)
self._export_STL_action = \
QAction('Export as STL',
self,
enabled=False,
triggered=lambda: \
self.export('stl',
self.preferences['STL precision']))
self._export_STEP_action = QAction(
"Export as STEP", self, enabled=False, triggered=lambda: self.export("step")
)
self._export_STEP_action = \
QAction('Export as STEP',
self,
enabled=False,
triggered=lambda: \
self.export('step'))
self._clear_current_action = QAction(
icon("delete"),
"Clear current",
self,
enabled=False,
triggered=self.removeSelected,
)
self._clear_current_action = QAction(icon('delete'),
'Clear current',
self,
enabled=False,
triggered=self.removeSelected)
self._toolbar_actions = [
QAction(
icon("delete-many"), "Clear all", self, triggered=self.removeObjects
),
self._clear_current_action,
]
self._toolbar_actions = \
[QAction(icon('delete-many'),'Clear all',self,triggered=self.removeObjects),
self._clear_current_action,]
self.prepareMenu()
@@ -181,157 +163,135 @@ class ObjectTree(QWidget, ComponentMixin):
self.prepareLayout()
def prepareMenu(self):
self.tree.setContextMenuPolicy(Qt.CustomContextMenu)
self._context_menu = QMenu(self)
self._context_menu.addActions(self._toolbar_actions)
self._context_menu.addActions(
(self._export_STL_action, self._export_STEP_action)
)
self._context_menu.addActions((self._export_STL_action,
self._export_STEP_action))
def prepareLayout(self):
self._splitter = splitter(
(self.tree, self.properties_editor),
stretch_factors=(2, 1),
orientation=Qt.Vertical,
)
layout(self, (self._splitter,), top_widget=self)
self._splitter = splitter((self.tree,self.properties_editor),
stretch_factors = (2,1),
orientation=Qt.Vertical)
layout(self,(self._splitter,),top_widget=self)
self._splitter.show()
def showMenu(self, position):
def showMenu(self,position):
self._context_menu.exec_(self.tree.viewport().mapToGlobal(position))
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):
return self._toolbar_actions
def addLines(self):
origin = (0, 0, 0)
origin = (0,0,0)
ais_list = []
for name, color, direction in zip(
("X", "Y", "Z"),
((0.2, 0, 0), "lawngreen", "blue"),
((1, 0, 0), (0, 1, 0), (0, 0, 1)),
):
line_placement = Geom_Line(gp_Ax1(gp_Pnt(*origin), gp_Dir(*direction)))
for name,color,direction in zip(('X','Y','Z'),
('red','lawngreen','blue'),
((1,0,0),(0,1,0),(0,0,1))):
line_placement = Geom_Line(gp_Ax1(gp_Pnt(*origin),
gp_Dir(*direction)))
line = AIS_Line(line_placement)
line.SetColor(to_occ_color(color))
self.Helpers.addChild(ObjectTreeItem(name, ais=line))
self.Helpers.addChild(ObjectTreeItem(name,
ais=line))
ais_list.append(line)
self.sigObjectsAdded.emit(ais_list)
def _current_properties(self):
current_params = {}
for i in range(self.CQ.childCount()):
child = self.CQ.child(i)
current_params[child.properties["Name"]] = child.properties
current_params[child.properties['Name']] = child.properties
return current_params
def _restore_properties(self, obj, properties):
for p in properties[obj.properties["Name"]]:
def _restore_properties(self,obj,properties):
for p in properties[obj.properties['Name']]:
obj.properties[p.name()] = p.value()
@pyqtSlot(dict, bool)
@pyqtSlot(dict,bool)
@pyqtSlot(dict)
def addObjects(self, objects, clean=False, root=None):
def addObjects(self,objects,clean=False,root=None):
if root is None:
root = self.CQ
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:
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()
ais_list = []
# remove empty objects
objects_f = {k: v for k, v in objects.items() if not is_obj_empty(v.shape)}
#remove empty objects
objects_f = {k:v for k,v in objects.items() if not is_obj_empty(v.shape)}
for name, obj in objects_f.items():
ais, shape_display = make_AIS(obj.shape, obj.options)
for name,obj in objects_f.items():
ais,shape_display = make_AIS(obj.shape,obj.options)
child = ObjectTreeItem(
name,
shape=obj.shape,
shape_display=shape_display,
ais=ais,
sig=self.sigObjectPropertiesChanged,
)
child = ObjectTreeItem(name,
shape=obj.shape,
shape_display=shape_display,
ais=ais,
sig=self.sigObjectPropertiesChanged)
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)
root.addChild(child)
if request_fit_view:
self.sigObjectsAdded[list, bool].emit(ais_list, True)
self.sigObjectsAdded[list,bool].emit(ais_list,True)
else:
self.sigObjectsAdded[list].emit(ais_list)
@pyqtSlot(object, str, object)
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,
):
@pyqtSlot(object,str,object)
def addObject(self,obj,name='',options={}):
root = self.CQ
ais, shape_display = make_AIS(obj, options)
ais,shape_display = make_AIS(obj, options)
root.addChild(
ObjectTreeItem(
name,
shape=obj,
shape_display=shape_display,
ais=ais,
sig=self.sigObjectPropertiesChanged,
)
)
root.addChild(ObjectTreeItem(name,
shape=obj,
shape_display=shape_display,
ais=ais,
sig=self.sigObjectPropertiesChanged))
self.sigObjectsAdded.emit([ais])
@pyqtSlot(list)
@pyqtSlot()
def removeObjects(self, objects=None):
def removeObjects(self,objects=None):
if objects:
removed_items_ais = [self.CQ.takeChild(i).ais for i in objects]
else:
@@ -340,7 +300,8 @@ class ObjectTree(QWidget, ComponentMixin):
self.sigObjectsRemoved.emit(removed_items_ais)
@pyqtSlot(bool)
def stashObjects(self, action: bool):
def stashObjects(self,action : bool):
if action:
self._stash = self.CQ.takeChildren()
removed_items_ais = [ch.ais for ch in self._stash]
@@ -353,12 +314,14 @@ class ObjectTree(QWidget, ComponentMixin):
@pyqtSlot()
def removeSelected(self):
ixs = self.tree.selectedIndexes()
rows = [ix.row() for ix in ixs]
self.removeObjects(rows)
def export(self, export_type, precision=None):
def export(self,export_type,precision=None):
items = self.tree.selectedItems()
# if CQ models is selected get all children
@@ -370,12 +333,13 @@ class ObjectTree(QWidget, ComponentMixin):
shapes = [item.shape for item in items if item.parent() is self.CQ]
fname = get_save_filename(export_type)
if fname != "":
export(shapes, export_type, fname, precision)
if fname != '':
export(shapes,export_type,fname,precision)
@pyqtSlot()
def handleSelection(self):
items = self.tree.selectedItems()
items =self.tree.selectedItems()
if len(items) == 0:
self._export_STL_action.setEnabled(False)
self._export_STEP_action.setEnabled(False)
@@ -392,9 +356,10 @@ class ObjectTree(QWidget, ComponentMixin):
self._export_STEP_action.setEnabled(True)
self._clear_current_action.setEnabled(True)
self.sigCQObjectSelected.emit(item.shape)
self.properties_editor.setParameters(item.properties, showTop=False)
self.properties_editor.setParameters(item.properties,
showTop=False)
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_STEP_action.setEnabled(True)
else:
@@ -405,7 +370,8 @@ class ObjectTree(QWidget, ComponentMixin):
self.properties_editor.clear()
@pyqtSlot(list)
def handleGraphicalSelection(self, shapes):
def handleGraphicalSelection(self,shapes):
self.tree.clearSelection()
CQ = self.CQ
@@ -415,10 +381,11 @@ class ObjectTree(QWidget, ComponentMixin):
if item.ais.Shape().IsEqual(shape):
item.setSelected(True)
@pyqtSlot(QTreeWidgetItem, int)
def handleChecked(self, item, col):
@pyqtSlot(QTreeWidgetItem,int)
def handleChecked(self,item,col):
if type(item) is ObjectTreeItem:
if item.checkState(0):
item.properties["Visible"] = True
item.properties['Visible'] = True
else:
item.properties["Visible"] = False
item.properties['Visible'] = False

View File

@@ -10,7 +10,7 @@ from OCP.Aspect import Aspect_DisplayConnection, Aspect_TypeOfTriedronPosition
from OCP.OpenGl import OpenGl_GraphicDriver
from OCP.V3d import V3d_Viewer
from OCP.AIS import AIS_InteractiveContext, AIS_DisplayMode
from OCP.Quantity import Quantity_Color, Quantity_TOC_RGB as TOC_RGB
from OCP.Quantity import Quantity_Color
ZOOM_STEP = 0.9
@@ -54,8 +54,6 @@ class OCCTWidget(QWidget):
Aspect_TypeOfTriedronPosition.Aspect_TOTP_RIGHT_LOWER,
Quantity_Color(), 0.1)
view.ZBufferTriedronSetup(Quantity_Color(*(0.2, 0.0, 0.0), TOC_RGB))
viewer = self.viewer
viewer.SetDefaultLights()
@@ -97,8 +95,8 @@ class OCCTWidget(QWidget):
self.old_pos.y() - y, theToStart=True)
elif event.buttons() == Qt.RightButton:
self.view.Pan(x - self.old_pos.x(),
self.old_pos.y() - y, theToStart=True)
self.view.ZoomAtPoint(self.old_pos.x(), y,
x, self.old_pos.y())
self.old_pos = pos

View File

@@ -252,13 +252,13 @@ class OCCViewer(QWidget,ComponentMixin):
def front_view(self):
v = self._get_view()
v.SetProj(0,-1,0)
v.SetProj(0,1,0)
v.SetTwist(0)
def back_view(self):
v = self._get_view()
v.SetProj(0,1,0)
v.SetProj(0,-1,0)
v.SetTwist(0)
def left_view(self):

View File

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

98
pyinstaller.spec Normal file
View File

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

View File

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

View File

@@ -9,18 +9,17 @@ block_cipher = None
spyder_data = Path(site.getsitepackages()[-1]) / 'spyder'
parso_grammar = (Path(site.getsitepackages()[-1]) / 'parso/python').glob('grammar*')
cqw_path = Path(site.getsitepackages()[-1]) / 'cq_warehouse'
bdw_path = Path(site.getsitepackages()[-1]) / 'bd_warehouse'
cq_path = Path(site.getsitepackages()[-1]) / 'cadquery'
if sys.platform == 'linux':
occt_dir = os.path.join(Path(sys.prefix), 'share', 'opencascade')
ocp_path = [(os.path.join(HOMEPATH, 'OCP.cpython-310-x86_64-linux-gnu.so'), '.')]
ocp_path = [(os.path.join(HOMEPATH, 'OCP.cpython-39-x86_64-linux-gnu.so'), '.')]
elif sys.platform == 'darwin':
occt_dir = os.path.join(Path(sys.prefix), 'share', 'opencascade')
ocp_path = [(os.path.join(HOMEPATH, 'OCP.cpython-310-darwin.so'), '.')]
ocp_path = [(os.path.join(HOMEPATH, 'OCP.cpython-39-darwin.so'), '.')]
elif sys.platform == 'win32':
occt_dir = os.path.join(Path(sys.prefix), 'Library', 'share', 'opencascade')
ocp_path = [(os.path.join(HOMEPATH, 'OCP.cp310-win_amd64.pyd'), '.')]
ocp_path = [(os.path.join(HOMEPATH, 'OCP.cp39-win_amd64.pyd'), '.')]
datas1, binaries1, hiddenimports1 = collect_all('debugpy')
hiddenimports2 = collect_submodules('xmlrpc')
@@ -30,7 +29,6 @@ a = Analysis(['run.py'],
binaries=ocp_path + binaries1,
datas=[(spyder_data, 'spyder'),
(cqw_path, 'cq_warehouse'),
(bdw_path, 'bd_warehouse'),
(cq_path, 'cadquery')] +
[(p, 'parso/python') for p in parso_grammar] + datas1,
hiddenimports=['ipykernel.datapub', 'debugpy', 'vtkmodules', 'vtkmodules.all',
@@ -40,8 +38,7 @@ a = Analysis(['run.py'],
'zmq.backend', 'cq_warehouse', 'cq_warehouse.bearing', 'cq_warehouse.chain',
'cq_warehouse.drafting', 'cq_warehouse.extensions', 'cq_warehouse.fastener',
'cq_warehouse.sprocket', 'cq_warehouse.thread', 'cq_gears', 'cq_cache',
'build123d', 'cqmore', 'bd_warehouse', 'bd_warehouse.pipe', 'bd_warehouse.flange',
'bd_warehouse.thread', 'bd_warehouse.gears'] + hiddenimports1 + hiddenimports2,
'build123d', 'cqmore'] + hiddenimports1 + hiddenimports2,
hookspath=['pyinstaller/extrahooks/'],
runtime_hooks=['pyinstaller/pyi_rth_occ.py',
'pyinstaller/pyi_rth_fontconfig.py'],
@@ -53,13 +50,13 @@ a = Analysis(['run.py'],
# There is an issue that keeps the OpenSSL libraries from being copied to the output directory.
# This should work if nothing else, but does not with GitHub Actions
# if sys.platform == 'win32':
# from PyInstaller.depend.bindepend import getfullnameof
# rel_data_path = ['PyQt5', 'Qt', 'bin']
# a.datas += [
# (getfullnameof('libssl-1_1-x64.dll'), os.path.join(*rel_data_path), 'DATA'),
# (getfullnameof('libcrypto-1_1-x64.dll'), os.path.join(*rel_data_path), 'DATA'),
# ]
if sys.platform == 'win32':
from PyInstaller.depend.bindepend import getfullnameof
rel_data_path = ['PyQt5', 'Qt', 'bin']
a.datas += [
(getfullnameof('libssl-1_1-x64.dll'), os.path.join(*rel_data_path), 'DATA'),
(getfullnameof('libcrypto-1_1-x64.dll'), os.path.join(*rel_data_path), 'DATA'),
]
pyz = PYZ(a.pure, a.zipped_data,

View File

@@ -26,14 +26,15 @@ setup(
"CQ-editor = cq_editor.__main__:main",
]
},
python_requires=">=3.8,<3.12",
python_requires=">=3.8,<3.11",
install_requires=[
"logbook>=1",
"ipython",
"ipython==8.4.0",
"jedi==0.17.2",
"path>=16",
"PyQt5>=5",
"requests>=2,<3",
"spyder>=5,<6",
"pyqtgraph",
"pyqtgraph==0.12.4",
],
)