Compare commits

...

99 Commits

Author SHA1 Message Date
Syres916 6530e36418 Change macro path selection from File to Folder 2021-12-04 21:56:30 +01:00
Yorik van Havre c5eca93a20 Start: Fixed faling test 2021-11-26 03:42:15 +01:00
Heewa Barfchin e2fd673287 Start: fix bug when custom dir missing 2021-11-26 03:39:22 +01:00
Chris Hennes 45e1973680 [Start] Correct TestStart installation location 2021-11-26 03:35:53 +01:00
Yorik van Havre e97f4985c8 Merge pull request #4912 from sprhawk/master
ReverseEngineering: compile with PCL-1.12.0
2021-11-26 02:27:17 +01:00
Bernd Waibel f29d8d8dde [Fem] fix femtest failure with yaml.load
Solves an issue with using an outdated call to yaml.load
as described in
https://forum.freecadweb.org/viewtopic.php?f=10&t=57617

Signed-off-by: Bernd Waibel <waebbl-gentoo@posteo.net>
2021-11-26 02:23:57 +01:00
0penBrain 73ed43d2c1 [Sketcher][Bugfix] Fix crash when applying 'Constrain internal alignment' on contraints, fixes #4790 2021-11-22 20:10:08 +01:00
0penBrain 4bec2964ed [Sheet] Assert non-null pointer before using it 2021-11-22 04:09:12 +01:00
Zheng, Lei be674c362b App: fix crash on copy of Enumeration 2021-11-15 15:21:28 +01:00
Ajinkya Dahale 6ca055d277 [PD] Check if property is named before strcmp 2021-11-13 01:28:17 +01:00
Chris Hennes 3daf116fdf Merge pull request #5149 from donovaly/PD-loft-visibility-fix
[PD] fixes loft visibility on creation and modification
2021-11-08 23:45:52 +01:00
Uwe 951cb89046 [PD] simplify Loft code
- also two automatic style fixes made by MSVC
2021-11-08 23:45:17 +01:00
Ajinkya Dahale b8753a8ede [PartDesign] Fix typo in method name 2021-11-08 23:44:28 +01:00
Uwe 0f1ab68e88 backport commit commit 4f56ee263d
(done this way since cherry-picking etc. failed)
2021-11-07 21:01:49 +01:00
wmayer b33324a5df PD: [skip ci] fix ambiguous shortcut overload in TaskDlgPipeParameters 2021-11-07 18:38:00 +01:00
0penBrain bea3c220ca [Sketcher] Removed lines that was introduced to workaround grid visibility management issues 2021-11-05 03:38:51 +01:00
0penBrain 08880ec7aa [ViewProvider2DObject=>Sketcher] Fix grid visibility management
Error in grid visibility equation that makes grid to disappear when saving document in Edit mode
2021-11-05 03:38:50 +01:00
0penBrain 7ef83f9d4d [ViewProvider2DObject] Fix typo 2021-11-05 03:37:23 +01:00
Chris Hennes 5534bd375a Merge pull request #5098 from realthunder/FixStepColor
Fix STEP import/export color problem
2021-11-05 03:28:50 +01:00
Chris Hennes 765d7f708d Addon manager: Add support for non-master branches 2021-11-04 20:57:45 -05:00
Chris Hennes f069150ef5 Addon manager: correct check for Markdown library 2021-11-04 20:31:35 -05:00
Chris Hennes 68384f549c Addon manager: add support for salsa.debian.org 2021-11-04 20:31:35 -05:00
Uwe af2e83e06b [skip ci] Win installer: prepare for 0.19.3 release 2021-11-04 02:08:21 +01:00
wmayer 999423cd74 Sketcher: [skip ci] handle coincident points when trying to create arc/circle 2021-10-28 17:05:01 +02:00
sliptonic f924551620 Black reformat 2021-10-20 09:34:51 -05:00
sliptonic 27cc2b2a65 Job object doesn't exist in this context.
Derive output name in the output section or default if unknown
2021-10-20 09:34:34 -05:00
Ton 6d334db42c Update PathSanity.py
Output fillename for the report is now the same as the postprocessor job (job.PostProcessorOutputFile), this way the setup file will be autoloaded in LinuxCNC qtDraggon ui and others based on probe-basic.
There is some error checking missing, the check to see if the job.PostProcessorOutputFile is not empty is not there for example. But since the report can only be generated when there is a postprocessor file this is not a big problem.
2021-10-20 09:34:20 -05:00
Chris Hennes 6a441fea8d [Doc] Update URL for Coin3D
Corresponds to commit 67ebeea PR 5103 from @donovaly, adapted for the
0.19 release branch.
2021-10-09 13:21:11 -05:00
Łukasz Stelmach 7622933a16 Arch: Parse window opening modes with more than one digit
Signed-off-by: Łukasz Stelmach <stlman@poczta.fm>
2021-09-30 17:04:52 -05:00
Łukasz Stelmach d292788097 Arch: Fix material color pickers
Provide currently set color as initial value for getColor() to enable
convenient incremental changes.

Do not change the colour if the Cancel buton was pressed.

Signed-off-by: Łukasz Stelmach <stlman@poczta.fm>
2021-09-30 17:04:13 -05:00
Łukasz Stelmach c5d0acce8a Draft: Ensure horizontal orientation of snap_widget
From time to time when switching to other workbenches snap_widget changes
orientation to vertical. Make sure it stays horizontal.

Signed-off-by: Łukasz Stelmach <stlman@poczta.fm>
2021-09-30 17:03:17 -05:00
Russell Johnson b2206d81f3 Path: Downgrade module's debug level to INFO 2021-10-07 13:43:33 -05:00
Russell Johnson ee5b201b1e Path: Fix Face Region boundary shape usage
This commit removes automatic edge extension for `Face Region` boundary shape.  It applies the `Clear Edges` toggle for `Face Region`.  It applies `Exclude Raised Areas` correctly.  The problem of collision paths for `Clear Edges` with `Exclude Raised Areas` for certain use cases remains and is not fixed with this commit.
2021-10-07 13:37:51 -05:00
Russell Johnson 1ecd012a02 Path: Fix relative paths for bit path attributes in library files 2021-10-07 13:37:17 -05:00
Russell Johnson dab2d652ac Path: Fixes #0004647, relative shape file locations
This solution restricts relative shape files to filename only within the toolbit file.
2021-10-07 13:36:49 -05:00
Russell Johnson c8109c9401 Path: Fix readonly lockout bug with blank expression 2021-10-07 13:34:01 -05:00
Russell Johnson 39da720c8a Path: Fix bug with inaccurate Task Panel values upon new op creation
Path: Fix bug with inaccurate Task Panel values upon new op creation
2021-10-07 13:33:12 -05:00
Noah eaf5814778 Path: fix invalid JSON in 45degree_chamfer.fctb tool bit
Fixes an issue reported in https://forum.freecadweb.org/viewtopic.php?f=15&t=54307.

> Error loading tool: 45degree_chamfer.fctb : Expecting property name enclosed in double quotes: line 12 column 3 (char 298)
2021-10-07 13:19:40 -05:00
sliptonic da51b0be53 Update 45degree_chamfer.fctb 2021-10-07 13:19:14 -05:00
Claas Ahlrichs bba3a23405 Update grbl_post.py 2021-10-07 13:10:47 -05:00
ho-ho-ho e6673d60ad Update grbl_post.py
Append new line to return-to line
2021-10-07 13:09:59 -05:00
ho-ho-ho f1e4cb4433 Path: fix grbl --return-to placement 2021-10-07 13:07:36 -05:00
Russell Johnson db2d518aa2 Path: Remove Side assignment to Outside for full model profiles
The profile operation is forcing  cut side to `Outside` when profiling whole models.  This change in code also allows for profiling `Inside` of entire models.  Apart from limiting profiling capabilities of entire models, this override could cause problems with profiling of selected faces or edges in very specific use cases.  Also, this fix removes the property assignment of a key control property within the execution code, which should not happen, apart from feedback properties.
2021-10-07 13:03:03 -05:00
sliptonic 0d3c05447a Path bug splitting gcode by Fixture 2021-10-07 13:02:05 -05:00
sliptonic 9ed2c3570c fix cancel bug 2021-10-07 11:03:05 -05:00
sliptonic abe65e7a75 removed PathUtils import from all post-processors to avoid the deleted proxy error
I removed or commented out code using this import.  In all cases,
the import was used to get descriptive information from the Job
2021-10-07 10:59:26 -05:00
sliptonic 781cb10a7d fix bug adding TC from job dialog.
When adding tool controllers from the job dialog, it will now
try to match the toolbit file with the currently selected library
and add use the toolnumber from there.  If no match is found,
it will autoincrement from the existing tool controllers
in the job
2021-10-07 10:58:41 -05:00
Chris Nisbet 99d6dd7c5b Path: vcarve - Fix depth calculation
Depths were being calculated incorrectly due to MIC distance getting
divided by scaling factor rather than multiplied by it.

The scaling factor is the inverse of the tan of half the cutting tool
angle, so for a 30 degree bit (say), the scaling factor would be
1 / tan(30/2), or 3.7320.

When given an MIC (which is a radius) of 3.175 (say), and cutting to the
same width with a 30 degree bit, the depth should be 3.175 * 3.7320
(11..849), not 3.175 / 3.7320 (0.8509) as it is currently.
2021-10-07 10:58:04 -05:00
Markus Lampert e2173e88f4 Disable stock/model linking by default 2021-10-07 10:57:09 -05:00
Markus Lampert 5cfb3ce018 Fixed race condition in deleting job when cancelling its task panel 2021-10-07 10:56:54 -05:00
Markus Lampert 597b3b6ba0 Fixed aligning path model along an axis. 2021-10-07 10:56:41 -05:00
sliptonic 5007a72eaa [PATH] Fix bug with spindle not restarting
if output is split by operation spindle should restart when next file is loaded
2021-10-07 10:55:20 -05:00
Russell Johnson e2e002ea27 Path: Fix multi-OS path issue.
Not all operating systems use the same path scheme - the slash direction.  The issue fixed with this commit exists in other parts of the Path workbench
2021-10-07 10:45:20 -05:00
troyp76 a8270021ee Operations were being recalculated even if the dirty state was False.
The "isDirty" function was not being called correctly by the accept call back.
2021-10-07 10:17:38 -05:00
Heewa Barfchin 24550b0ccf Path: check for empty before using - fixes #4645
In a few locations, python objects are used without checking if they
exist and are non-null, which throws missing attribute exceptions. The
fix is to simply check first.
2021-10-07 10:16:47 -05:00
Russell Johnson c9ab285c61 Path: Fix connected multi-face clearing bug
Identified by MLampert.  If multiple faces are selected at varying Z-heights, the `combineConnectedShapes()` method fails to connect them due to offset Z-heights.  This fix places all input faces for that method into the same working plane.
2021-10-07 10:13:59 -05:00
mwganson 31b8344d73 [Spreadsheet] Prevent infinite loop when copy/paste of non-rectangular selection 2021-10-04 17:47:42 -05:00
0penBrain 0f303a785b [Sketcher] Fix 'Reference' checkbox not working for diameter 2021-10-04 20:21:20 +02:00
0penBrain 51ea0ed9e6 [Sketcher] Fix 'Reference' checkbox not working for radius/diameter ; fixes #4627
As a consequence :
Remove message box when several arcs/circles are selected ...
... and enforce creation of equality constraints in this case
Former existing option of adding a dimensional constraint ...
... to each circle encouraged improper constraining
2021-10-04 20:20:52 +02:00
Bernd Hahnebach 9989a2ef4a Test: fix failing doc tests, from commit 20c4c369b3 2021-10-02 08:03:02 +02:00
Bernd Hahnebach 2b9894f3a9 FEM: material task panel, raise max possible young's modul value 2021-10-01 22:18:23 +02:00
wmayer 76c7f52543 Part: [skip ci] workaround for OCCT 7.5.2 2021-10-01 22:07:57 +02:00
Bernd Hahnebach de7cba4669 gitlab CI: fix build 2021-10-01 18:15:17 +02:00
Przemo Firszt 1f024523e6 [UnitTest] Fix UnitTests - use utf-8
On some systems there is a problem with utf-8 during testing. This
commit is trying to address it. Tested only on ubuntu 20.04

Signed-off-by: Przemo Firszt <przemo@firszt.eu>
2021-10-01 17:52:35 +02:00
Przemo Firszt 6989306097 [UnitTest] Replace deg symbol with deg
unittest cannot handle the symbol on some systems. deg is a safe
solution.

Signed-off-by: Przemo Firszt <przemo@firszt.eu>
2021-10-01 17:52:35 +02:00
Bernd Hahnebach 211d74f4a6 CI gitlab: update readme 2021-10-01 17:52:35 +02:00
Przemo Firszt eafe6b73ec [gitlab-ci] gitlab CI on docker initial commit 2021-10-01 17:52:35 +02:00
donovaly 1eb65aa002 [Part] uniform feature sorting
at the moment the sorting is confusing, the sorting in the menu differs from the toolbars and some features even belong in the menu to another category than in the toolbars and one feature has not yet a toolbar entry

This PR implements a uniform sorting for the menu and toolbar and adds the missing toolbar icon
2021-09-29 10:44:43 -05:00
Syres916 969c1eee2d Changes as per @Chennes advice 2021-09-23 12:07:37 -05:00
Syres916 c974f914db [Gui] Preferences - Selection Fix Bug for new...
... users who save an unrelated Preference such as Stylesheet from loosing the default selection settings.
See discussion : https://forum.freecadweb.org/viewtopic.php?f=3&t=58215
2021-09-23 12:07:27 -05:00
Tomas Pavlicek 5a7a2d2f81 TechDraw - Undo for Cosmetics Vertices and Lines 2021-09-23 12:07:08 -05:00
Heewa Barfchin 312d8299cb App: Abort save when unable to finish
In these two cases, the project's file will not contain the latest
changes, so returning as if the save is complete is incorrect and can
lead to data loss (such as on app quit).
2021-09-23 12:06:47 -05:00
Heewa Barfchin 257d22e1e0 Fix app quitting after failed save. Fixes #4098
Return `false` from Document::save() to properly communicate the
failure, which the app-quit process already handles by aborting.
2021-09-23 12:06:32 -05:00
Syres916 a43995fc45 [AddonManager] stderr remove translate 2021-09-23 12:06:08 -05:00
Syres916 0988177e62 [AddonManager] Handle stderr from git status
See discussion https://forum.freecadweb.org/viewtopic.php?f=10&t=62265
2021-09-23 12:05:59 -05:00
turn211 7b5e18a075 Gui: ProDark Stylesheet QT Icon Fix
forum thread: https://forum.freecadweb.org/viewtopic.php?f=34&t=57427
2021-04-15 11:17:08 +02:00
turn211 ed87d3aceb [Stylesheet] ProDark: major bugfix for add-ons
https://forum.freecadweb.org/viewtopic.php?f=34&t=55134&start=100
2021-04-15 11:15:49 +02:00
turn211 133ef7173d Gui: Update usability enhancements to ProDark theme
forum thread: https://forum.freecadweb.org/viewtopic.php?f=34&t=55134
2021-04-15 11:15:33 +02:00
donovaly 8e7a8b1e7b [FEM] fix specific heat of air
- also adjust values slightly according to various sources

Since a factor 1000 is a big mistake and air is the most common gas, please commit this also for FC 0.19
2021-04-14 14:06:58 +02:00
Chris Hennes b3abe32683 [Draft] Eliminate required GUI for importDXF 2021-04-14 13:59:36 +02:00
Chris Hennes 53b4eb0b2e [Start] Correct TestStart installation location 2021-04-14 13:57:42 +02:00
Chris Hennes 51855e5341 [GUI] Stylesheet fix for icons in Qt file dialog
Work by @turn211.
2021-04-14 13:34:28 +02:00
wmayer 12055a22d8 Py: fix memory leak 2021-04-14 13:24:32 +02:00
Chris Hennes 6f67fbc425 [GUI] Remove explicit triangle styling from tabs
On Windows the lower tabs of the PropertyView were being set to
triangular, which breaks stylesheets. This removes that code, so they
now take on whatever the standard shape is on the system.

Fixes #0004599
2021-04-04 09:58:35 +02:00
Matthijs Kooijman 83e308c8eb Gui: Enable compression of tablet motion events
By default (on platforms that support it, X11 and Windows currently) QT
applies compression for high frequency events (mouse move, touch, window
resizes) to keep things smooth even when handling the event takes a
while (e.g. to calculate snapping).

However, tablet pen move events (and mouse move events synthesised from
those, which is what FreeCAD uses) are not compressed by default (to
allow maximum precision when e.g. hand-drawing curves), leading to
unacceptable slowdowns using a tablet pen.

This commit enable compression for tablet events here to solve that and
make use of a tablet just as smooth as a mouse with FreeCAD.

This can (and likely will) lead to some movement events being dropped,
but since there is no freeform curve drawing tool, that should not be
problematic (and if it is ever added, it could still work without
compression if the mouse movement event handler is written to be fast
enough).
2021-03-29 14:10:56 +02:00
donovaly de1d0acaea [installer] improve uninstalling
- fix missing removal of the *.FCStd extension for local users
- FC-specific file extensions might appear in the Windows registry. The aim of the uninstaller is to leave a clear registry without traces of FC.
2021-03-28 16:44:20 +02:00
wmayer e239684102 TD: fixes #0004598: Segfault when deleting template without page 2021-03-28 13:40:35 +02:00
tomate44 aedd1f96e4 PartDesign Helix: update NbTurns in Pitch-Height-Angle mode 2021-03-28 13:17:09 +02:00
lvk88 e7c796edb4 [GUI] Enable dragging in dependency graph view
This commit makes it possible to drag the dependency graph using the
left mouse button.

See issue #3896.
2021-03-28 12:10:43 +02:00
wmayer 6858586642 Import: [skip ci] avoid conflicting class names in global namespace 2021-03-19 15:18:57 +01:00
wmayer a88db11e0a Fem: [skip ci] set backend of matplotlib to Qt5Agg 2021-03-11 20:48:53 +01:00
donovaly f4574cf02d [Gui] fix HiDPI rounding bug
As reported in https://forum.freecadweb.org/viewtopic.php?f=10&t=56425&start=20#p485809
this PR fixes the problem reported here: https://forum.freecadweb.org/viewtopic.php?f=10&t=56425&start=20#p485759
2021-03-10 15:23:33 +01:00
Chris Hennes 159a71bba7 [0.19][Web] Remove HiDPI fix when using QTWEBVIEW
The screen scale adjustment code here should not be necessary when using
the Chromium-based QtWebView widget, which correctly handles logical vs.
device px units. The scaling causes poor font size choice when used on
ultrawide monitors, as reported in
https://forum.freecadweb.org/viewtopic.php?p=485431
2021-03-08 18:51:06 +01:00
donovaly dfc3e2b9a6 [Main] set Coin environment variable
as discussed in https://forum.freecadweb.org/viewtopic.php?f=10&t=56386&start=10 we need to set this environment variable for Coin on Windows
2021-03-08 15:21:17 +01:00
donovaly 27b568e4e4 update for FC 0.19.0 2021-03-05 12:24:05 +01:00
wmayer a266852510 PD: AddressSanitizer: heap-use-after-free
Fix crash inside TaskDlgSketchBasedParameters::reject()
2021-03-03 15:53:44 +01:00
donovaly 408128838c [PD] fix multi-transform View
- fixes bug 4581
  See the first issue reported here:
  https://forum.freecadweb.org/viewtopic.php?f=3&t=56093#p482553

- also fix issue that one could set 1 occurrence for polar patterns despite 2 are required at least

- also fix potential dereferencing null pointer (reported by MSVC)
2021-03-02 10:40:13 +01:00
Yorik van Havre fc0c1069d2 Arch: Fixed SectionPlane outside objects bug in ArchSectionPlane - Fixes #4549 2021-03-01 16:50:10 +01:00
wmayer 5ea583466d Gui: [skip ci] allow to unbind() an ExpressionBinding and automatically unbind it if the observed object has been deleted
For more details see: https://forum.freecadweb.org/viewtopic.php?f=8&t=56097
2021-03-01 16:00:08 +01:00
101 changed files with 1788 additions and 1211 deletions
+43
View File
@@ -0,0 +1,43 @@
# gitlab CI config file
# this image is on dockerhub. Dockerfile is here: https://gitlab.com/PrzemoF/FreeCAD/-/blob/gitlab-v1/ci/Dockerfile
image: freecadci/runner
stages: # List of stages for jobs, and their order of execution
- build
- test
before_script:
- apt-get update -yqq
# CCache Config
- mkdir -p ccache
- export CCACHE_BASEDIR=${PWD}
- export CCACHE_DIR=${PWD}/ccache
cache:
paths:
- ccache/
build-job: # This job runs in the build stage, which runs first.
stage: build
script:
- echo "Compiling the code..."
- mkdir build
- cd build
- ccache cmake -DBUILD_QT5=1 ../
- ccache cmake --build ./ -j$(nproc)
- echo "Compile complete."
artifacts:
paths:
- build/
test-job: # This job runs in the test stage.
stage: test # It only starts when the job in the build stage completes successfully.
script:
- echo "Running unit tests... "
- cd build/bin/
# Testing currently doesn't work due to problems with libraries ot being visible by the binary.
- ./FreeCADCmd -t 0
+118
View File
@@ -0,0 +1,118 @@
FROM ubuntu:20.04
MAINTAINER Przemo Firszt
# This is the docker image definition used to build FreeCAD. It's currently accessible on:
# https://hub.docker.com/repository/docker/freecadci/runner
# on under name freecadci/runner when using docker
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update -y
RUN apt-get update -y && apt-get install -y gnupg2
RUN echo "deb http://ppa.launchpad.net/freecad-maintainers/freecad-daily/ubuntu focal main" >> /etc/apt/sources.list.d/freecad-daily.list
RUN apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 83193AA3B52FF6FCF10A1BBF005EAE8119BB5BCA
RUN apt-get update -y
# those 3 are for debugging purposes only. Not required to build FreeCAD
RUN apt-get install -y \
vim \
nano \
bash
# Main set of FreeCAD dependencies. To be verified.
RUN apt-get install -y \
ccache \
cmake \
debhelper \
dh-exec \
dh-python \
doxygen \
git \
graphviz \
libboost-date-time-dev \
libboost-dev \
libboost-filesystem-dev \
libboost-filesystem1.71-dev \
libboost-graph-dev \
libboost-iostreams-dev \
libboost-program-options-dev \
libboost-program-options1.71-dev \
libboost-python1.71-dev \
libboost-regex-dev \
libboost-regex1.71-dev \
libboost-serialization-dev \
libboost-system1.71-dev \
libboost-thread-dev \
libboost-thread1.71-dev \
libboost1.71-dev \
libcoin-dev \
libdouble-conversion-dev \
libeigen3-dev \
libglew-dev \
libgts-bin \
libgts-dev \
libkdtree++-dev \
liblz4-dev \
libmedc-dev \
libmetis-dev \
libnglib-dev \
libocct-data-exchange-dev \
libocct-ocaf-dev \
libocct-visualization-dev \
libopencv-dev \
libproj-dev \
libpyside2-dev \
libqt5opengl5 \
libqt5opengl5-dev \
libqt5svg5-dev \
libqt5webkit5 \
libqt5webkit5-dev \
libqt5x11extras5-dev \
libqt5xmlpatterns5-dev \
libshiboken2-dev \
libspnav-dev \
libvtk7-dev \
libvtk7.1p \
libvtk7.1p-qt \
libx11-dev \
libxerces-c-dev \
libzipios++-dev \
lsb-release \
nastran \
netgen \
netgen-headers \
occt-draw \
pybind11-dev \
pyqt5-dev-tools \
pyside2-tools \
python3-dev \
python3-matplotlib \
python3-pivy \
python3-ply \
python3-pyqt5 \
python3-pyside2.* \
python3-pyside2.qtcore \
python3-pyside2.qtgui \
python3-pyside2.qtsvg \
python3-pyside2.qtuitools \
python3-pyside2.qtwidgets \
python3-pyside2.qtxml \
python3-requests \
python3-yaml \
qt5-default \
qt5-qmake \
qtbase5-dev \
qttools5-dev \
qtwebengine5-dev \
swig
RUN apt-get update -y --fix-missing
# Clean
RUN apt-get clean \
&& rm /var/lib/apt/lists/* \
/usr/share/doc/* \
/usr/share/locale/* \
/usr/share/man/* \
/usr/share/info/* -fR
+4 -5
View File
@@ -2398,8 +2398,8 @@ private:
Base::FileInfo tmp(sourcename);
if (tmp.renameFile(targetname.c_str()) == false) {
Base::Console().Warning("Cannot rename file from '%s' to '%s'\n",
sourcename.c_str(), targetname.c_str());
throw Base::FileException(
"Cannot rename tmp save file to project file", targetname);
}
}
void applyTimeStamp(const std::string& sourcename, const std::string& targetname) {
@@ -2531,9 +2531,8 @@ private:
Base::FileInfo tmp(sourcename);
if (tmp.renameFile(targetname.c_str()) == false) {
Base::Console().Error("Save interrupted: Cannot rename file from '%s' to '%s'\n",
sourcename.c_str(), targetname.c_str());
//throw Base::FileException("Save interrupted: Cannot rename temporary file to project file", tmp);
throw Base::FileException(
"Save interrupted: Cannot rename temporary file to project file", tmp);
}
if (numberOfFiles <= 0) {
+1
View File
@@ -38,6 +38,7 @@ Enumeration::Enumeration()
}
Enumeration::Enumeration(const Enumeration &other)
: _EnumArray(NULL), _ownEnumArray(false), _index(0), _maxVal(-1)
{
if (other._ownEnumArray) {
setEnums(other.getEnumVector());
+1 -1
View File
@@ -56,7 +56,7 @@ DEALINGS IN THE SOFTWARE.
<hr>
<h3><a name="_TocCoin3D"></a>Coin3D</h3>
<p>Web site: <a href="https://bitbucket.org/Coin3D/coin/">https://bitbucket.org/Coin3D/coin/</a></p>
<p>Web site: <a href="https://coin3d.github.io">https://coin3d.github.io</a></p>
<p>Copyright: Coin is copyright (C) 1998-2013 Kongsberg Oil & Gas Technologies AS</p>
<p>License: BSD-3-clause</p>
<pre>
+17
View File
@@ -1937,6 +1937,9 @@ void Application::runApplication(void)
else {
// Enable automatic scaling based on pixel density of display (added in Qt 5.6)
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#if QT_VERSION >= QT_VERSION_CHECK(5,14,0)
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
#endif
}
#endif // QT_VERSION >= 0x050600
@@ -1954,6 +1957,20 @@ void Application::runApplication(void)
}
#endif // QT_VERSION >= 0x050400
#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0))
// By default (on platforms that support it, see docs for
// Qt::AA_CompressHighFrequencyEvents) QT applies compression
// for high frequency events (mouse move, touch, window resizes)
// to keep things smooth even when handling the event takes a
// while (e.g. to calculate snapping).
// However, tablet pen move events (and mouse move events
// synthesised from those) are not compressed by default (to
// allow maximum precision when e.g. hand-drawing curves),
// leading to unacceptable slowdowns using a tablet pen. Enable
// compression for tablet events here to solve that.
QCoreApplication::setAttribute(Qt::AA_CompressTabletEvents);
#endif
// A new QApplication
Base::Console().Log("Init: Creating Gui::Application and QApplication\n");
+3
View File
@@ -220,6 +220,9 @@
<property name="toolTip">
<string>The directory in which the application will search for macros</string>
</property>
<property name="mode">
<enum>Gui::FileChooser::Directory</enum>
</property>
<property name="prefEntry" stdset="0">
<cstring>MacroPath</cstring>
</property>
+4 -4
View File
@@ -58,10 +58,10 @@ void DlgSettingsSelection::saveSettings()
void DlgSettingsSelection::loadSettings()
{
auto handle = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/TreeView");
ui->checkBoxAutoSwitch->setChecked(handle->GetBool("SyncView"));
ui->checkBoxAutoExpand->setChecked(handle->GetBool("SyncSelection"));
ui->checkBoxPreselect->setChecked(handle->GetBool("PreSelection"));
ui->checkBoxRecord->setChecked(handle->GetBool("RecordSelection"));
ui->checkBoxAutoSwitch->setChecked(handle->GetBool("SyncView", true));
ui->checkBoxAutoExpand->setChecked(handle->GetBool("SyncSelection", true));
ui->checkBoxPreselect->setChecked(handle->GetBool("PreSelection", true));
ui->checkBoxRecord->setChecked(handle->GetBool("RecordSelection", true));
ui->checkBoxSelectionCheckBoxes->setChecked(handle->GetBool("CheckBoxesSelection"));
}
+1
View File
@@ -1161,6 +1161,7 @@ bool Document::save(void)
catch (const Base::Exception& e) {
QMessageBox::critical(getMainWindow(), QObject::tr("Saving document failed"),
QString::fromLatin1(e.what()));
return false;
}
return true;
}
+20 -1
View File
@@ -59,6 +59,13 @@ bool ExpressionBinding::isBound() const
return path.getDocumentObject() != 0;
}
void ExpressionBinding::unbind()
{
expressionchanged.disconnect();
objectdeleted.disconnect();
path = App::ObjectIdentifier();
}
void Gui::ExpressionBinding::setExpression(boost::shared_ptr<Expression> expr)
{
DocumentObject * docObj = path.getDocumentObject();
@@ -100,7 +107,11 @@ void ExpressionBinding::bind(const App::ObjectIdentifier &_path)
//connect to be informed about changes
DocumentObject * docObj = path.getDocumentObject();
connection = docObj->ExpressionEngine.expressionChanged.connect(boost::bind(&ExpressionBinding::expressionChange, this, bp::_1));
if (docObj) {
expressionchanged = docObj->ExpressionEngine.expressionChanged.connect(boost::bind(&ExpressionBinding::expressionChange, this, bp::_1));
App::Document* doc = docObj->getDocument();
objectdeleted = doc->signalDeletedObject.connect(boost::bind(&ExpressionBinding::objectDeleted, this, bp::_1));
}
}
void ExpressionBinding::bind(const Property &prop)
@@ -247,3 +258,11 @@ void ExpressionBinding::expressionChange(const ObjectIdentifier& id) {
if(id==path)
onChange();
}
void ExpressionBinding::objectDeleted(const App::DocumentObject& obj)
{
DocumentObject * docObj = path.getDocumentObject();
if (docObj == &obj) {
unbind();
}
}
+7 -4
View File
@@ -44,6 +44,7 @@ public:
virtual void bind(const App::ObjectIdentifier & _path);
virtual void bind(const App::Property & prop);
bool isBound() const;
void unbind();
virtual bool apply(const std::string &propName);
virtual bool apply();
bool hasExpression() const;
@@ -52,8 +53,8 @@ public:
//auto apply means that the python code is issued not only on apply() but
//also on setExpression
bool autoApply() const {return m_autoApply;};
void setAutoApply(bool value) {m_autoApply = value;};
bool autoApply() const {return m_autoApply;}
void setAutoApply(bool value) {m_autoApply = value;}
protected:
const App::ObjectIdentifier & getPath() const { return path; }
@@ -63,7 +64,7 @@ protected:
virtual void setExpression(boost::shared_ptr<App::Expression> expr);
//gets called when the bound expression is changed, either by this binding or any external action
virtual void onChange() {};
virtual void onChange() {}
private:
App::ObjectIdentifier path;
@@ -75,7 +76,9 @@ protected:
int iconHeight;
void expressionChange(const App::ObjectIdentifier& id);
boost::signals2::scoped_connection connection;
void objectDeleted(const App::DocumentObject&);
boost::signals2::scoped_connection expressionchanged;
boost::signals2::scoped_connection objectdeleted;
bool m_autoApply;
};
+89 -1
View File
@@ -33,8 +33,10 @@
# include <QSvgRenderer>
# include <QGraphicsSvgItem>
# include <QMessageBox>
# include <QMouseEvent>
# include <QGraphicsScene>
# include <QGraphicsView>
# include <QScrollBar>
# include <QThread>
# include <QProcess>
# include <boost_bind_bind.hpp>
@@ -146,6 +148,92 @@ private:
QByteArray str, flatStr;
};
// Simple wrapper around QGraphicsView to make panning possible
class GraphvizGraphicsView final : public QGraphicsView
{
public:
GraphvizGraphicsView(QGraphicsScene* scene, QWidget* parent);
~GraphvizGraphicsView() = default;
GraphvizGraphicsView(const GraphvizGraphicsView&) = delete;
GraphvizGraphicsView(GraphvizGraphicsView&&) = delete;
GraphvizGraphicsView& operator=(const GraphvizGraphicsView&) = delete;
GraphvizGraphicsView& operator=(GraphvizGraphicsView&&) = delete;
protected:
void mousePressEvent(QMouseEvent *event) override;
void mouseMoveEvent(QMouseEvent *event) override;
void mouseReleaseEvent(QMouseEvent *event) override;
private:
bool isPanning;
QPoint panStart;
};
GraphvizGraphicsView::GraphvizGraphicsView(QGraphicsScene* scene, QWidget* parent) : QGraphicsView(scene, parent),
isPanning(false)
{
}
void GraphvizGraphicsView::mousePressEvent(QMouseEvent* e)
{
if(e && e->button() == Qt::LeftButton)
{
isPanning = true;
panStart = e->pos();
e->accept();
QApplication::setOverrideCursor(Qt::ClosedHandCursor);
}
QGraphicsView::mousePressEvent(e);
return;
}
void GraphvizGraphicsView::mouseMoveEvent(QMouseEvent *e)
{
if(e == nullptr)
{
return;
}
if(isPanning)
{
auto* horizontalScrollbar = horizontalScrollBar();
auto* verticalScrollbar = verticalScrollBar();
if(horizontalScrollbar == nullptr ||
verticalScrollbar == nullptr)
{
return;
}
auto direction = e->pos() - panStart;
horizontalScrollbar->setValue(horizontalScrollbar->value() - direction.x());
verticalScrollbar->setValue(verticalScrollbar->value() - direction.y());
panStart = e->pos();
e->accept();
}
QGraphicsView::mouseMoveEvent(e);
return;
}
void GraphvizGraphicsView::mouseReleaseEvent(QMouseEvent* e)
{
if(e && e->button() & Qt::LeftButton)
{
isPanning = false;
QApplication::restoreOverrideCursor();
e->accept();
}
QGraphicsView::mouseReleaseEvent(e);
return;
}
}
/* TRANSLATOR Gui::GraphvizView */
@@ -165,7 +253,7 @@ GraphvizView::GraphvizView(App::Document & _doc, QWidget* parent)
scene->addItem(svgItem);
// Create view and zoomer object
view = new QGraphicsView(scene, this);
view = new GraphvizGraphicsView(scene, this);
zoomer = new GraphicsViewZoom(view);
zoomer->set_modifiers(Qt::NoModifier);
view->show();
-3
View File
@@ -89,9 +89,6 @@ PropertyView::PropertyView(QWidget *parent)
tabs = new QTabWidget (this);
tabs->setObjectName(QString::fromUtf8("propertyTab"));
tabs->setTabPosition(QTabWidget::South);
#if defined(Q_OS_WIN32)
tabs->setTabShape(QTabWidget::Triangular);
#endif
pLayout->addWidget(tabs, 0, 0);
propertyEditorView = new Gui::PropertyEditor::PropertyEditor();
+2 -2
View File
@@ -571,14 +571,14 @@ void AboutDialog::showLicenseInformation()
// Coin3D
li.name = QLatin1String("Coin3D");
li.href = baseurl + QLatin1String("#_TocCoin3D");
li.url = QLatin1String("https://bitbucket.org/Coin3D/coin/");
li.url = QLatin1String("https://coin3d.github.io");
li.version = QLatin1String(COIN_VERSION);
libInfo << li;
// Eigen3
li.name = QLatin1String("Eigen3");
li.href = baseurl + QLatin1String("#_TocEigen3");
li.url = QLatin1String("http://eigen.tuxfamily.org/");
li.url = QLatin1String("http://eigen.tuxfamily.org");
li.version.clear();
libInfo << li;
+12
View File
@@ -1552,6 +1552,18 @@ QPushButton:checked {
border-color: #65A2E5;
}
/*==================================================================================================
Tool button Icon fix in save dialogs
==================================================================================================*/
/* found under Tools -> Save Picture */ /* Draft -> ShapeString -> Font file */
QFileDialog#QFileDialog QToolButton {
background-color: transparent;
padding: 1px;
border: 1px;
margin: 0px;
}
/*==================================================================================================
Tool button inside QDialogs that works as QPushButtons
+12
View File
@@ -1519,6 +1519,18 @@ QPushButton:checked {
border-color: #3874f2;
}
/*==================================================================================================
Tool button Icon fix in save dialogs
==================================================================================================*/
/* found under Tools -> Save Picture */ /* Draft -> ShapeString -> Font file */
QFileDialog#QFileDialog QToolButton {
background-color: transparent;
padding: 1px;
border: 1px;
margin: 0px;
}
/*==================================================================================================
Tool button inside QDialogs that works as QPushButtons
+12
View File
@@ -1519,6 +1519,18 @@ QPushButton:checked {
border-color: #2053c0;
}
/*==================================================================================================
Tool button Icon fix in save dialogs
==================================================================================================*/
/* found under Tools -> Save Picture */ /* Draft -> ShapeString -> Font file */
QFileDialog#QFileDialog QToolButton {
background-color: transparent;
padding: 1px;
border: 1px;
margin: 0px;
}
/*==================================================================================================
Tool button inside QDialogs that works as QPushButtons
+11
View File
@@ -1519,6 +1519,17 @@ QPushButton:checked {
border-color: #819c0c;
}
/*==================================================================================================
Tool button Icon fix in save dialogs
==================================================================================================*/
/* found under Tools -> Save Picture */ /* Draft -> ShapeString -> Font file */
QFileDialog#QFileDialog QToolButton {
background-color: transparent;
padding: 1px;
border: 1px;
margin: 0px;
}
/*==================================================================================================
Tool button inside QDialogs that works as QPushButtons
+12
View File
@@ -1519,6 +1519,18 @@ QPushButton:checked {
border-color: #d0970c;
}
/*==================================================================================================
Tool button Icon fix in save dialogs
==================================================================================================*/
/* found under Tools -> Save Picture */ /* Draft -> ShapeString -> Font file */
QFileDialog#QFileDialog QToolButton {
background-color: transparent;
padding: 1px;
border: 1px;
margin: 0px;
}
/*==================================================================================================
Tool button inside QDialogs that works as QPushButtons
+12
View File
@@ -1519,6 +1519,18 @@ QPushButton:checked {
border-color: #2053c0;
}
/*==================================================================================================
Tool button Icon fix in save dialogs
==================================================================================================*/
/* found under Tools -> Save Picture */ /* Draft -> ShapeString -> Font file */
QFileDialog#QFileDialog QToolButton {
background-color: transparent;
padding: 1px;
border: 1px;
margin: 0px;
}
/*==================================================================================================
Tool button inside QDialogs that works as QPushButtons
+12
View File
@@ -1519,6 +1519,18 @@ QPushButton:checked {
border-color: #74831d;
}
/*==================================================================================================
Tool button Icon fix in save dialogs
==================================================================================================*/
/* found under Tools -> Save Picture */ /* Draft -> ShapeString -> Font file */
QFileDialog#QFileDialog QToolButton {
background-color: transparent;
padding: 1px;
border: 1px;
margin: 0px;
}
/*==================================================================================================
Tool button inside QDialogs that works as QPushButtons
+12
View File
@@ -1519,6 +1519,18 @@ QPushButton:checked {
border-color: #b28416;
}
/*==================================================================================================
Tool button Icon fix in save dialogs
==================================================================================================*/
/* found under Tools -> Save Picture */ /* Draft -> ShapeString -> Font file */
QFileDialog#QFileDialog QToolButton {
background-color: transparent;
padding: 1px;
border: 1px;
margin: 0px;
}
/*==================================================================================================
Tool button inside QDialogs that works as QPushButtons
+12
View File
@@ -1516,6 +1516,18 @@ QPushButton:checked {
border-color: #3874f2;
}
/*==================================================================================================
Tool button Icon fix in save dialogs
==================================================================================================*/
/* found under Tools -> Save Picture */ /* Draft -> ShapeString -> Font file */
QFileDialog#QFileDialog QToolButton {
background-color: transparent;
padding: 1px;
border: 1px;
margin: 0px;
}
/*==================================================================================================
Tool button inside QDialogs that works as QPushButtons
+12
View File
@@ -1516,6 +1516,18 @@ QPushButton:checked {
border-color: #819c0c;
}
/*==================================================================================================
Tool button Icon fix in save dialogs
==================================================================================================*/
/* found under Tools -> Save Picture */ /* Draft -> ShapeString -> Font file */
QFileDialog#QFileDialog QToolButton {
background-color: transparent;
padding: 1px;
border: 1px;
margin: 0px;
}
/*==================================================================================================
Tool button inside QDialogs that works as QPushButtons
+12
View File
@@ -1516,6 +1516,18 @@ QPushButton:checked {
border-color: #d0970c;
}
/*==================================================================================================
Tool button Icon fix in save dialogs
==================================================================================================*/
/* found under Tools -> Save Picture */ /* Draft -> ShapeString -> Font file */
QFileDialog#QFileDialog QToolButton {
background-color: transparent;
padding: 1px;
border: 1px;
margin: 0px;
}
/*==================================================================================================
Tool button inside QDialogs that works as QPushButtons
+329 -130
View File
@@ -24,7 +24,9 @@ INSTALLATION
WINDOWS = C:/[INSTALLATION_PATH]/FreeCAD/data/Gui/Stylesheets/
LINUX = /home/[YOUR_USER_NAME]/.FreeCAD/Gui/Stylesheets/
============================================================================================================
============================================================================================================
THESE COLOURS WERE USED AS TEMP SCRATCHPAD FOR DESIGNING. PLEASE DISREGARD!
BACKGROUND (darker to lighter)
black
#1e1e1e
@@ -74,10 +76,23 @@ QToolBar * {
padding: 0px;
}
/*==================================================================================================
Style Links
==================================================================================================*/
QLabel[haslink="true"] {
color: #55aaff;
}
Gui--UrlLabel {
color: #55aaff;
}
/*==================================================================================================
Main window
==================================================================================================*/
QWidget {
background-color: #333333;
}
QMainWindow,
QDialog,
QDockWidget,
@@ -130,6 +145,10 @@ QToolBox::tab:hover
/*==================================================================================================
QStatusBar
==================================================================================================*/
QStatusBar > QLabel {
margin-left: 4px;
}
QStatusBar::item {
border: 1px solid #333333;
@@ -207,7 +226,7 @@ QMenu QToolButton:pressed,
QMenu QPushButton:selected,
QMenu QToolButton:selected {
color: white;
background-color: #696969; /* same as QMenu::item:selected and QMenu::item:pressed */
background-color: #557bb6; /* same as QMenu::item:selected and QMenu::item:pressed */
}
QMenu QRadioButton:disabled,
@@ -271,9 +290,9 @@ QToolBar::separator:vertical {
Group box
==================================================================================================*/
QGroupBox {
color: rgba(255,255,255,120);
color: #bcbcbc;
border:1px solid rgba(255,255,255,20); /* lighter than its own border-color */;
border-radius: 2px;
border-radius: 1px;
margin-top: 10px;
padding: 6px;
background-color: rgba(255,255,255,0);
@@ -300,10 +319,10 @@ Tooltip
==================================================================================================*/
QToolTip {
color: #ffffff;
background-color: #1e1e1e;
background-color: #2a2a2a;
/*opacity: 90%; doesn't correctly work */
padding: 4px;
border-radius: 2px; /* has no effect */
border-radius: 1px; /* has no effect */
}
@@ -319,17 +338,17 @@ QDockWidget {
QDockWidget::title {
text-align: center;
background-color: rgba(0,0,0,40);
border: 4px solid #333333; /* fix to simulate margin between this :title and tabs */ /* same as main background color */
border-radius: 6px; /* bigger than normal due to previous border fix */
padding: 4px 0px; /* also needed because of previous border fix */
background-color: #2a2a2a;
border-bottom: 4px solid #333333; /* fix to simulate margin between this :title and tabs */ /* same as main background color */
margin-left: 6px;
margin-right: 6px;
}
QDockWidget::close-button,
QDockWidget::float-button {
border: none;
background: transparent;
border-radius: 2px;
border-radius: 1px;
subcontrol-origin: padding;
subcontrol-position: right center;
}
@@ -344,18 +363,18 @@ QDockWidget::float-button {
QDockWidget::close-button:hover,
QDockWidget::float-button:hover {
background-color: rgba(0,0,0,60);
background-color: #557bb6;
}
QDockWidget::close-button:pressed,
QDockWidget::float-button:pressed {
background-color: rgba(0,0,0,120);
background-color: #42608d;
border: 2px solid #76acfd
}
/* fix for Python Console (probably there is a smarter way to arrive to it) */
QDockWidget > QFrame {
background-color: #3C3C3C;
background-color: #3c3c3c;
border: 6px solid #333333;
}
@@ -366,16 +385,17 @@ Progress bar
QProgressBar,
QProgressBar:horizontal {
color: white;
min-height: 24px;
background-color: rgba(0,0,0,70);
text-align: center;
border: 1px solid rgba(0,0,0,140);
padding: 1px;
border-radius: 2px;
border-radius: 1px;
}
QProgressBar::chunk,
QProgressBar::chunk:horizontal {
background-color: qlineargradient(spread:pad, x1:1, y1:0.545, x2:1, y2:0, stop:0 #3874f2, stop:1 #557BB6);
border-radius: 2px;
background-color: #557BB6;
border-radius: 1px;
}
@@ -383,7 +403,7 @@ QProgressBar::chunk:horizontal {
Scroll
==================================================================================================*/
QAbstractScrollArea {
border-radius: 2px;
border-radius: 1px;
background-color: transparent;
}
@@ -410,7 +430,7 @@ QScrollBar::handle:horizontal:hover {
QScrollBar::handle:horizontal {
min-width: 5px;
border-radius: 2px;
border-radius: 1px;
margin: 4px 15px;
}
@@ -435,11 +455,13 @@ QScrollBar::add-line:horizontal {
QScrollBar::sub-line:horizontal:hover,
QScrollBar::sub-line:horizontal:on {
border-image: url(qss:images_dark-light/left_arrow_lighter.svg);
background-color: #557BB6;
}
QScrollBar::add-line:horizontal:hover,
QScrollBar::add-line:horizontal:on {
border-image: url(qss:images_dark-light/right_arrow_lighter.svg);
background-color: #557BB6;
}
QScrollBar::up-arrow:horizontal,
@@ -460,7 +482,7 @@ QScrollBar:vertical {
QScrollBar::handle:vertical {
min-height: 24px;
border-radius: 2px;
border-radius: 1px;
margin: 15px 4px;
}
@@ -485,11 +507,13 @@ QScrollBar::add-line:vertical {
QScrollBar::sub-line:vertical:hover,
QScrollBar::sub-line:vertical:on {
border-image: url(qss:images_dark-light/up_arrow_lighter.svg);
background-color: #557BB6;
}
QScrollBar::add-line:vertical:hover,
QScrollBar::add-line:vertical:on {
border-image: url(qss:images_dark-light/down_arrow_lighter.svg);
background-color: #557BB6;
}
QScrollBar::up-arrow:vertical,
@@ -507,7 +531,7 @@ QScrollBar::sub-page:vertical {
Tab bar
==================================================================================================*/
QTabWidget::pane {
background-color: #333333; /* temporal (transparent background) */ /* tab content background color */ /* was transparent. fixes no color undocked Combo View */
background-color: transparent; /* temporal (transparent background) */ /* tab content background color */ /* was transparent. fixes no color undocked Combo View */
position: absolute;
}
@@ -543,7 +567,7 @@ QTabWidget::tab-bar:right {
QTabBar {
qproperty-drawBase: 0; /* important */
background-color: #333333; /* Hack for Undocked white background - was transparent*/
background-color: transparent; /* Hack for Undocked white background - was transparent*/
}
/* Workaround for QTabBars created from docked QDockWidgets which don't draw the border if not set and reset as follows: */
@@ -642,12 +666,13 @@ QDialog#Gui__Dialog__DlgPreferences QTabWidget::pane {
/* hack to correctly align Preferences icon list on OSX */
QDialog#Gui__Dialog__DlgPreferences > QListView {
min-width: 130px;
min-width: 108px; /* narrowed for new smaller icons - was 130px*/
max-width: 108px;
}
/* unique styles for sections inside Preferences */
QDialog#Gui__Dialog__DlgPreferences > QListView::item {
border-radius: 2px;
border-radius: 1px;
}
QDialog#Gui__Dialog__DlgPreferences > QListView::item:hover { /* Preference left icons*/
@@ -667,7 +692,7 @@ Tab bar buttons
QTabBar::close-button {
subcontrol-origin: margin;
subcontrol-position: center right; /* only works for Qt 4.6 and newer */;
border-radius: 2px;
border-radius: 1px;
background-image: url(qss:images_dark-light/close_light.svg);
background-position: center center;
background-repeat: none;
@@ -770,7 +795,7 @@ QTableView {
selection-color: #ffffff;
selection-background-color: #557BB6; /* should be similar to QListView::item selected background-color */
show-decoration-selected: 1; /* make the selection span the entire width of the view */
border-radius: 2px;
border-radius: 1px;
}
QListView::item:hover,
@@ -801,7 +826,7 @@ Gui--PropertyEditor--PropertyEditor > QWidget > QWidget > QLabel:disabled {
color: transparent;
background-color: transparent;
border: none;
border-radius: 2px;
border-radius: 1px;
margin: 0px;
padding: 0px;
}
@@ -820,8 +845,8 @@ Gui--PropertyEditor--PropertyEditor QAbstractSpinBox:disabled {
/* hack to hide weird redundant information inside cells with links and no editable data (but editable via "..." button) */
Gui--PropertyEditor--PropertyEditor > QWidget > QWidget > QLabel {
color: #557BB6;
background-color: #696969; /* same as focused background color */
color: #949494;
background-color: #2a2a2a; /* same as focused background color */
}
/* hack to disable margin inside Property values to following elements */
@@ -874,7 +899,7 @@ QTreeView > QWidget > QTimeEdit:down-button,
QTreeView > QWidget > QDateEdit:down-button,
QTreeView > QWidget > QDateTimeEdit:down-button,
QTreeView > QWidget > Gui--ColorButton {
border-radius: 2px;
border-radius: 1px;
}
/* set focus colors to best viewing the editable fields */
@@ -907,7 +932,7 @@ QTreeView > QWidget > QDateTimeEdit:read-only {
/* Fix to correctly (not totally) draw QTextEdit on OSX at Page properties: "Page result", "Template" and "Editable Texts" */
Gui--PropertyEditor--PropertyEditor > QWidget > QWidget > QWidget {
min-height: 14px;
border-radius: 2px; /* reset */
border-radius: 1px; /* reset */
}
@@ -917,8 +942,8 @@ Header of tree and list views
QHeaderView {
color: #d2d2d2;
background-color: #2a2a2a;
border-top-left-radius: 2px; /* 1px less than its container */
border-top-right-radius: 2px; /* 1px less than its container */
border-top-left-radius: 1px; /* 1px less than its container */
border-top-right-radius: 1px; /* 1px less than its container */
border-bottom-left-radius: 0px;
border-bottom-right-radius: 0px;
}
@@ -1057,7 +1082,7 @@ Text/Python editor (macros, etc...)
==================================================================================================*/
QPlainTextEdit,
QPlainTextEdit:focus {
background-color: #3C3C3C; /* Python Console */
background-color: #3c3c3c; /* Python Console */
selection-color: #f5f5f5;
selection-background-color: #557BB6;
border: 6px solid #333333;
@@ -1181,12 +1206,12 @@ QSint--ActionGroup QFrame[class="content"] > QWidget > QPushButton {
margin: 0px;
}
/* Fix for lists inside task panels */
/* Fix for lists inside task panels */ /* sketcher constraints list */
QSint--ActionGroup QFrame[class="content"] QTreeView,
QSint--ActionGroup QFrame[class="content"] QListView,
QSint--ActionGroup QFrame[class="content"] QTableView {
color: #f5f5f5;
background-color: #3C3C3C;
background-color: #494949;
}
@@ -1194,6 +1219,24 @@ QSint--ActionGroup QFrame[class="content"] QTableView {
Buttons
==================================================================================================*/
/* Common */
QToolBar > Gui--WorkbenchComboBox {
color: #f5f5f5;
background-color: #2a2a2a; /* workbench picker and drop-down */
selection-color: #ffffff;
selection-background-color: #2a2a2a;
border: 1px solid #2a2a2a;
border-radius: 1px;
min-width: 50px; /* it ensures the default value is correctly displayed */
min-height: 16px; /* important to be a pair number in order to up/down buttons to be divisible by two (if not set could create a blank line in Ubuntu. Its downside is that it's needed to reset it (min-width: 0px) on following elements that can't have it such as fields inside QToolBar and inside QTreeView (Property editor) */
padding: 1px 2px; /* temporal: could don't be compatible with elements inside Tree/List view */
}
QToolBar > Gui--WorkbenchComboBox:!editable {
color: #f5f5f5;
font-weight: bold;
background-color: #557bb6; /* workbench disabled color */
}
QComboBox,
QAbstractSpinBox,
QSpinBox,
@@ -1204,13 +1247,13 @@ QTimeEdit,
QDateEdit,
QDateTimeEdit {
color: #f5f5f5;
background-color: #696969;
background-color: #494949; /* lineedits and drop-downs */
selection-color: #ffffff;
selection-background-color: #557BB6;
selection-background-color: #557bb6;
border: 0px solid #2a2a2a;
border-radius: 2px;
border-radius: 1px;
min-width: 50px; /* it ensures the default value is correctly displayed */
min-height: 20px; /* important to be a pair number in order to up/down buttons to be divisible by two (if not set could create a blank line in Ubuntu. Its downside is that it's needed to reset it (min-width: 0px) on following elements that can't have it such as fields inside QToolBar and inside QTreeView (Property editor) */
min-height: 16px; /* important to be a pair number in order to up/down buttons to be divisible by two (if not set could create a blank line in Ubuntu. Its downside is that it's needed to reset it (min-width: 0px) on following elements that can't have it such as fields inside QToolBar and inside QTreeView (Property editor) */
padding: 1px 2px; /* temporal: could don't be compatible with elements inside Tree/List view */
}
@@ -1234,7 +1277,7 @@ QDateTimeEdit {
QTextEdit:!editable,
QTextEdit:!editable:focus {
color: #f5f5f5;
background-color: #3C3C3C;
background-color: #3c3c3c;
border: 6px solid #333333;
border-radius: 0px;
margin: 0px;
@@ -1252,10 +1295,10 @@ QDateEdit:focus,
QDateTimeEdit:focus {
font-weight: bold;
color: #f5f5f5;
border-color: #3c3c3c;
border-color: #333333;
border: 1px;
border-right-color: #557BB6; /* same as up/down or drop-down button color */
background-color: #696969;
background-color: #494949;
}
QComboBox:disabled,
@@ -1267,8 +1310,8 @@ QTextEdit:disabled,
QTimeEdit:disabled,
QDateEdit:disabled,
QDateTimeEdit:disabled {
color: #f5f5f5;
background-color: #696969; /* same as enabled color */
color: #696969;
background-color: #494949; /* same as enabled color */
border-color: #2a2a2a; /* same as enabled color */
}
@@ -1315,7 +1358,7 @@ QDoubleSpinBox:up-button:focus,
QTimeEdit:up-button:focus,
QDateEdit:up-button:focus,
QDateTimeEdit:up-button:focus {
background-color: #557BB6;
background-color: #2a2a2a;
}
QAbstractSpinBox:down-button:focus,
@@ -1324,7 +1367,7 @@ QDoubleSpinBox:down-button:focus,
QTimeEdit:down-button:focus,
QDateEdit:down-button:focus,
QDateTimeEdit:down-button:focus {
background-color: #557BB6;
background-color: #2a2a2a;
}
QAbstractSpinBox:up-button:disabled,
@@ -1423,13 +1466,13 @@ QComboBox::drop-down {
subcontrol-origin: border; /* important */
subcontrol-position: top right;
width: 20px;
border-top-right-radius: 2px;
border-bottom-right-radius: 2px;
border-top-right-radius: 1px;
border-bottom-right-radius: 1px;
}
QComboBox::drop-down:on,
QComboBox::drop-down:focus {
background-color: #557BB6;
background-color: #2a2a2a;
}
QComboBox::down-arrow {
@@ -1454,7 +1497,7 @@ QComboBox {
QComboBox QAbstractItemView {
color: #bebebe; /* same as regular QComboBox color */
background-color: #2a2a2a; /* was transparent */
background-color: transparent; /* was transparent */
selection-color: #f5f5f5;
selection-background-color: #557BB6;
border-width: 5px 0px 5px 0px;
@@ -1470,38 +1513,153 @@ Push button
QPushButton {
color: #e0e0e0;
text-align: center;
background-color: qlineargradient(spread:pad, x1:0, y1:0.3, x2:0, y2:1, stop:0 #696969, stop:1 #696961); /* Middle Mouse Navigation Button and Ok Cancel Apply Help Preferences Buttons */
border: 2px;
border-color: #2a2a2a;
background-color: #2a2a2a;
padding: 4px 20px;
margin: 2px 2px;
margin-right: 5px;
min-height: 20px; /* same as QTabBar QPushButton min-width */
border-radius: 2px;
border: 1px solid #494949;
margin: 4px 4px;
border-radius: 1px;
}
QPushButton:hover,
QPushButton:focus {
color: #cbd8e6;
border-color: #2a2a2a;
background-color: #557BB6;
color: #ffffff;
background-color: #557bb6;
border: 1px solid #f5f5f5;
}
QPushButton:disabled,
QPushButton:disabled:checked {
color: #2a2a2a;
background-color: #696969; /* same as enabled color */
border-color: #2a2a2a; /* same as enabled color */
color: #f5f5f5;
background-color: #2a2a2a; /* same as enabled color */
border: 1px solid #2a2a2a; /* same as enabled color */
}
QPushButton:pressed {
background-color: #557BB6;
color: #ffffff;
background-color: #48699a;
border: 1px solid #3c3c3c;
}
QPushButton:checked {
background-color: #696969;
border-color: #557BB6;
background-color: #557BB6;
border: 1px solid #557BB6;
}
/* Inspect Widgets Addon */
QDockWidget#InspectWidgets QPushButton {
background-color: #2a2a2a;
border: 1px solid #3c3c3c;
min-height: 16px;
margin: 0px;
}
QDockWidget#InspectWidgets QPushButton:hover {
color: #ffffff;
background-color: #557bb6;
border: 1px solid #f5f5f5;
border-bottom: 1px solid #f5f5f5;
}
QDockWidget#InspectWidgets QPushButton:checked,
QDockWidget#InspectWidgets QPushButton:pressed {
background-color: #557bb6;
border: 1px solid #557bb6;
}
/* CAD Navigation Style */
QPushButton#NavigationIndicator {
background-color: #557bb6;
padding: 2px;
margin: 0px;
border: 1px solid #333333;
border-radius: 1px;
min-width: 90px;
min-height: 24px;
}
QPushButton:hover#NavigationIndicator {
color: #ffffff;
border: 1px solid #f5f5f5;
}
QPushButton:pressed#NavigationIndicator {
color: #ffffff;
background-color: #557bb6;
border: 1px solid #557bb6;
}
/* BIM Views Manager */
QWidget#Form QPushButton {
background-color: #333333;
padding: 4px 2px;
border: 1px solid #3c3c3c;
border-radius: 1px;
margin: 2px;
margin-bottom: 8px;
max-width: 100%;
min-width: 16px;
min-height: 24px;
}
QWidget#Form QPushButton:hover {
border: 1px solid #f5f5f5;
background-color: #557BB6;
}
QWidget#Form QPushButton:pressed {
border: 1px solid #557bb6;
background-color: #557BB6;
}
/* Sketcher Manual Update Button */
QPushButton#manualUpdate {
padding: 4px;
margin: 0px;
border: 1px solid #494949;
}
QPushButton:pressed#manualUpdate {
color: #ffffff;
border: 1px solid #3c3c3c;
background-color: #48699a;
}
/* Addon Manager */
QDialog#Dialog QPushButton {
padding: 4px;
margin: 0px;
border: 1px solid #494949;
}
QDialog#Dialog QPushButton:hover {
color: #ffffff;
border: 1px solid #3c3c3c;
background-color: #48699a;
}
QPushButton#buttonUninstall {
margin-left: 16px;
}
QPushButton#buttonClose {
margin-right: 8px;
}
/* Ok Cancel Apply Help Preferences Buttons */ /* Hack to move Help button left */
QDialogButtonBox > QPushButton {
color: #e0e0e0;
text-align: center;
background-color: #2a2a2a;
border: 1px solid #494949;
padding: 4px;
margin-right: 8px;
min-width: 60px;
}
/* Color Buttons */
@@ -1538,7 +1696,7 @@ Gui--PropertyEditor--PropertyEditor > QWidget > QWidget > QPushButton {
background-color: #2a2a2a;
border: 1px solid #1e1e1e;
min-width: 16px; /* reset it due to larger value on regular QPushButton, same or bigger value as regular QPushButton min-height */
border-radius: 2px;
border-radius: 1px;
margin: 0px; /* reset */
padding: 0px; /* reset */
}
@@ -1547,48 +1705,63 @@ Gui--PropertyEditor--PropertyEditor > QWidget > QWidget > QPushButton {
Gui--PropertyEditor--PropertyEditor > QWidget > QWidget > QWidget > QWidget > QFrame {
background-color: #333333; /* main background color */
border: 1px solid #333333;
border-radius: 2px;
border-radius: 1px;
padding: 2px 6px;
}
QPushButton:checked {
background-color: #696969;
border-color: #696969;
/*==================================================================================================
Tool button Icon fix in save dialogs
==================================================================================================*/
/* found under Tools -> Save Picture */ /* Draft -> ShapeString -> Font file */
QFileDialog#QFileDialog QToolButton {
background-color: transparent;
padding: 1px;
border: 1px;
margin: 0px;
}
QFileDialog#QFileDialog QToolButton:hover,
QFileDialog#QFileDialog QToolButton:focus {
color: #ffffff;
background-color: #557bb6;
border: 1px solid #f5f5f5;
}
/*==================================================================================================
Tool button inside QDialogs that works as QPushButtons
==================================================================================================*/
/* found under Tools -> Customize -> Macros -> Pixmap "..." button */
QDialog QToolButton {
color: #e0e0e0;
text-align: center;
background-color: qlineargradient(spread:pad, x1:0, y1:0.3, x2:0, y2:1, stop:0 #2a2a2a, stop:1 #1e1e1e);
border: 1px solid #1e1e1e;
border-bottom-color: black; /* simulates shadow under the button */
background-color: #2a2a2a;
border: 1px solid #494949;
padding: 0px; /* different than regular QPushButton */
margin: 2px; /* different than regular QPushButton */
margin: 2px;
min-height: 16px; /* same as QTabBar QPushButton min-width */
border-radius: 2px;
border-radius: 1px;
}
QDialog QToolButton:hover,
QDialog QToolButton:focus {
color: #cbd8e6;
border-color: #557BB6;
background-color: #557BB6;
color: #ffffff;
background-color: #557bb6;
border: 1px solid #f5f5f5;
}
QDialog QToolButton:disabled,
QDialog QToolButton:disabled:checked {
color: #333333;
border-color: #424242;
background-color: #424242;
color: #f5f5f5;
background-color: #2a2a2a;
border: 1px solid #2a2a2a;
}
QDialog QToolButton:pressed {
background-color: #557BB6;
color: #ffffff;
background-color: #48699a;
border: 1px solid #3c3c3c;
}
@@ -1599,26 +1772,26 @@ Tool button inside Task Panel content that works as QPushButtons
QSint--ActionGroup QFrame[class="content"] QToolButton {
color: #e0e0e0;
text-align: center;
background-color: qlineargradient(spread:pad, x1:0, y1:0.3, x2:0, y2:1, stop:0 #2a2a2a, stop:1 #1e1e1e);
border: 1px solid #1e1e1e;
background-color: #2a2a2a;
border: 1px solid #494949;
border-bottom-color: black; /* simulates shadow under the button */
padding: 2px 6px; /* different than regular QPushButton */
margin: 2px; /* different than regular QPushButton */
min-height: 16px; /* same as QTabBar QPushButton min-width */
border-radius: 2px;
border-radius: 1px;
}
QSint--ActionGroup QFrame[class="content"] QToolButton:hover,
QSint--ActionGroup QFrame[class="content"] QToolButton:focus {
color: white;
border-color: #557BB6;
border-color: solid #f5f5f5;
background-color: #557BB6;
}
QSint--ActionGroup QFrame[class="content"] QToolButton:disabled,
QSint--ActionGroup QFrame[class="content"] QToolButton:disabled:checked {
color: #333333;
border-color: #424242;
color: #f5f5f5;
border-color: #494949;
background-color: #424242;
}
@@ -1639,7 +1812,7 @@ QSint--ActionGroup QFrame[class="content"] QMenu::item {
}
QSint--ActionGroup QFrame[class="content"] QComboBox QAbstractItemView {
background-color: #696969;
background-color: #333333;
}
@@ -1673,7 +1846,7 @@ QRadioButton:disabled {
QRadioButton::indicator {
width: 12px;
height: 12px;
border-radius: 2px;
border-radius: 1px;
}
QRadioButton::indicator:pressed {
@@ -1838,7 +2011,7 @@ QSlider:vertical {
QSlider::groove {
background-color: #2a2a2a;
border: 2px solid #3c3c3c;
border-radius: 2px;
border-radius: 1px;
margin: 4px 0px;
}
@@ -1862,7 +2035,7 @@ QSlider::handle:vertical {
border: 1px solid #2a2a2a;
width: 16px;
height: 16px;
border-radius: 2px;
border-radius: 1px;
}
QSlider::handle:horizontal {
@@ -1918,39 +2091,48 @@ QToolBar > QDateTimeEdit {
}
QToolBar > QPushButton {
padding: 4px;
padding: 2px;
margin: 0px; /* doesn't work with :left, :right:, :top or :bottom sub-controls */
min-width: 24px; /* could not be larger due to switchable Preferences "Size of toolbar icons" */
min-height: 20px; /* could not be larger due to switchable Preferences "Size of toolbar icons" */
border-radius: 2px; /* same as regular QPushButton */
min-height: 24px; /* could not be larger due to switchable Preferences "Size of toolbar icons" */
border-radius: 1px; /* same as regular QPushButton */
}
QToolBar > QPushButton:checked {
border: 1px solid #333333;
border: 1px solid #3c3c3c;
background-color: #557BB6;
}
/* Hack to avoid QPushButton text partially hidden under menu-indicator */
QToolBar > QPushButton::menu-indicator:!checked {
image: none;
width: 0px;
}
QToolBar > QPushButton:!checked {
background-color: #333333; /* Current Working Plane and Nudge */
border: 1px solid #333333;
text-align: left;
padding: 2px 4px;
border: 1px solid #3c3c3c;
margin: 0px 2px;
}
QToolBar > QPushButton:checked:hover {
border-color: #557BB6;
border: 1px solid #f5f5f5;
}
QToolBar > QPushButton:!checked:hover {
color: #ffffff;
background-color: #557BB6;
border-color: #557BB6;
border: 1px solid #f5f5f5;
}
QToolBar > QPushButton:checked:pressed {
border: 1px solid #557bb6;
background-color: solid #557BB6;
}
QToolBar > QPushButton:!checked:pressed {
border: 1px solid #557bb6;
background-color: #557BB6;
}
@@ -1963,43 +2145,60 @@ QToolBar > QPushButton:!checked:disabled {
QToolBar > QToolButton {
margin: 2px;
padding: 2px;
border-radius: 2px;
border-radius: 1px;
border: 1px solid transparent;
}
QToolBar > QToolButton:hover {
background-color: #557BB6;
border: 1px solid #f5f5f5;
}
QToolBar > QToolButton:pressed {
background-color: #557BB6;
border: 1px solid #557bb6;
}
/* ToolBar menu buttons (buttons with drop-down menu) */
QToolBar > QToolButton#qt_toolbutton_menubutton {
padding-right: 20px; /* Hack to add more width to buttons with menu */
border: 1px solid transparent;
border-radius: 2px;
}
QToolBar > QToolButton#qt_toolbutton_menubutton:hover,
QToolBar > QToolButton#qt_toolbutton_menubutton:pressed,
QToolBar > QToolButton#qt_toolbutton_menubutton:open {
border: 1px solid #557BB6;
border: 1px solid #333333;
border-radius: 1px;
}
QToolBar QToolButton::menu-button,
QToolBar > QToolButton#qt_toolbutton_menubutton::menu-button {
border: none;
border-top-right-radius: 3px;
border-bottom-right-radius: 3px;
border-top-right-radius: 1px;
border-bottom-right-radius: 1px;
width: 16px; /* 16px width + 4px for border = 20px allocated above */
outline: none;
background-color: transparent;
}
QToolBar > QToolButton#qt_toolbutton_menubutton::menu-button:hover,
QToolBar > QToolButton#qt_toolbutton_menubutton::menu-button:hover {
border-top: 1px solid #f5f5f5;
border-bottom: 1px solid #f5f5f5;
border-right: 1px solid #f5f5f5;
background-color: #557BB6;
}
QToolBar > QToolButton#qt_toolbutton_menubutton:pressed,
QToolBar > QToolButton#qt_toolbutton_menubutton:open {
background-color: #557BB6;
border: 1px solid #557BB6;
}
QToolBar > QToolButton#qt_toolbutton_menubutton:hover {
background-color: #557BB6;
border: 1px solid #f5f5f5;
}
QToolBar > QToolButton#qt_toolbutton_menubutton::menu-button:pressed,
QToolBar > QToolButton#qt_toolbutton_menubutton::menu-button:open {
border-top: 1px solid #557bb6;
border-bottom: 1px solid #557bb6;
border-right: 1px solid #557bb6;
background-color: #557BB6;
}
@@ -2058,14 +2257,14 @@ QToolBar QToolButton#qt_toolbar_ext_button:on {
Tables (spreadsheets)
==================================================================================================*/
QTableView {
gridline-color: #696969;
gridline-color: #a8a8a8;
selection-color: #ffffff;
selection-background-color: #2D5C9A; /* Default Spreadsheet cell selection background color */
background-color: #a0a0a0; /* Default Spreadsheet cell background color */
selection-background-color: #557bb6; /* Default Spreadsheet cell selection background color */
background-color: #cecece; /* Default Spreadsheet cell background color */
}
QTableView::item:hover {
background-color: rgba(0,0,0,10); /* temporal: is it displayed in Linux or Windows? on OSX it isn't */
background-color: #557bb6; /* temporal: is it displayed in Linux or Windows? on OSX it isn't */
}
QTableView::item:disabled {
@@ -2074,7 +2273,7 @@ QTableView::item:disabled {
QTableView::item:selected {
color: #a0a0a0;
border-color: #cbd8e6; /* same as focused background color */
border-color: #cecece; /* same as focused background color */
border-bottom-color: #557BB6; /* same as focused border color */
}
@@ -2102,7 +2301,7 @@ QTableView > QWidget > QTimeEdit:down-button,
QTableView > QWidget > QDateEdit:down-button,
QTableView > QWidget > QDateTimeEdit:down-button,
QTableView > QWidget > Gui--ColorButton {
border-radius: 2px;
border-radius: 1px;
}
QTableView > QWidget > QComboBox,
@@ -2114,7 +2313,7 @@ QTableView > QWidget > QTextEdit,
QTableView > QWidget > QTimeEdit,
QTableView > QWidget > QDateEdit,
QTableView > QWidget > QDateTimeEdit {
color: black;
color: #f5f5f5;
background-color: transparent;
border-color: transparent;
}
@@ -2145,11 +2344,11 @@ QTableView > QWidget > QTextEdit:focus,
QTableView > QWidget > QTimeEdit:focus,
QTableView > QWidget > QDateEdit:focus,
QTableView > QWidget > QDateTimeEdit:focus {
color: #2a2a2a;
selection-color: white;
color: #000000;
selection-color: #f5f5f5;
selection-background-color: #557BB6;
border-color: #cbd8e6;
background-color: #cbd8e6;
border-color: #3c3c3c;
background-color: #cecece;
}
QTableView > QWidget > QComboBox:disabled,
@@ -2161,7 +2360,7 @@ QTableView > QWidget > QTextEdit:disabled,
QTableView > QWidget > QTimeEdit:disabled,
QTableView > QWidget > QDateEdit:disabled,
QTableView > QWidget > QDateTimeEdit:disabled {
color: rgba(0,0,0,120);
color: #f5f5f5;
background-color: transparent;
border-color: transparent;
}
@@ -2175,7 +2374,7 @@ QTableView > QWidget > QTextEdit:read-only,
QTableView > QWidget > QTimeEdit:read-only,
QTableView > QWidget > QDateEdit:read-only,
QTableView > QWidget > QDateTimeEdit:read-only {
color: black;
color: #f5f5f5;
background-color: transparent;
border-color: transparent;
}
@@ -2228,7 +2427,7 @@ QToolBar#Selector QToolButton {
border: none;
margin: 0px;
padding: 2px 6px;
border-radius: 2px;
border-radius: 1px;
}
/* Active tab */
+4
View File
@@ -146,6 +146,10 @@ int main( int argc, char ** argv )
#endif
#if defined (FC_OS_WIN32)
// we need to force Coin not to use Freetype in order to find installed fonts on Windows
// see https://forum.freecadweb.org/viewtopic.php?p=485142#p485016
_putenv("COIN_FORCE_FREETYPE_OFF=1");
int argc_ = argc;
QVector<QByteArray> data;
QVector<char *> argv_;
+12 -10
View File
@@ -248,26 +248,28 @@ def restart_freecad():
QtCore.QProcess.startDetached(QtGui.QApplication.applicationFilePath(), args)
def get_zip_url(baseurl):
def get_zip_url(baseurl, branch="master"):
"Returns the location of a zip file from a repo, if available"
url = getserver(baseurl).strip("/")
if url.endswith("github.com"):
return baseurl+"/archive/master.zip"
elif url.endswith("framagit.org") or url.endswith("gitlab.com"):
return baseurl+"/archive/" + branch + ".zip"
elif url.endswith("framagit.org") or url.endswith("gitlab.com") or url.endswith("salsa.debian.org"):
# https://framagit.org/freecad-france/mooc-workbench/-/archive/master/mooc-workbench-master.zip
reponame = baseurl.strip("/").split("/")[-1]
return baseurl+"/-/archive/master/"+reponame+"-master.zip"
return baseurl+"/-/archive/" + branch + "/"+reponame+"-" + branch + ".zip"
else:
print("Debug: addonmanager_utilities.get_zip_url: Unknown git host:", url)
return None
def get_readme_url(url):
def get_readme_url(url, branch="master"):
"Returns the location of a readme file"
if "github" in url or "framagit" in url or "gitlab" in url:
return url+"/raw/master/README.md"
if "github" in url or "framagit" in url:
return url+"/raw/" + branch + "/README.md"
elif "gitlab" in url or "salsa.debian.org" in url:
return url+"/-/raw/" + branch + "/README.md"
else:
print("Debug: addonmanager_utilities.get_readme_url: Unknown git host:", url)
return None
@@ -279,17 +281,17 @@ def get_desc_regex(url):
if "github" in url:
return r'<meta property="og:description" content="(.*?)"'
elif "framagit" in url or "gitlab" in url:
elif "framagit" in url or "gitlab" in url or "salsa.debian.org" in url:
return r'<meta.*?content="(.*?)".*?og:description.*?>'
print("Debug: addonmanager_utilities.get_desc_regex: Unknown git host:", url)
return None
def get_readme_html_url(url):
def get_readme_html_url(url, branch="master"):
"""Returns the location of a html file containing readme"""
if "github" in url:
return url + "/blob/master/README.md"
return url + "/blob/" + branch + "/README.md"
else:
print("Debug: addonmanager_utilities.get_readme_html_url: Unknown git host:", url)
return None
+37 -11
View File
@@ -82,6 +82,8 @@ NOGIT = False # for debugging purposes, set this to True to always use http dow
NOMARKDOWN = False # for debugging purposes, set this to True to disable Markdown lib
url_to_branch_map = {} # Added to 0.19.3 to support branches with names other than "master"
"""Multithread workers for the Addon Manager"""
@@ -137,12 +139,13 @@ class UpdateWorker(QtCore.QThread):
u.close()
p = re.findall((r'(?m)\[submodule\s*"(?P<name>.*)"\]\s*'
r"path\s*=\s*(?P<path>.+)\s*"
r"url\s*=\s*(?P<url>https?://.*)"), p)
r"url\s*=\s*(?P<url>https?://.*)\s*"
r"(branch\s*=\s*(?P<branch>.*)\s*)?"), p)
basedir = FreeCAD.getUserAppDataDir()
moddir = basedir + os.sep + "Mod"
repos = []
# querying official addons
for name, path, url in p:
for name, path, url, _, branch in p:
self.info_label.emit(name)
url = url.split(".git")[0]
addondir = moddir + os.sep + name
@@ -152,11 +155,20 @@ class UpdateWorker(QtCore.QThread):
else:
state = 0
repos.append([name, url, state])
if branch is None or len(branch) == 0:
branch = "master"
url_to_branch_map[url] = branch
# querying custom addons
customaddons = (FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Addons")
.GetString("CustomRepositories", "").split("\n"))
for url in customaddons:
if url:
if " " in url:
addon_and_branch = addon.split(" ")
url = addon_and_branch[0]
branch = addon_and_branch[1]
else:
branch = "master"
name = url.split("/")[-1]
if name.lower().endswith(".git"):
name = name[:-4]
@@ -166,6 +178,7 @@ class UpdateWorker(QtCore.QThread):
else:
state = 1
repos.append([name, url, state])
url_to_branch_map[url] = branch
if not repos:
self.info_label.emit(translate("AddonsInstaller", "Unable to download addon list."))
else:
@@ -262,11 +275,15 @@ class CheckWBWorker(QtCore.QThread):
except Exception:
print("AddonManager: Unable to fetch git updates for repo", repo[0])
else:
if "git pull" in gitrepo.status():
self.mark.emit(repo[0])
upds.append(repo[0])
# mark as already installed AND already checked for updates AND update available
self.repos[self.repos.index(repo)][2] = 3
try:
if "git pull" in gitrepo.status():
self.mark.emit(repo[0])
upds.append(repo[0])
# mark as already installed AND already checked for updates AND update available
self.repos[self.repos.index(repo)][2] = 3
except stderr:
FreeCAD.Console.PrintWarning("AddonManager - " + repo[0] + " git status"
" fatal: this operation must be run in a work tree \n")
self.addon_repos.emit(self.repos)
self.enable.emit(len(upds))
self.stop = True
@@ -382,17 +399,22 @@ class ShowWorker(QtCore.QThread):
self.progressbar_show.emit(True)
self.info_label.emit(translate("AddonsInstaller", "Retrieving description..."))
if len(self.repos[self.idx]) == 4:
desc = self.repos[self.idx][3]
else:
u = None
url = self.repos[self.idx][1]
if url in url_to_branch_map:
branch = url_to_branch_map[url]
else:
branch = "master"
self.info_label.emit(translate("AddonsInstaller", "Retrieving info from") + " " + str(url))
desc = ""
regex = utils.get_readme_regex(url)
if regex:
# extract readme from html via regex
readmeurl = utils.get_readme_html_url(url)
readmeurl = utils.get_readme_html_url(url,branch)
if not readmeurl:
print("Debug: README not found for", url)
u = utils.urlopen(readmeurl)
@@ -411,7 +433,7 @@ class ShowWorker(QtCore.QThread):
print("Debug: README not found at", readmeurl)
else:
# convert raw markdown using lib
readmeurl = utils.get_readme_url(url)
readmeurl = utils.get_readme_url(url,branch)
if not readmeurl:
print("Debug: README not found for", url)
u = utils.urlopen(readmeurl)
@@ -421,7 +443,7 @@ class ShowWorker(QtCore.QThread):
p = p.decode("utf-8")
u.close()
desc = utils.fix_relative_links(p, readmeurl.rsplit("/README.md")[0])
if NOMARKDOWN or not have_markdown:
if have_markdown and not NOMARKDOWN:
desc = markdown.markdown(desc, extensions=["md_in_html"])
else:
message = """
@@ -863,7 +885,11 @@ class InstallWorker(QtCore.QThread):
shutil.rmtree(bakdir)
os.rename(clonedir, bakdir)
os.makedirs(clonedir)
zipurl = utils.get_zip_url(baseurl)
if baseurl in url_to_branch_map:
branch = url_to_branch_map[baseurl]
else:
branch = "master"
zipurl = utils.get_zip_url(baseurl, branch)
if not zipurl:
return translate("AddonsInstaller", "Error: Unable to locate zip from") + " " + baseurl
try:
+11 -9
View File
@@ -645,18 +645,20 @@ class _ArchMaterialTaskPanel:
self.material["Father"] = text
def getColor(self):
"opens a color picker dialog"
color = QtGui.QColorDialog.getColor()
colorPix = QtGui.QPixmap(16,16)
colorPix.fill(color)
self.form.ButtonColor.setIcon(QtGui.QIcon(colorPix))
self.getColorForButton(self.form.ButtonColor)
def getSectionColor(self):
self.getColorForButton(self.form.ButtonSectionColor)
def getColorForButton(self,button):
"opens a color picker dialog"
color = QtGui.QColorDialog.getColor()
colorPix = QtGui.QPixmap(16,16)
colorPix.fill(color)
self.form.ButtonSectionColor.setIcon(QtGui.QIcon(colorPix))
icon = button.icon()
pixel = icon.pixmap(16,16).toImage().pixel(0,0)
color = QtGui.QColorDialog.getColor(QtGui.QColor(pixel))
if color.isValid():
colorPix = QtGui.QPixmap(16,16)
colorPix.fill(color)
button.setIcon(QtGui.QIcon(colorPix))
def fillMaterialCombo(self):
"fills the combo with the existing FCMat cards"
+7 -5
View File
@@ -211,11 +211,11 @@ def getCutShapes(objs,cutplane,onlySolids,clip,joinArch,showHidden,groupSshapesB
cutface,cutvolume,invcutvolume = ArchCommands.getCutVolume(cutplane,shapes,clip)
shapes = []
if cutvolume:
for o, shapeList in objectShapes:
tmpSshapes = []
for sh in shapeList:
for sol in sh.Solids:
for o, shapeList in objectShapes:
tmpSshapes = []
for sh in shapeList:
for sol in sh.Solids:
if cutvolume:
if sol.Volume < 0:
sol.reverse()
c = sol.cut(cutvolume)
@@ -235,6 +235,8 @@ def getCutShapes(objs,cutplane,onlySolids,clip,joinArch,showHidden,groupSshapesB
if showHidden:
c = sol.cut(invcutvolume)
hshapes.append(c)
else:
shapes.extend(sol.Solids)
if len(tmpSshapes) > 0:
sshapes.extend(tmpSshapes)
+9 -2
View File
@@ -632,7 +632,10 @@ class _Window(ArchComponent.Component):
elif "Edge" in s:
hinge = int(s[4:])-1
elif "Mode" in s:
omode = int(s[-1])
omode = int(s[4:])
if omode >= len(WindowOpeningModes):
# Ignore modes not listed in WindowOpeningModes
omode = None
if wires:
max_length = 0
for w in wires:
@@ -1506,7 +1509,11 @@ class _ArchWindowTaskPanel:
elif "Edge" in l:
self.field6.setText(l)
elif "Mode" in l:
self.field7.setCurrentIndex(int(l[-1]))
if int(l[4:]) < len(WindowOpeningModes):
self.field7.setCurrentIndex(int(l[4:]))
else:
# Ignore modes not listed in WindowOpeningModes
self.field7.setCurrentIndex(0)
if wires:
f.setText(",".join(wires))
@@ -340,6 +340,7 @@ def init_draft_statusbar_snap():
# add snap widget to the statusbar
sb.insertPermanentWidget(2, snap_widget)
snap_widget.setOrientation(QtCore.Qt.Orientation.Horizontal)
snap_widget.show()
@@ -373,11 +374,13 @@ def show_draft_statusbar():
snap_widget = sb.findChild(QtGui.QToolBar,"draft_snap_widget")
if snap_widget:
snap_widget.setOrientation(QtCore.Qt.Orientation.Horizontal)
snap_widget.show()
else:
snap_widget = mw.findChild(QtGui.QToolBar,"draft_snap_widget")
if snap_widget:
sb.insertPermanentWidget(2, snap_widget)
snap_widget.setOrientation(QtCore.Qt.Orientation.Horizontal)
snap_widget.show()
elif params.GetBool("DisplayStatusbarSnapWidget", True):
t = QtCore.QTimer()
+4 -2
View File
@@ -55,7 +55,7 @@ import six
import FreeCAD
import Part, Draft, Mesh
import DraftVecUtils, DraftGeomUtils, WorkingPlane
from Draft import _Dimension, _ViewProviderDimension
from Draft import _Dimension
from FreeCAD import Vector
from FreeCAD import Console as FCC
@@ -2573,7 +2573,9 @@ def processdxf(document, filename, getShapes=False, reComputeFlag=True):
newob = doc.addObject("App::FeaturePython", "Dimension")
lay.addObject(newob)
_Dimension(newob)
_ViewProviderDimension(newob.ViewObject)
if FreeCAD.GuiUp:
from Draft import _ViewProviderDimension
_ViewProviderDimension(newob.ViewObject)
newob.Start = p1
newob.End = p2
newob.Dimline = pt
+1 -1
View File
@@ -246,7 +246,7 @@
<double>2.000000000000000</double>
</property>
<property name="maximum" stdset="0">
<double>2000000000.000000000000000</double>
<double>90000000000.000000000000000</double>
</property>
<property name="unit" stdset="0">
<string notr="true">Pa</string>
+1 -1
View File
@@ -162,7 +162,7 @@ def read(
or fileExtension.lower() == ".yml"
) and has_yaml:
fp = pyopen(fileString, "rt")
raw_mesh_data = yaml.load(fp)
raw_mesh_data = yaml.load(fp, Loader=yaml.SafeLoader)
fp.close()
else:
Console.PrintError(
@@ -30,6 +30,12 @@ __url__ = "https://www.freecadweb.org"
# \ingroup FEM
# \brief task panel for mechanical ResultObjectPython
try:
import matplotlib
matplotlib.use("Qt5Agg")
except Exception:
print("Failed to set matplotlib backend to Qt5Agg")
import matplotlib.pyplot as plt
import numpy as np
+2 -1
View File
@@ -82,6 +82,8 @@
#include "ImpExpDxf.h"
namespace Import {
class ImportOCAFExt : public Import::ImportOCAF2
{
public:
@@ -98,7 +100,6 @@ private:
}
};
namespace Import {
class Module : public Py::ExtensionModule<Module>
{
public:
+18 -23
View File
@@ -58,7 +58,7 @@
# include <TDF_LabelSequence.hxx>
# include <TDF_ChildIterator.hxx>
# include <TDataStd_Name.hxx>
# include <Quantity_Color.hxx>
# include <Quantity_ColorRGBA.hxx>
# include <STEPCAFControl_Reader.hxx>
# include <STEPControl_Writer.hxx>
# include <IGESCAFControl_Reader.hxx>
@@ -95,6 +95,17 @@
#include <App/DocumentObject.h>
#include <App/DocumentObjectGroup.h>
#if OCC_VERSION_HEX >= 0x070500
// See https://dev.opencascade.org/content/occt-3d-viewer-becomes-srgb-aware
# define OCC_COLOR_SPACE Quantity_TOC_sRGB
#else
# define OCC_COLOR_SPACE Quantity_TOC_RGB
#endif
static inline Quantity_ColorRGBA convertColor(const App::Color &c)
{
return Quantity_ColorRGBA(Quantity_Color(c.r, c.g, c.b, OCC_COLOR_SPACE), 1.0 - c.a);
}
using namespace Import;
@@ -284,7 +295,7 @@ int ExportOCAF::saveShape(Part::Feature* part, const std::vector<App::Color>& co
*/
// Add color information
Quantity_Color col;
Quantity_ColorRGBA col;
std::set<int> face_index;
TopTools_IndexedMapOfShape faces;
@@ -317,11 +328,7 @@ int ExportOCAF::saveShape(Part::Feature* part, const std::vector<App::Color>& co
if (!faceLabel.IsNull()) {
const App::Color& color = colors[index-1];
Standard_Real mat[3];
mat[0] = color.r;
mat[1] = color.g;
mat[2] = color.b;
col.SetValues(mat[0],mat[1],mat[2],Quantity_TOC_RGB);
col = convertColor(color);
aColorTool->SetColor(faceLabel, col, XCAFDoc_ColorSurf);
}
}
@@ -330,11 +337,7 @@ int ExportOCAF::saveShape(Part::Feature* part, const std::vector<App::Color>& co
}
else if (!colors.empty()) {
App::Color color = colors.front();
Standard_Real mat[3];
mat[0] = color.r;
mat[1] = color.g;
mat[2] = color.b;
col.SetValues(mat[0],mat[1],mat[2],Quantity_TOC_RGB);
col = convertColor(color);
aColorTool->SetColor(shapeLabel, col, XCAFDoc_ColorGen);
}
@@ -400,7 +403,7 @@ void ExportOCAF::reallocateFreeShape(std::vector <App::DocumentObject*> hierarch
TopoDS_Shape baseShape = part->Shape.getValue();
// Add color information
Quantity_Color col;
Quantity_ColorRGBA col;
std::set<int> face_index;
TopTools_IndexedMapOfShape faces;
@@ -433,11 +436,7 @@ void ExportOCAF::reallocateFreeShape(std::vector <App::DocumentObject*> hierarch
if (!faceLabel.IsNull()) {
const App::Color& color = colors[index-1];
Standard_Real mat[3];
mat[0] = color.r;
mat[1] = color.g;
mat[2] = color.b;
col.SetValues(mat[0],mat[1],mat[2],Quantity_TOC_RGB);
col = convertColor(color);
aColorTool->SetColor(faceLabel, col, XCAFDoc_ColorSurf);
}
}
@@ -447,11 +446,7 @@ void ExportOCAF::reallocateFreeShape(std::vector <App::DocumentObject*> hierarch
}
else if (!colors.empty()) {
App::Color color = colors.front();
Standard_Real mat[3];
mat[0] = color.r;
mat[1] = color.g;
mat[2] = color.b;
col.SetValues(mat[0],mat[1],mat[2],Quantity_TOC_RGB);
col = convertColor(color);
aColorTool->SetColor(label, col, XCAFDoc_ColorGen);
}
}
+24 -15
View File
@@ -60,7 +60,7 @@
# include <TDF_LabelSequence.hxx>
# include <TDF_ChildIterator.hxx>
# include <TDataStd_Name.hxx>
# include <Quantity_Color.hxx>
# include <Quantity_ColorRGBA.hxx>
# include <STEPCAFControl_Reader.hxx>
# include <STEPControl_Writer.hxx>
# include <IGESCAFControl_Reader.hxx>
@@ -108,6 +108,23 @@
using namespace Import;
#if OCC_VERSION_HEX >= 0x070500
// See https://dev.opencascade.org/content/occt-3d-viewer-becomes-srgb-aware
# define OCC_COLOR_SPACE Quantity_TOC_sRGB
#else
# define OCC_COLOR_SPACE Quantity_TOC_RGB
#endif
static inline App::Color convertColor(const Quantity_ColorRGBA &c)
{
Standard_Real r, g, b;
c.GetRGB().Values(r, g, b, OCC_COLOR_SPACE);
return App::Color(static_cast<float>(r),
static_cast<float>(g),
static_cast<float>(b),
1.0f - static_cast<float>(c.Alpha()));
}
#define OCAF_KEEP_PLACEMENT
ImportOCAF::ImportOCAF(Handle(TDocStd_Document) h, App::Document* d, const std::string& name)
@@ -439,14 +456,12 @@ void ImportOCAF::createShape(const TopoDS_Shape& aShape, const TopLoc_Location&
void ImportOCAF::loadColors(Part::Feature* part, const TopoDS_Shape& aShape)
{
Quantity_Color aColor;
Quantity_ColorRGBA aColor;
App::Color color(0.8f,0.8f,0.8f);
if (aColorTool->GetColor(aShape, XCAFDoc_ColorGen, aColor) ||
aColorTool->GetColor(aShape, XCAFDoc_ColorSurf, aColor) ||
aColorTool->GetColor(aShape, XCAFDoc_ColorCurv, aColor)) {
color.r = (float)aColor.Red();
color.g = (float)aColor.Green();
color.b = (float)aColor.Blue();
color = convertColor(aColor);
std::vector<App::Color> colors;
colors.push_back(color);
applyColors(part, colors);
@@ -468,9 +483,7 @@ void ImportOCAF::loadColors(Part::Feature* part, const TopoDS_Shape& aShape)
aColorTool->GetColor(xp.Current(), XCAFDoc_ColorSurf, aColor) ||
aColorTool->GetColor(xp.Current(), XCAFDoc_ColorCurv, aColor)) {
int index = faces.FindIndex(xp.Current());
color.r = (float)aColor.Red();
color.g = (float)aColor.Green();
color.b = (float)aColor.Blue();
color = convertColor(aColor);
faceColors[index-1] = color;
found_face_color = true;
}
@@ -551,7 +564,7 @@ void ImportXCAF::createShape(const TopoDS_Shape& shape, bool perface, bool setna
part = static_cast<Part::Feature*>(doc->addObject("Part::Feature", default_name.c_str()));
part->Label.setValue(default_name);
part->Shape.setValue(shape);
std::map<Standard_Integer, Quantity_Color>::const_iterator jt;
std::map<Standard_Integer, Quantity_ColorRGBA>::const_iterator jt;
jt = myColorMap.find(shape.HashCode(INT_MAX));
App::Color partColor(0.8f,0.8f,0.8f);
@@ -596,11 +609,7 @@ void ImportXCAF::createShape(const TopoDS_Shape& shape, bool perface, bool setna
jt = myColorMap.find(xp.Current().HashCode(INT_MAX));
if (jt != myColorMap.end()) {
int index = faces.FindIndex(xp.Current());
App::Color color;
color.r = (float)jt->second.Red();
color.g = (float)jt->second.Green();
color.b = (float)jt->second.Blue();
faceColors[index-1] = color;
faceColors[index-1] = convertColor(jt->second);
found_face_color = true;
}
xp.Next();
@@ -653,7 +662,7 @@ void ImportXCAF::loadShapes(const TDF_Label& label)
}
// getting color
Quantity_Color col;
Quantity_ColorRGBA col;
if (hColors->GetColor(label, XCAFDoc_ColorGen, col) ||
hColors->GetColor(label, XCAFDoc_ColorSurf, col) ||
hColors->GetColor(label, XCAFDoc_ColorCurv, col)) {
+2 -2
View File
@@ -27,7 +27,7 @@
#include <TDocStd_Document.hxx>
#include <XCAFDoc_ColorTool.hxx>
#include <XCAFDoc_ShapeTool.hxx>
#include <Quantity_Color.hxx>
#include <Quantity_ColorRGBA.hxx>
#include <TopoDS_Shape.hxx>
#include <TDF_LabelMapHasher.hxx>
#include <climits>
@@ -117,7 +117,7 @@ private:
std::map<Standard_Integer, TopoDS_Shape> myShells;
std::map<Standard_Integer, TopoDS_Shape> myCompds;
std::map<Standard_Integer, TopoDS_Shape> myShapes;
std::map<Standard_Integer, Quantity_Color> myColorMap;
std::map<Standard_Integer, Quantity_ColorRGBA> myColorMap;
std::map<Standard_Integer, std::string> myNameMap;
};
+59 -25
View File
@@ -41,7 +41,7 @@
# include <TDF_LabelSequence.hxx>
# include <TDF_ChildIterator.hxx>
# include <TDataStd_Name.hxx>
# include <Quantity_Color.hxx>
# include <Quantity_ColorRGBA.hxx>
# include <TopoDS_Iterator.hxx>
# include <Interface_Static.hxx>
# include <TDF_AttributeSequence.hxx>
@@ -50,6 +50,7 @@
#include <XCAFDoc_ShapeMapTool.hxx>
#include <boost/format.hpp>
#include <boost/regex.hpp>
#include <boost/algorithm/string.hpp>
#include <Base/Parameter.h>
@@ -71,12 +72,40 @@
#include <App/DocumentObject.h>
#include <App/DocumentObjectGroup.h>
#if OCC_VERSION_HEX >= 0x070500
// See https://dev.opencascade.org/content/occt-3d-viewer-becomes-srgb-aware
# define OCC_COLOR_SPACE Quantity_TOC_sRGB
#else
# define OCC_COLOR_SPACE Quantity_TOC_RGB
#endif
FC_LOG_LEVEL_INIT("Import",true,true)
using namespace Import;
/////////////////////////////////////////////////////////////////////
static inline App::Color convertColor(const Quantity_ColorRGBA &c)
{
Standard_Real r, g, b;
c.GetRGB().Values(r, g, b, OCC_COLOR_SPACE);
return App::Color(static_cast<float>(r),
static_cast<float>(g),
static_cast<float>(b),
1.0f - static_cast<float>(c.Alpha()));
}
static inline Quantity_ColorRGBA convertColor(const App::Color &c)
{
return Quantity_ColorRGBA(Quantity_Color(c.r, c.g, c.b, OCC_COLOR_SPACE), 1.0f - c.a);
}
static inline std::ostream& operator<<(std::ostream& os, const Quantity_ColorRGBA &c) {
App::Color color = convertColor(c);
auto toHex = [](float v) {return boost::format("%02X") % static_cast<int>(v*255);};
return os << "#" << toHex(color.r) << toHex(color.g) << toHex(color.b) << toHex(color.a);
}
static std::string labelName(TDF_Label label) {
std::string txt;
Handle(TDataStd_Name) name;
@@ -116,13 +145,13 @@ static void printLabel(TDF_Label label, Handle(XCAFDoc_ShapeTool) aShapeTool,
ss << ", " << Part::TopoShape::shapeName(shape.ShapeType(),true);
}
if(aShapeTool->IsShape(label)) {
Quantity_Color c;
Quantity_ColorRGBA c;
if(aColorTool->GetColor(label,XCAFDoc_ColorGen,c))
ss << ", gc: " << c.StringName(c.Name());
ss << ", gc: " << c;
if(aColorTool->GetColor(label,XCAFDoc_ColorSurf,c))
ss << ", sc: " << c.StringName(c.Name());
ss << ", sc: " << c;
if(aColorTool->GetColor(label,XCAFDoc_ColorCurv,c))
ss << ", cc: " << c.StringName(c.Name());
ss << ", cc: " << c;
}
ss << std::endl;
@@ -229,12 +258,11 @@ void ImportOCAF2::setObjectName(Info &info, TDF_Label label) {
}
}
bool ImportOCAF2::getColor(const TopoDS_Shape &shape, Info &info, bool check, bool noDefault) {
bool ret = false;
Quantity_Color aColor;
Quantity_ColorRGBA aColor;
if(aColorTool->GetColor(shape, XCAFDoc_ColorSurf, aColor)) {
App::Color c(aColor.Red(),aColor.Green(),aColor.Blue());
App::Color c = convertColor(aColor);
if(!check || info.faceColor!=c) {
info.faceColor = c;
info.hasFaceColor = true;
@@ -242,7 +270,7 @@ bool ImportOCAF2::getColor(const TopoDS_Shape &shape, Info &info, bool check, bo
}
}
if(!noDefault && !info.hasFaceColor && aColorTool->GetColor(shape, XCAFDoc_ColorGen, aColor)) {
App::Color c(aColor.Red(),aColor.Green(),aColor.Blue());
App::Color c = convertColor(aColor);
if(!check || info.faceColor!=c) {
info.faceColor = c;
info.hasFaceColor = true;
@@ -250,7 +278,7 @@ bool ImportOCAF2::getColor(const TopoDS_Shape &shape, Info &info, bool check, bo
}
}
if(aColorTool->GetColor(shape, XCAFDoc_ColorCurv, aColor)) {
App::Color c(aColor.Red(),aColor.Green(),aColor.Blue());
App::Color c = convertColor(aColor);
// Some STEP include a curve color with the same value of the face
// color. And this will look weird in FC. So for shape with face
// we'll ignore the curve color, if it is the same as the face color.
@@ -365,15 +393,15 @@ bool ImportOCAF2::createObject(App::Document *doc, TDF_Label label,
bool foundFaceColor=false,foundEdgeColor=false;
App::Color faceColor,edgeColor;
Quantity_Color aColor;
Quantity_ColorRGBA aColor;
if(aColorTool->GetColor(l, XCAFDoc_ColorSurf, aColor) ||
aColorTool->GetColor(l, XCAFDoc_ColorGen, aColor))
{
faceColor = App::Color(aColor.Red(),aColor.Green(),aColor.Blue());
faceColor = convertColor(aColor);
foundFaceColor = true;
}
if(aColorTool->GetColor(l, XCAFDoc_ColorCurv, aColor)) {
edgeColor = App::Color(aColor.Red(),aColor.Green(),aColor.Blue());
edgeColor = convertColor(aColor);
foundEdgeColor = true;
if(j==0 && foundFaceColor && faceColors.size() && edgeColor==faceColor) {
// Do not set edge the same color as face
@@ -648,11 +676,11 @@ void ImportOCAF2::getSHUOColors(TDF_Label label,
subname += App::DocumentObject::hiddenMarker();
colors.emplace(subname,App::Color());
} else {
Quantity_Color aColor;
Quantity_ColorRGBA aColor;
if(aColorTool->GetColor(slabel, XCAFDoc_ColorSurf, aColor) ||
aColorTool->GetColor(slabel, XCAFDoc_ColorGen, aColor))
{
colors.emplace(subname,App::Color(aColor.Red(),aColor.Green(),aColor.Blue()));
colors.emplace(subname,convertColor(aColor));
}
}
}
@@ -782,12 +810,9 @@ bool ImportOCAF2::createAssembly(App::Document *_doc,
childInfo.vis.push_back(vis);
childInfo.labels.push_back(childLabel);
childInfo.plas.emplace_back(Part::TopoShape::convert(childShape.Location().Transformation()));
Quantity_Color aColor;
Quantity_ColorRGBA aColor;
if (aColorTool->GetColor(childShape, XCAFDoc_ColorSurf, aColor)) {
auto &color = childInfo.colors[childInfo.plas.size()-1];
color.r = (float)aColor.Red();
color.g = (float)aColor.Green();
color.b = (float)aColor.Blue();
childInfo.colors[childInfo.plas.size()-1] = convertColor(aColor);
}
}
assert(visibilities.size() == children.size());
@@ -1036,7 +1061,7 @@ void ExportOCAF2::setupObject(TDF_Label label, App::DocumentObject *obj,
continue;
}
const App::Color& c = vv.second;
Quantity_Color color(c.r,c.g,c.b,Quantity_TOC_RGB);
Quantity_ColorRGBA color = convertColor(c);
auto colorType = vv.first[0]=='F'?XCAFDoc_ColorSurf:XCAFDoc_ColorCurv;
if(vv.first=="Face" || vv.first=="Edge") {
aColorTool->SetColor(nodeLabel, color, colorType);
@@ -1219,8 +1244,17 @@ TDF_Label ExportOCAF2::exportObject(App::DocumentObject* parentObj,
// not call setupObject() on a non-located baseshape like above,
// because OCCT does not respect shape style sharing when not
// exporting assembly
if(!keepPlacement)
if(!keepPlacement || shape.getPlacement() == Base::Placement())
shape.setShape(shape.getShape().Located(TopLoc_Location()));
else {
Base::Matrix4D mat = shape.getTransform();
shape.setShape(shape.getShape().Located(TopLoc_Location()));
// Transform with copy to conceal the transformation
shape.transformShape(mat, true);
// Even if the shape has no transformation, TopoShape still sets
// a TopLoc_Location, so we need to clear it again.
shape.setShape(shape.getShape().Located(TopLoc_Location()));
}
label = aShapeTool->AddShape(shape.getShape(),Standard_False, Standard_False);
auto o = name?parentObj:obj;
if(o!=linked)
@@ -1277,7 +1311,7 @@ TDF_Label ExportOCAF2::exportObject(App::DocumentObject* parentObj,
// Work around OCCT bug. If no color setting here, it will crash.
// The culprit is at STEPCAFControl_Writer::1093 as shown below
//
// surfColor = Styles.EncodeColor(Quantity_Color(1,1,1,Quantity_TOC_RGB),DPDCs,ColRGBs);
// surfColor = Styles.EncodeColor(Quantity_Color(1,1,1,OCC_COLOR_SPACE),DPDCs,ColRGBs);
// PSA = Styles.MakeColorPSA ( item, surfColor, curvColor, isComponent );
// if ( isComponent )
// setDefaultInstanceColor( override, PSA);
@@ -1287,13 +1321,13 @@ TDF_Label ExportOCAF2::exportObject(App::DocumentObject* parentObj,
// setDefaultInstanceColor( override, PSA);
//
auto childShape = aShapeTool->GetShape(childLabel);
Quantity_Color col;
Quantity_ColorRGBA col;
if(!aColorTool->GetInstanceColor(childShape,XCAFDoc_ColorGen,col) &&
!aColorTool->GetInstanceColor(childShape,XCAFDoc_ColorSurf,col) &&
!aColorTool->GetInstanceColor(childShape,XCAFDoc_ColorCurv,col))
{
auto &c = defaultColor;
aColorTool->SetColor(childLabel, Quantity_Color(c.r,c.g,c.b,Quantity_TOC_RGB), XCAFDoc_ColorGen);
aColorTool->SetColor(childLabel, convertColor(c), XCAFDoc_ColorGen);
FC_WARN(labelName(childLabel) << " set default color");
}
aColorTool->SetVisibility(childLabel,Standard_False);
+4 -3
View File
@@ -138,6 +138,7 @@
FC_LOG_LEVEL_INIT("Import", true, true)
namespace ImportGui {
class OCAFBrowser
{
public:
@@ -321,9 +322,10 @@ private:
return;
}
// vp->MapFaceColor.setValue(false);
if(colors.size() == 1)
if(colors.size() == 1) {
vp->ShapeColor.setValue(colors.front());
else
vp->Transparency.setValue(100 * colors.front().a);
} else
vp->DiffuseColor.setValues(colors);
}
virtual void applyEdgeColors(Part::Feature* part, const std::vector<App::Color>& colors) override {
@@ -382,7 +384,6 @@ public:
}
};
namespace ImportGui {
class Module : public Py::ExtensionModule<Module>
{
public:
+4 -4
View File
@@ -1,19 +1,19 @@
[General]
Name = Air
Description = Standard air properties at 20 Degrees Celsius and 1 atm
Description = Dry air properties at 20 Degrees Celsius and 1 atm
MolarMass = 28.965
Father = Gas
[Fluidic]
Density = 1.20 kg/m^3
Density = 1.204 kg/m^3
DynamicViscosity = 1.80e-5 kg/m/s
KinematicViscosity = 1.511e-5 m^2/s
; PrandtlNumber is a nondimension number for CFD simulation
PrandtlNumber = 0.7
[Thermal]
SpecificHeat = 1.005 J/kg/K
ThermalConductivity = 0.0257 W/m/K
SpecificHeat = 1.01 kJ/kg/K
ThermalConductivity = 0.02587 W/m/K
; volumetric expansion coeff of ideal gas depends on temperature and pressure
VolumetricThermalExpansionCoefficient = 3.43e-3 m/m/K
@@ -27,6 +27,9 @@
# include <Standard_Failure.hxx>
#endif
// Needed for OCCT 7.5.2
#include <TopoDS_Edge.hxx>
#include "ShapeUpgrade/UnifySameDomainPy.h"
#include "ShapeUpgrade/UnifySameDomainPy.cpp"
#include "TopoShapePy.h"
+7 -6
View File
@@ -977,13 +977,14 @@ Py::Object TopoShapeFacePy::getWire(void) const
Py::Object TopoShapeFacePy::getOuterWire(void) const
{
const TopoDS_Shape& clSh = getTopoShapePtr()->getShape();
if (clSh.IsNull())
const TopoDS_Shape& shape = getTopoShapePtr()->getShape();
if (shape.IsNull())
throw Py::RuntimeError("Null shape");
if (clSh.ShapeType() == TopAbs_FACE) {
TopoDS_Face clFace = (TopoDS_Face&)clSh;
TopoDS_Wire clWire = ShapeAnalysis::OuterWire(clFace);
return Py::Object(new TopoShapeWirePy(new TopoShape(clWire)),true);
if (shape.ShapeType() == TopAbs_FACE) {
TopoDS_Wire wire = ShapeAnalysis::OuterWire(TopoDS::Face(shape));
Base::PyObjectBase* wirepy = new TopoShapeWirePy(new TopoShape(wire));
wirepy->setNotTracking();
return Py::asObject(wirepy);
}
else {
throw Py::TypeError("Internal error, TopoDS_Shape is not a face!");
+3 -1
View File
@@ -194,7 +194,9 @@ Py::Object TopoShapeVertexPy::getPoint(void) const
try {
const TopoDS_Vertex& v = TopoDS::Vertex(getTopoShapePtr()->getShape());
gp_Pnt p = BRep_Tool::Pnt(v);
return Py::asObject(new Base::VectorPy(new Base::Vector3d(p.X(),p.Y(),p.Z())));
Base::PyObjectBase* pnt = new Base::VectorPy(new Base::Vector3d(p.X(),p.Y(),p.Z()));
pnt->setNotTracking();
return Py::asObject(pnt);
}
catch (Standard_Failure& e) {
+1 -1
View File
@@ -274,7 +274,7 @@ void ViewProvider2DObjectGrid::onChanged(const App::Property* prop)
ViewProviderPart::onChanged(prop);
if (prop == &ShowGrid || prop == &ShowOnlyInEditMode || prop == &Visibility) {
if (ShowGrid.getValue() && Visibility.getValue() && !(ShowOnlyInEditMode.getValue() && !this->isEditing()))
if (ShowGrid.getValue() && ((Visibility.getValue() && !ShowOnlyInEditMode.getValue()) || this->isEditing()))
createGrid();
else
Gui::coinRemoveAllChildren(GridRoot);
+3 -3
View File
@@ -21,8 +21,8 @@
***************************************************************************/
#ifndef PARTGUI_IEWPROVIDER2DOBJECT_H
#define PARTGUI_IEWPROVIDER2DOBJECT_H
#ifndef PARTGUI_VIEWPROVIDER2DOBJECT_H
#define PARTGUI_VIEWPROVIDER2DOBJECT_H
#include "ViewProvider.h"
#include <App/PropertyUnits.h>
@@ -103,5 +103,5 @@ typedef Gui::ViewProviderPythonFeatureT<ViewProvider2DObject> ViewProvider2DObje
} // namespace PartGui
#endif // PARTGUI_IEWPROVIDER2DOBJECT_H
#endif // PARTGUI_VIEWPROVIDER2DOBJECT_H
+9 -13
View File
@@ -427,24 +427,19 @@ void ViewProviderPartExt::onChanged(const App::Property* prop)
else if (prop == &ShapeMaterial || prop == &ShapeColor) {
pcFaceBind->value = SoMaterialBinding::OVERALL;
ViewProviderGeometryObject::onChanged(prop);
DiffuseColor.setValue(ShapeColor.getValue());
App::Color c = ShapeColor.getValue();
c.a = Transparency.getValue()/100.0f;
DiffuseColor.setValue(c);
}
else if (prop == &Transparency) {
const App::Material& Mat = ShapeMaterial.getValue();
long value = (long)(100*Mat.transparency);
if (value != Transparency.getValue()) {
float trans = Transparency.getValue()/100.0f;
if (pcFaceBind->value.getValue() == SoMaterialBinding::PER_PART) {
int cnt = pcShapeMaterial->diffuseColor.getNum();
pcShapeMaterial->transparency.setNum(cnt);
float *t = pcShapeMaterial->transparency.startEditing();
for (int i=0; i<cnt; i++)
t[i] = trans;
pcShapeMaterial->transparency.finishEditing();
}
else {
pcShapeMaterial->transparency = trans;
}
auto colors = DiffuseColor.getValues();
for (auto &c : colors)
c.a = trans;
DiffuseColor.setValues(colors);
App::PropertyContainer* parent = ShapeMaterial.getContainer();
ShapeMaterial.setContainer(0);
@@ -713,7 +708,7 @@ void ViewProviderPartExt::setHighlightedFaces(const std::vector<App::Color>& col
else if (colors.size() == 1) {
pcFaceBind->value = SoMaterialBinding::OVERALL;
pcShapeMaterial->diffuseColor.setValue(colors[0].r, colors[0].g, colors[0].b);
//pcShapeMaterial->transparency = colors[0].a; do not get transparency from DiffuseColor in this case
pcShapeMaterial->transparency = Transparency.getValue()/100.f;
}
}
@@ -779,6 +774,7 @@ std::map<std::string,App::Color> ViewProviderPartExt::getElementColors(const cha
}
if(size && singleColor) {
color = DiffuseColor[0];
color.a = Transparency.getValue()/100.0f;
ret.clear();
}
ret["Face"] = color;
+7 -6
View File
@@ -127,17 +127,17 @@ Gui::MenuItem* Workbench::setupMenuBar() const
<< "Separator"
<< bop << join << split << compound
<< "Separator"
<< "Part_Section"
<< "Part_CrossSections"
<< "Part_MakeFace"
<< "Part_Extrude"
<< "Part_Revolve"
<< "Part_Mirror"
<< "Part_Fillet"
<< "Part_Chamfer"
<< "Part_MakeFace"
<< "Part_RuledSurface"
<< "Part_Loft"
<< "Part_Sweep"
<< "Part_Section"
<< "Part_CrossSections"
<< "Part_Offset"
<< "Part_Offset2D"
<< "Part_Thickness"
@@ -191,9 +191,12 @@ Gui::ToolBarItem* Workbench::setupToolBars() const
<< "Part_Mirror"
<< "Part_Fillet"
<< "Part_Chamfer"
<< "Part_MakeFace"
<< "Part_RuledSurface"
<< "Part_Loft"
<< "Part_Sweep"
<< "Part_Section"
<< "Part_CrossSections"
<< "Part_CompOffset"
<< "Part_Thickness"
<< "Part_ProjectionOnSurface"
@@ -209,9 +212,7 @@ Gui::ToolBarItem* Workbench::setupToolBars() const
<< "Part_CompJoinFeatures"
<< "Part_CompSplitFeatures"
<< "Part_CheckGeometry"
<< "Part_Defeaturing"
<< "Part_Section"
<< "Part_CrossSections";
<< "Part_Defeaturing";
Gui::ToolBarItem* measure = new Gui::ToolBarItem(root);
measure->setCommand("Measure");
+1
View File
@@ -111,6 +111,7 @@ App::DocumentObjectExecReturn *Helix::execute(void)
return new App::DocumentObjectExecReturn("Error: Pitch too small");
if (Height.getValue() < Precision::Confusion())
return new App::DocumentObjectExecReturn("Error: height too small!");
Turns.setValue(Height.getValue()/Pitch.getValue());
break;
case 1: // pitch - turns
if (Pitch.getValue() < Precision::Confusion())
+13 -11
View File
@@ -55,7 +55,7 @@ using namespace Gui;
/* TRANSLATOR PartDesignGui::TaskLoftParameters */
TaskLoftParameters::TaskLoftParameters(ViewProviderLoft *LoftView,bool /*newObj*/, QWidget *parent)
TaskLoftParameters::TaskLoftParameters(ViewProviderLoft *LoftView, bool /*newObj*/, QWidget *parent)
: TaskSketchBasedParameters(LoftView, parent, "PartDesign_AdditiveLoft", tr("Loft parameters"))
, ui(new Ui_TaskLoftParameters)
{
@@ -69,7 +69,7 @@ TaskLoftParameters::TaskLoftParameters(ViewProviderLoft *LoftView,bool /*newObj*
connect(ui->buttonRefAdd, SIGNAL(toggled(bool)),
this, SLOT(onRefButtonAdd(bool)));
connect(ui->buttonRefRemove, SIGNAL(toggled(bool)),
this, SLOT(onRefButtonRemvove(bool)));
this, SLOT(onRefButtonRemove(bool)));
connect(ui->checkBoxRuled, SIGNAL(toggled(bool)),
this, SLOT(onRuled(bool)));
connect(ui->checkBoxClosed, SIGNAL(toggled(bool)),
@@ -121,24 +121,23 @@ TaskLoftParameters::TaskLoftParameters(ViewProviderLoft *LoftView,bool /*newObj*
ui->checkBoxRuled->setChecked(loft->Ruled.getValue());
ui->checkBoxClosed->setChecked(loft->Closed.getValue());
if (!loft->Sections.getValues().empty()) {
LoftView->makeTemporaryVisible(true);
}
// activate and de-activate dialog elements as appropriate
for (QWidget* child : proxy->findChildren<QWidget*>())
child->blockSignals(false);
updateUI(0);
updateUI();
}
TaskLoftParameters::~TaskLoftParameters()
{
}
void TaskLoftParameters::updateUI(int index)
void TaskLoftParameters::updateUI()
{
Q_UNUSED(index);
// we must assure the changed loft is kept visible on section changes,
// see https://forum.freecadweb.org/viewtopic.php?f=3&t=63252
PartDesign::Loft* loft = static_cast<PartDesign::Loft*>(vp->getObject());
vp->makeTemporaryVisible(!loft->Sections.getValues().empty());
}
void TaskLoftParameters::onSelectionChanged(const Gui::SelectionChanges& msg)
@@ -173,6 +172,7 @@ void TaskLoftParameters::onSelectionChanged(const Gui::SelectionChanges& msg)
clearButtons();
exitSelectionMode();
updateUI();
}
}
@@ -256,6 +256,7 @@ void TaskLoftParameters::onDeleteSection()
//static_cast<ViewProviderLoft*>(vp)->highlightReferences(false, true);
recomputeFeature();
updateUI();
}
}
}
@@ -279,6 +280,7 @@ void TaskLoftParameters::indexesMoved()
loft->Sections.setValues(originals);
recomputeFeature();
updateUI();
}
void TaskLoftParameters::clearButtons() {
@@ -324,7 +326,7 @@ void TaskLoftParameters::onRefButtonAdd(bool checked) {
}
}
void TaskLoftParameters::onRefButtonRemvove(bool checked) {
void TaskLoftParameters::onRefButtonRemove(bool checked) {
if (checked) {
Gui::Selection().clearSelection();
@@ -359,7 +361,7 @@ bool TaskDlgLoftParameters::accept()
// TODO Fill this with commands (2015-09-11, Fat-Zer)
PartDesign::Loft* pcLoft = static_cast<PartDesign::Loft*>(vp->getObject());
for(App::DocumentObject* obj : pcLoft->Sections.getValues()) {
for (App::DocumentObject* obj : pcLoft->Sections.getValues()) {
FCMD_OBJ_HIDE(obj);
}
+3 -3
View File
@@ -50,13 +50,13 @@ class TaskLoftParameters : public TaskSketchBasedParameters
Q_OBJECT
public:
TaskLoftParameters(ViewProviderLoft *LoftView,bool newObj=false,QWidget *parent = 0);
TaskLoftParameters(ViewProviderLoft *LoftView, bool newObj=false, QWidget *parent = 0);
~TaskLoftParameters();
private Q_SLOTS:
void onProfileButton(bool);
void onRefButtonAdd(bool);
void onRefButtonRemvove(bool);
void onRefButtonRemove(bool);
void onClosed(bool);
void onRuled(bool);
void onDeleteSection();
@@ -67,7 +67,7 @@ protected:
private:
void onSelectionChanged(const Gui::SelectionChanges& msg);
void updateUI(int index);
void updateUI();
bool referenceSelected(const Gui::SelectionChanges& msg) const;
void removeFromListWidget(QListWidget*w, QString name);
void clearButtons();
@@ -229,7 +229,8 @@ void TaskMultiTransformParameters::closeSubTask()
void TaskMultiTransformParameters::onTransformDelete()
{
if (editHint) return; // Can't delete the hint...
if (editHint)
return; // Can't delete the hint...
int row = ui->listTransformFeatures->currentIndex().row();
PartDesign::MultiTransform* pcMultiTransform = static_cast<PartDesign::MultiTransform*>(TransformedView->getObject());
std::vector<App::DocumentObject*> transformFeatures = pcMultiTransform->Transformations.getValues();
@@ -254,7 +255,8 @@ void TaskMultiTransformParameters::onTransformDelete()
void TaskMultiTransformParameters::onTransformEdit()
{
if (editHint) return; // Can't edit the hint...
if (editHint)
return; // Can't edit the hint...
closeSubTask(); // For example if user is editing one subTask and then double-clicks on another without OK'ing first
ui->listTransformFeatures->currentItem()->setSelected(true);
int row = ui->listTransformFeatures->currentIndex().row();
@@ -288,17 +290,23 @@ void TaskMultiTransformParameters::onTransformAddMirrored()
closeSubTask();
std::string newFeatName = TransformedView->getObject()->getDocument()->getUniqueObjectName("Mirrored");
auto pcActiveBody = PartDesignGui::getBody(false);
if(!pcActiveBody) return;
if (!pcActiveBody)
return;
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Mirrored"));
FCMD_OBJ_CMD(pcActiveBody,"newObject('PartDesign::Mirrored','"<<newFeatName<<"')");
FCMD_OBJ_CMD(pcActiveBody, "newObject('PartDesign::Mirrored','"<<newFeatName<<"')");
auto Feat = pcActiveBody->getDocument()->getObject(newFeatName.c_str());
if (!Feat)
return;
//Gui::Command::updateActive();
App::DocumentObject* sketch = getSketchObject();
if (sketch)
FCMD_OBJ_CMD(Feat,"MirrorPlane = ("<<Gui::Command::getObjectCmd(sketch)<<",['V_Axis'])");
FCMD_OBJ_CMD(Feat, "MirrorPlane = ("<<Gui::Command::getObjectCmd(sketch)<<",['V_Axis'])");
finishAdd(newFeatName);
// show the new view when no error
if (!Feat->isError())
TransformedView->getObject()->Visibility.setValue(true);
}
void TaskMultiTransformParameters::onTransformAddLinearPattern()
@@ -308,29 +316,35 @@ void TaskMultiTransformParameters::onTransformAddLinearPattern()
closeSubTask();
std::string newFeatName = TransformedView->getObject()->getDocument()->getUniqueObjectName("LinearPattern");
auto pcActiveBody = PartDesignGui::getBody(false);
if(!pcActiveBody) return;
if (!pcActiveBody)
return;
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Make LinearPattern"));
FCMD_OBJ_CMD(pcActiveBody,"newObject('PartDesign::LinearPattern','"<<newFeatName<<"')");
FCMD_OBJ_CMD(pcActiveBody, "newObject('PartDesign::LinearPattern','"<<newFeatName<<"')");
auto Feat = pcActiveBody->getDocument()->getObject(newFeatName.c_str());
if (!Feat)
return;
//Gui::Command::updateActive();
App::DocumentObject* sketch = getSketchObject();
if (sketch) {
FCMD_OBJ_CMD(Feat,"Direction = ("<<Gui::Command::getObjectCmd(sketch)<<",['H_Axis'])");
FCMD_OBJ_CMD(Feat, "Direction = ("<<Gui::Command::getObjectCmd(sketch)<<",['H_Axis'])");
}
else {
// set Direction value before filling up the combo box to avoid creating an empty item
// inside updateUI()
PartDesign::Body* body = static_cast<PartDesign::Body*>(Part::BodyBase::findBodyOf(getObject()));
if (body) {
FCMD_OBJ_CMD(Feat,"Direction = ("<<Gui::Command::getObjectCmd(body->getOrigin()->getX())<<",[''])");
FCMD_OBJ_CMD(Feat, "Direction = ("<<Gui::Command::getObjectCmd(body->getOrigin()->getX())<<",[''])");
}
}
FCMD_OBJ_CMD(Feat,"Length = 100");
FCMD_OBJ_CMD(Feat,"Occurrences = 2");
FCMD_OBJ_CMD(Feat, "Length = 100");
FCMD_OBJ_CMD(Feat, "Occurrences = 2");
finishAdd(newFeatName);
// show the new view when no error
if (!Feat->isError())
TransformedView->getObject()->Visibility.setValue(true);
}
void TaskMultiTransformParameters::onTransformAddPolarPattern()
@@ -338,19 +352,25 @@ void TaskMultiTransformParameters::onTransformAddPolarPattern()
closeSubTask();
std::string newFeatName = TransformedView->getObject()->getDocument()->getUniqueObjectName("PolarPattern");
auto pcActiveBody = PartDesignGui::getBody(false);
if(!pcActiveBody) return;
if (!pcActiveBody)
return;
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "PolarPattern"));
FCMD_OBJ_CMD(pcActiveBody,"newObject('PartDesign::PolarPattern','"<<newFeatName<<"')");
FCMD_OBJ_CMD(pcActiveBody, "newObject('PartDesign::PolarPattern','"<<newFeatName<<"')");
auto Feat = pcActiveBody->getDocument()->getObject(newFeatName.c_str());
if (!Feat)
return;
//Gui::Command::updateActive();
App::DocumentObject* sketch = getSketchObject();
if (sketch)
FCMD_OBJ_CMD(Feat, "Axis = ("<<Gui::Command::getObjectCmd(sketch)<<",['N_Axis'])");
FCMD_OBJ_CMD(Feat,"Angle = 360");
FCMD_OBJ_CMD(Feat,"Occurrences = 2");
FCMD_OBJ_CMD(Feat, "Angle = 360");
FCMD_OBJ_CMD(Feat, "Occurrences = 2");
finishAdd(newFeatName);
// show the new view when no error
if (!Feat->isError())
TransformedView->getObject()->Visibility.setValue(true);
}
void TaskMultiTransformParameters::onTransformAddScaled()
@@ -358,16 +378,22 @@ void TaskMultiTransformParameters::onTransformAddScaled()
closeSubTask();
std::string newFeatName = TransformedView->getObject()->getDocument()->getUniqueObjectName("Scaled");
auto pcActiveBody = PartDesignGui::getBody(false);
if(!pcActiveBody) return;
if (!pcActiveBody)
return;
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Scaled"));
FCMD_OBJ_CMD(pcActiveBody,"newObject('PartDesign::Scaled','"<<newFeatName<<"')");
FCMD_OBJ_CMD(pcActiveBody, "newObject('PartDesign::Scaled','"<<newFeatName<<"')");
auto Feat = pcActiveBody->getDocument()->getObject(newFeatName.c_str());
if (!Feat)
return;
//Gui::Command::updateActive();
FCMD_OBJ_CMD(Feat,"Factor = 2");
FCMD_OBJ_CMD(Feat,"Occurrences = 2");
FCMD_OBJ_CMD(Feat, "Factor = 2");
FCMD_OBJ_CMD(Feat, "Occurrences = 2");
finishAdd(newFeatName);
// show the new view when no error
if (!Feat->isError())
TransformedView->getObject()->Visibility.setValue(true);
}
void TaskMultiTransformParameters::finishAdd(std::string &newFeatName)
@@ -94,6 +94,7 @@ TaskPipeParameters::TaskPipeParameters(ViewProviderPipe *PipeView, bool /*newObj
// Create context menu
QAction* remove = new QAction(tr("Remove"), this);
remove->setShortcut(QKeySequence::Delete);
remove->setShortcutContext(Qt::WidgetShortcut);
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
// display shortcut behind the context menu entry
remove->setShortcutVisibleInContextMenu(true);
@@ -582,6 +583,7 @@ TaskPipeOrientation::TaskPipeOrientation(ViewProviderPipe* PipeView, bool /*newO
// Create context menu
QAction* remove = new QAction(tr("Remove"), this);
remove->setShortcut(QKeySequence::Delete);
remove->setShortcutContext(Qt::WidgetShortcut);
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
// display shortcut behind the context menu entry
remove->setShortcutVisibleInContextMenu(true);
@@ -897,6 +899,7 @@ TaskPipeScaling::TaskPipeScaling(ViewProviderPipe* PipeView, bool /*newObj*/, QW
// Create context menu
QAction* remove = new QAction(tr("Remove"), this);
remove->setShortcut(QKeySequence::Delete);
remove->setShortcutContext(Qt::WidgetShortcut);
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
// display shortcut behind the context menu entry
remove->setShortcutVisibleInContextMenu(true);
@@ -264,6 +264,7 @@ bool TaskDlgSketchBasedParameters::accept() {
bool TaskDlgSketchBasedParameters::reject()
{
PartDesign::ProfileBased* pcSketchBased = static_cast<PartDesign::ProfileBased*>(vp->getObject());
App::DocumentObjectWeakPtrT weakptr(pcSketchBased);
// get the Sketch
Sketcher::SketchObject *pcSketch = static_cast<Sketcher::SketchObject*>(pcSketchBased->Profile.getValue());
bool rv;
@@ -273,7 +274,7 @@ bool TaskDlgSketchBasedParameters::reject()
// if abort command deleted the object the sketch is visible again.
// The previous one feature already should be made visible
if (!Gui::Application::Instance->getViewProvider(pcSketchBased)) {
if (weakptr.expired()) {
// Make the sketch visible
if (pcSketch && Gui::Application::Instance->getViewProvider(pcSketch))
Gui::Application::Instance->getViewProvider(pcSketch)->show();
@@ -267,7 +267,7 @@ void ViewProviderAddSub::updateAddSubShapeIndicator() {
void ViewProviderAddSub::updateData(const App::Property* p) {
if(strcmp(p->getName(), "AddSubShape")==0)
if(p->getName() && strcmp(p->getName(), "AddSubShape")==0)
updateAddSubShapeIndicator();
PartDesignGui::ViewProvider::updateData(p);
+15 -3
View File
@@ -68,10 +68,10 @@ def updateInputField(obj, prop, widget, onBeforeChange=None):
isDiff = True
break
if noExpr:
widget.setProperty('readonly', False)
widget.setReadOnly(False)
widget.setStyleSheet("color: black")
else:
widget.setProperty('readonly', True)
widget.setReadOnly(True)
widget.setStyleSheet("color: gray")
widget.update()
@@ -100,6 +100,7 @@ class QuantitySpinBox:
self.widget = widget
self.onBeforeChange = onBeforeChange
self.prop = None
self.obj = obj
self.attachTo(obj, prop)
def attachTo(self, obj, prop = None):
@@ -139,9 +140,14 @@ class QuantitySpinBox:
If no value is provided the value of the bound property is used.
quantity can be of type Quantity or Float.'''
PathLog.track(self.prop, self.valid)
if self.valid:
expr = self._hasExpression()
if quantity is None:
quantity = PathUtil.getProperty(self.obj, self.prop)
if expr:
quantity = FreeCAD.Units.Quantity(self.obj.evalExpression(expr))
else:
quantity = PathUtil.getProperty(self.obj, self.prop)
value = quantity.Value if hasattr(quantity, 'Value') else quantity
self.widget.setProperty('rawValue', value)
@@ -151,3 +157,9 @@ class QuantitySpinBox:
if self.valid:
return updateInputField(self.obj, self.prop, self.widget, self.onBeforeChange)
return None
def _hasExpression(self):
for (prop, exp) in self.obj.ExpressionEngine:
if prop == self.prop:
return exp
return None
+61 -36
View File
@@ -171,7 +171,7 @@ class ObjectJob:
obj.Stock.ViewObject.Visibility = False
def setupSetupSheet(self, obj):
if not hasattr(obj, 'SetupSheet'):
if not getattr(obj, 'SetupSheet', None):
obj.addProperty('App::PropertyLink', 'SetupSheet', 'Base', QtCore.QT_TRANSLATE_NOOP('PathJob', 'SetupSheet holding the settings for this job'))
obj.SetupSheet = PathSetupSheet.Create()
if obj.SetupSheet.ViewObject:
@@ -223,53 +223,58 @@ class ObjectJob:
PathLog.track(obj.Label, arg2)
doc = obj.Document
# the first to tear down are the ops, they depend on other resources
PathLog.debug('taking down ops: %s' % [o.Name for o in self.allOperations()])
while obj.Operations.Group:
op = obj.Operations.Group[0]
if not op.ViewObject or not hasattr(op.ViewObject.Proxy, 'onDelete') or op.ViewObject.Proxy.onDelete(op.ViewObject, ()):
PathUtil.clearExpressionEngine(op)
doc.removeObject(op.Name)
obj.Operations.Group = []
doc.removeObject(obj.Operations.Name)
obj.Operations = None
if getattr(obj, 'Operations', None):
# the first to tear down are the ops, they depend on other resources
PathLog.debug('taking down ops: %s' % [o.Name for o in self.allOperations()])
while obj.Operations.Group:
op = obj.Operations.Group[0]
if not op.ViewObject or not hasattr(op.ViewObject.Proxy, 'onDelete') or op.ViewObject.Proxy.onDelete(op.ViewObject, ()):
PathUtil.clearExpressionEngine(op)
doc.removeObject(op.Name)
obj.Operations.Group = []
doc.removeObject(obj.Operations.Name)
obj.Operations = None
# stock could depend on Model, so delete it first
if obj.Stock:
if getattr(obj, 'Stock', None):
PathLog.debug('taking down stock')
PathUtil.clearExpressionEngine(obj.Stock)
doc.removeObject(obj.Stock.Name)
obj.Stock = None
# base doesn't depend on anything inside job
for base in obj.Model.Group:
PathLog.debug("taking down base %s" % base.Label)
self.removeBase(obj, base, False)
obj.Model.Group = []
doc.removeObject(obj.Model.Name)
obj.Model = None
if getattr(obj, 'Model', None):
for base in obj.Model.Group:
PathLog.debug("taking down base %s" % base.Label)
self.removeBase(obj, base, False)
obj.Model.Group = []
doc.removeObject(obj.Model.Name)
obj.Model = None
# Tool controllers might refer to either legacy tool or toolbit
PathLog.debug('taking down tool controller')
for tc in obj.Tools.Group:
if hasattr(tc.Tool, "Proxy"):
PathUtil.clearExpressionEngine(tc.Tool)
doc.removeObject(tc.Tool.Name)
PathUtil.clearExpressionEngine(tc)
tc.Proxy.onDelete(tc)
doc.removeObject(tc.Name)
obj.Tools.Group = []
doc.removeObject(obj.Tools.Name)
obj.Tools = None
if getattr(obj, 'Tools', None):
PathLog.debug('taking down tool controller')
for tc in obj.Tools.Group:
if hasattr(tc.Tool, "Proxy"):
PathUtil.clearExpressionEngine(tc.Tool)
doc.removeObject(tc.Tool.Name)
PathUtil.clearExpressionEngine(tc)
tc.Proxy.onDelete(tc)
doc.removeObject(tc.Name)
obj.Tools.Group = []
doc.removeObject(obj.Tools.Name)
obj.Tools = None
# SetupSheet
PathUtil.clearExpressionEngine(obj.SetupSheet)
doc.removeObject(obj.SetupSheet.Name)
obj.SetupSheet = None
if getattr(obj, 'SetupSheet', None):
PathUtil.clearExpressionEngine(obj.SetupSheet)
doc.removeObject(obj.SetupSheet.Name)
obj.SetupSheet = None
return True
def fixupOperations(self, obj):
if obj.Operations.ViewObject:
if getattr(obj.Operations, 'ViewObject', None):
try:
obj.Operations.ViewObject.DisplayMode
except Exception: # pylint: disable=broad-except
@@ -296,6 +301,18 @@ class ObjectJob:
obj.addProperty("App::PropertyString", "CycleTime", "Path", QtCore.QT_TRANSLATE_NOOP("PathOp", "Operations Cycle Time Estimation"))
obj.setEditorMode('CycleTime', 1) # read-only
if not hasattr(obj, "Fixtures"):
obj.addProperty("App::PropertyStringList", "Fixtures", "WCS", QtCore.QT_TRANSLATE_NOOP("PathJob", "The Work Coordinate Systems for the Job"))
obj.Fixtures = ['G54']
if not hasattr(obj, "OrderOutputBy"):
obj.addProperty("App::PropertyEnumeration", "OrderOutputBy", "WCS", QtCore.QT_TRANSLATE_NOOP("PathJob", "If multiple WCS, order the output this way"))
obj.OrderOutputBy = ['Fixture', 'Tool', 'Operation']
if not hasattr(obj, "SplitOutput"):
obj.addProperty("App::PropertyBool", "SplitOutput", "Output", QtCore.QT_TRANSLATE_NOOP("PathJob", "Split output into multiple gcode files"))
obj.SplitOutput = False
def onChanged(self, obj, prop):
if prop == "PostProcessor" and obj.PostProcessor:
processor = PostProcessor.load(obj.PostProcessor)
@@ -397,7 +414,7 @@ class ObjectJob:
return None
def execute(self, obj):
if hasattr(obj, 'Operations'):
if getattr(obj, 'Operations', None):
obj.Path = obj.Operations.Path
self.getCycleTime()
@@ -445,6 +462,11 @@ class ObjectJob:
self.obj.Operations.Group = group
op.Path.Center = self.obj.Operations.Path.Center
def nextToolNumber(self):
# returns the next available toolnumber in the job
group = self.obj.Tools.Group
return sorted([t.ToolNumber for t in group])[-1] + 1
def addToolController(self, tc):
group = self.obj.Tools.Group
PathLog.debug("addToolController(%s): %s" % (tc.Label, [t.Label for t in group]))
@@ -467,8 +489,11 @@ class ObjectJob:
ops.append(op)
for sub in op.Group:
collectBaseOps(sub)
for op in self.obj.Operations.Group:
collectBaseOps(op)
if getattr(self.obj, 'Operations', None) and getattr(self.obj.Operations, 'Group', None):
for op in self.obj.Operations.Group:
collectBaseOps(op)
return ops
def setCenterOfRotation(self, center):
+2 -1
View File
@@ -213,7 +213,8 @@ class JobCreate:
def setupTemplate(self):
templateFiles = []
for path in PathPreferences.searchPaths():
templateFiles.extend(self.templateFilesIn(path))
cleanPaths = [f.replace("\\", "/") for f in self.templateFilesIn(path)] # Standardize slashes used accross os platforms
templateFiles.extend(cleanPaths)
template = {}
for tFile in templateFiles:
+84 -62
View File
@@ -27,11 +27,11 @@ import math
import traceback
from pivy import coin
from PySide import QtCore, QtGui
import json
import FreeCAD
import FreeCADGui
import PathGui as PGui # ensure Path/Gui/Resources are loaded
import PathScripts.PathJob as PathJob
import PathScripts.PathJobCmd as PathJobCmd
import PathScripts.PathJobDlg as PathJobDlg
@@ -59,17 +59,11 @@ def translate(context, text, disambig=None):
return QtCore.QCoreApplication.translate(context, text, disambig)
LOGLEVEL = False
if LOGLEVEL:
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule()) # lgtm [py/unreachable-statement]
PathLog.trackModule(PathLog.thisModule()) # lgtm [py/unreachable-statement]
else:
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
# PathLog.trackModule(PathLog.thisModule())
def _OpenCloseResourceEditor(obj, vobj, edit):
# pylint: disable=unused-argument
job = PathUtils.findParentJob(obj)
if job and job.ViewObject and job.ViewObject.Proxy:
if edit:
@@ -171,7 +165,6 @@ class ViewProvider:
return hasattr(self, 'deleteOnReject') and self.deleteOnReject
def setEdit(self, vobj=None, mode=0):
# pylint: disable=unused-argument
PathLog.track(mode)
if 0 == mode:
self.openTaskPanel()
@@ -190,7 +183,6 @@ class ViewProvider:
self.taskPanel = None
def unsetEdit(self, arg1, arg2):
# pylint: disable=unused-argument
if self.taskPanel:
self.taskPanel.reject(False)
@@ -204,7 +196,6 @@ class ViewProvider:
return self.openTaskPanel()
def uneditObject(self, obj=None):
# pylint: disable=unused-argument
self.unsetEdit(None, None)
def getIcon(self):
@@ -250,7 +241,6 @@ class ViewProvider:
base.ViewObject.Visibility = True
def forgetBaseVisibility(self, obj, base):
# pylint: disable=unused-argument
if self.baseVisibility.get(base.Name):
visibility = self.baseVisibility[base.Name]
visibility[0].ViewObject.Visibility = visibility[1]
@@ -274,7 +264,6 @@ class ViewProvider:
obj.Stock.ViewObject.Visibility = self.stockVisibility
def setupContextMenu(self, vobj, menu):
# pylint: disable=unused-argument
PathLog.track()
for action in menu.actions():
menu.removeAction(action)
@@ -308,7 +297,7 @@ class StockEdit(object):
widget.hide()
if select:
self.form.stock.setCurrentIndex(self.Index)
editor = self.editorFrame() # pylint: disable=assignment-from-none
editor = self.editorFrame()
showHide(self.form.stockFromExisting, editor)
showHide(self.form.stockFromBase, editor)
showHide(self.form.stockCreateBox, editor)
@@ -370,7 +359,7 @@ class StockFromBaseBoundBoxEdit(StockEdit):
stock.ExtZneg = FreeCAD.Units.Quantity(self.form.stockExtZneg.text())
if 'zpos' in fields:
stock.ExtZpos = FreeCAD.Units.Quantity(self.form.stockExtZpos.text())
except Exception: # pylint: disable=broad-except
except Exception:
pass
def getFields(self, obj, fields=None):
@@ -411,7 +400,7 @@ class StockFromBaseBoundBoxEdit(StockEdit):
self.form.stockExtYpos.textChanged.connect(self.checkYpos)
self.form.stockExtZpos.textChanged.connect(self.checkZpos)
if hasattr(self.form, 'linkStockAndModel'):
self.form.linkStockAndModel.setChecked(True)
self.form.linkStockAndModel.setChecked(False)
def checkXpos(self):
self.trackXpos = self.form.stockExtXneg.text() == self.form.stockExtXpos.text()
@@ -467,7 +456,7 @@ class StockCreateBoxEdit(StockEdit):
obj.Stock.Height = FreeCAD.Units.Quantity(self.form.stockBoxHeight.text())
else:
PathLog.error(translate('PathJob', 'Stock not a box!'))
except Exception: # pylint: disable=broad-except
except Exception:
pass
def setFields(self, obj):
@@ -503,7 +492,7 @@ class StockCreateCylinderEdit(StockEdit):
obj.Stock.Height = FreeCAD.Units.Quantity(self.form.stockCylinderHeight.text())
else:
PathLog.error(translate('PathJob', 'Stock not a cylinder!'))
except Exception: # pylint: disable=broad-except
except Exception:
pass
def setFields(self, obj):
@@ -531,7 +520,7 @@ class StockFromExistingEdit(StockEdit):
stock = self.form.stockExisting.itemData(self.form.stockExisting.currentIndex())
if not (hasattr(obj.Stock, 'Objects') and len(obj.Stock.Objects) == 1 and obj.Stock.Objects[0] == stock):
if stock:
stock = PathJob.createResourceClone(obj, stock, self.StockLabelPrefix , 'Stock')
stock = PathJob.createResourceClone(obj, stock, self.StockLabelPrefix, 'Stock')
stock.ViewObject.Visibility = True
PathStock.SetupStockObject(stock, PathStock.StockType.Unknown)
stock.Proxy.execute(stock)
@@ -557,7 +546,7 @@ class StockFromExistingEdit(StockEdit):
index = -1
for i, solid in enumerate(self.candidates(obj)):
self.form.stockExisting.addItem(solid.Label, solid)
label="{}-{}".format(self.StockLabelPrefix, solid.Label)
label = "{}-{}".format(self.StockLabelPrefix, solid.Label)
if label == stockName:
index = i
@@ -583,6 +572,7 @@ class TaskPanel:
self.deleteOnReject = deleteOnReject
self.form = FreeCADGui.PySideUic.loadUi(":/panels/PathEdit.ui")
self.template = PathJobDlg.JobTemplateExport(self.obj, self.form.jobBox.widget(1))
self.name = self.obj.Name
vUnit = FreeCAD.Units.Quantity(1, FreeCAD.Units.Velocity).getUserPreferred()[2]
self.form.toolControllerList.horizontalHeaderItem(1).setText('#')
@@ -634,12 +624,14 @@ class TaskPanel:
self.setupGlobal.reject()
self.setupOps.reject()
FreeCAD.ActiveDocument.abortTransaction()
if self.deleteOnReject:
if self.deleteOnReject and FreeCAD.ActiveDocument.getObject(self.name):
PathLog.info("Uncreate Job")
FreeCAD.ActiveDocument.openTransaction(translate("Path_Job", "Uncreate Job"))
if self.obj.ViewObject.Proxy.onDelete(self.obj.ViewObject, None):
FreeCAD.ActiveDocument.removeObject(self.obj.Name)
FreeCAD.ActiveDocument.commitTransaction()
else:
PathLog.track(self.name, self.deleteOnReject, FreeCAD.ActiveDocument.getObject(self.name))
self.cleanup(resetEdit)
return True
@@ -680,7 +672,7 @@ class TaskPanel:
if self.form.wcslist.item(i).checkState() == QtCore.Qt.CheckState.Checked:
flist.append(self.form.wcslist.item(i).text())
self.obj.Fixtures = flist
except Exception: # pylint: disable=broad-except
except Exception:
FreeCAD.Console.PrintWarning("The Job was created without fixture support. Please delete and recreate the job\r\n")
self.updateTooltips()
@@ -872,13 +864,33 @@ class TaskPanel:
self.toolControllerSelect()
def toolControllerAdd(self):
# adding a TC from a toolbit directly.
# Try to find a tool number from the currently selected lib. Otherwise
# use next available number
if PathPreferences.toolsUseLegacyTools():
PathToolLibraryEditor.CommandToolLibraryEdit().edit(self.obj, self.updateToolController)
else:
tools = PathToolBitGui.LoadTools()
curLib = PathPreferences.lastFileToolLibrary()
library = None
if curLib is not None:
with open(curLib) as fp:
library = json.load(fp)
for tool in tools:
tc = PathToolControllerGui.Create(name=tool.Label, tool=tool)
toolNum = self.obj.Proxy.nextToolNumber()
if library is not None:
for toolBit in library['tools']:
if toolBit['path'] == tool.File:
toolNum = toolBit['nr']
tc = PathToolControllerGui.Create(name=tool.Label, tool=tool, toolNumber=toolNum)
self.obj.Proxy.addToolController(tc)
FreeCAD.ActiveDocument.recompute()
self.updateToolController()
@@ -894,7 +906,7 @@ class TaskPanel:
elif 'Number' == prop:
try:
tc.ToolNumber = int(item.text())
except Exception: # pylint: disable=broad-except
except Exception:
pass
item.setText("%d" % tc.ToolNumber)
elif 'Spindle' == prop:
@@ -906,7 +918,7 @@ class TaskPanel:
speed = -speed
tc.SpindleDir = rot
tc.SpindleSpeed = speed
except Exception: # pylint: disable=broad-except
except Exception:
pass
item.setText("%s%g" % ('+' if tc.SpindleDir == 'Forward' else '-', tc.SpindleSpeed))
elif 'HorizFeed' == prop or 'VertFeed' == prop:
@@ -918,58 +930,69 @@ class TaskPanel:
elif FreeCAD.Units.Unit() == val.Unit:
val = FreeCAD.Units.Quantity(item.text() + vUnit)
setattr(tc, prop, val)
except Exception: # pylint: disable=broad-except
except Exception:
pass
item.setText("%g" % getattr(tc, prop).getValueAs(vUnit))
else:
try:
val = FreeCAD.Units.Quantity(item.text())
setattr(tc, prop, val)
except Exception: # pylint: disable=broad-except
except Exception:
pass
item.setText("%g" % getattr(tc, prop).Value)
self.template.updateUI()
def modelSetAxis(self, axis):
def alignSel(sel, n, flip=False):
PathLog.debug("alignSel")
PathLog.track(axis)
def alignSel(sel, normal, flip=False):
PathLog.track("Vector(%.2f, %.2f, %.2f)" % (normal.x, normal.y, normal.z), flip)
vector = axis
if flip:
vector = n.negative()
r = axis.cross(n) # rotation axis
a = DraftVecUtils.angle(n, vector, r) * 180 / math.pi
vector = axis.negative()
r = axis.cross(normal) # rotation axis
a = DraftVecUtils.angle(normal, vector, r) * 180 / math.pi
PathLog.debug("oh boy: (%.2f, %.2f, %.2f) -> %.2f" % (r.x, r.y, r.z, a))
Draft.rotate(sel.Object, a, axis=r)
selObject = None
selFeature = None
for sel in FreeCADGui.Selection.getSelectionEx():
selObject = sel.Object
for feature in sel.SubElementNames:
selFeature = feature
sub = sel.Object.Shape.getElement(feature)
if 'Face' == sub.ShapeType:
n = sub.normalAt(0, 0).negative()
if sub.Orientation == 'Reversed':
n = FreeCAD.Vector() - n
PathLog.debug("(%.2f, %.2f, %.2f) -> reversed (%s)" % (n.x, n.y, n.z, sub.Orientation))
else:
PathLog.debug("(%.2f, %.2f, %.2f) -> forward (%s)" % (n.x, n.y, n.z, sub.Orientation))
with selectionEx() as selection:
for sel in selection:
selObject = sel.Object
for feature in sel.SubElementNames:
selFeature = feature
PathLog.track(selObject.Label, feature)
sub = sel.Object.Shape.getElement(feature)
if PathGeom.pointsCoincide(axis, n) or PathGeom.pointsCoincide(axis, FreeCAD.Vector() - n):
alignSel(sel, n, True)
else:
alignSel(sel, n)
if 'Face' == sub.ShapeType:
normal = sub.normalAt(0, 0)
if sub.Orientation == 'Reversed':
normal = FreeCAD.Vector() - normal
PathLog.debug("(%.2f, %.2f, %.2f) -> reversed (%s)" % (normal.x, normal.y, normal.z, sub.Orientation))
else:
PathLog.debug("(%.2f, %.2f, %.2f) -> forward (%s)" % (normal.x, normal.y, normal.z, sub.Orientation))
if PathGeom.pointsCoincide(axis, normal):
alignSel(sel, normal, True)
elif PathGeom.pointsCoincide(axis, FreeCAD.Vector() - normal):
alignSel(sel, FreeCAD.Vector() - normal, True)
else:
alignSel(sel, normal)
elif 'Edge' == sub.ShapeType:
normal = (sub.Vertexes[1].Point - sub.Vertexes[0].Point).normalize()
if PathGeom.pointsCoincide(axis, normal) or PathGeom.pointsCoincide(axis, FreeCAD.Vector() - normal):
# Don't really know the orientation of an edge, so let's just flip the object
# and if the user doesn't like it they can flip again
alignSel(sel, normal, True)
else:
alignSel(sel, normal)
if 'Edge' == sub.ShapeType:
n = (sub.Vertexes[1].Point - sub.Vertexes[0].Point).normalize()
if PathGeom.pointsCoincide(axis, n) or PathGeom.pointsCoincide(axis, FreeCAD.Vector() - n):
# Don't really know the orientation of an edge, so let's just flip the object
# and if the user doesn't like it they can flip again
alignSel(sel, n, True)
else:
alignSel(sel, n)
PathLog.track(sub.ShapeType)
if selObject and selFeature:
FreeCADGui.Selection.clearSelection()
FreeCADGui.Selection.addSelection(selObject, selFeature)
@@ -980,10 +1003,13 @@ class TaskPanel:
FreeCADGui.Selection.addSelection(sel.Object, sel.SubElementNames)
def modelSet0(self, axis):
PathLog.track(axis)
with selectionEx() as selection:
for sel in selection:
selObject = sel.Object
PathLog.track(selObject.Label)
for name in sel.SubElementNames:
PathLog.track(selObject.Label, name)
feature = selObject.Shape.getElement(name)
bb = feature.BoundBox
offset = FreeCAD.Vector(axis.x * bb.XMax, axis.y * bb.YMax, axis.z * bb.ZMax)
@@ -1216,7 +1242,7 @@ class TaskPanel:
# first remove all obsolete base models
for model, count in PathUtil.keyValueIter(obsolete):
for i in range(count): # pylint: disable=unused-variable
for i in range(count):
# it seems natural to remove the last of all the base objects for a given model
base = [b for b in obj.Model.Group if proxy.baseObject(obj, b) == model][-1]
self.vproxy.forgetBaseVisibility(obj, base)
@@ -1338,19 +1364,15 @@ class TaskPanel:
# SelectionObserver interface
def addSelection(self, doc, obj, sub, pnt):
# pylint: disable=unused-argument
self.updateSelection()
def removeSelection(self, doc, obj, sub):
# pylint: disable=unused-argument
self.updateSelection()
def setSelection(self, doc):
# pylint: disable=unused-argument
self.updateSelection()
def clearSelection(self, doc):
# pylint: disable=unused-argument
self.updateSelection()
@@ -1366,7 +1388,7 @@ def Create(base, template=None):
obj.Document.recompute()
obj.ViewObject.Proxy.editObject(obj.Stock)
return obj
except Exception as exc: # pylint: disable=broad-except
except Exception as exc:
PathLog.error(exc)
traceback.print_exc()
FreeCAD.ActiveDocument.abortTransaction()
+9 -5
View File
@@ -195,16 +195,20 @@ class ObjectFace(PathPocketBase.ObjectPocket):
import PathScripts.PathSurfaceSupport as PathSurfaceSupport
baseShape = oneBase[0].Shape
psZMin = planeshape.BoundBox.ZMin
ofstShape = PathUtils.getOffsetArea(planeshape,
self.tool.Diameter * 1.1,
plane=planeshape)
ofst = 0.0
if obj.ClearEdges:
ofst = self.tool.Diameter * 0.51
ofstShape = PathUtils.getOffsetArea(planeshape, ofst, plane=planeshape)
ofstShape.translate(FreeCAD.Vector(0.0, 0.0, psZMin - ofstShape.BoundBox.ZMin))
# Calculate custom depth params for removal shape envelope, with start and final depth buffers
custDepthparams = self._customDepthParams(obj, obj.StartDepth.Value + 0.2, obj.FinalDepth.Value - 0.1) # only an envelope
ofstShapeEnv = PathUtils.getEnvelope(partshape=ofstShape, depthparams=custDepthparams)
env = ofstShapeEnv.cut(baseShape)
env.translate(FreeCAD.Vector(0.0, 0.0, -0.000001)) # lower removal shape into buffer zone
if obj.ExcludeRaisedAreas:
env = ofstShapeEnv.cut(baseShape)
env.translate(FreeCAD.Vector(0.0, 0.0, -0.00001)) # lower removal shape into buffer zone
else:
env = ofstShapeEnv
if holeShape:
PathLog.debug("Processing holes and face ...")
+12 -1
View File
@@ -1113,7 +1113,7 @@ class TaskPanel(object):
def accept(self, resetEdit=True):
'''accept() ... callback invoked when user presses the task panel OK button.'''
self.preCleanup()
if self.isDirty:
if self.isDirty():
self.panelGetFields()
FreeCAD.ActiveDocument.commitTransaction()
self.cleanup(resetEdit)
@@ -1207,7 +1207,18 @@ class TaskPanel(object):
page.clearBase()
page.addBaseGeometry(sel)
# Update properties based upon expressions in case expression value has changed
for (prp, expr) in self.obj.ExpressionEngine:
val = FreeCAD.Units.Quantity(self.obj.evalExpression(expr))
value = val.Value if hasattr(val, 'Value') else val
prop = getattr(self.obj, prp)
if hasattr(prop, "Value"):
prop.Value = value
else:
prop = value
self.panelSetFields()
for page in self.featurePages:
page.pageRegisterSignalHandlers()
@@ -391,6 +391,10 @@ class ObjectPocket(PathPocketBase.ObjectPocket):
self.horiz.append(face)
self.exts.append(face)
# Place all self.horiz faces into same working plane
for h in self.horiz:
h.translate(FreeCAD.Vector(0.0, 0.0, 0.0 - h.BoundBox.ZMin))
# check all faces and see if they are touching/overlapping and combine those into a compound
self.horizontal = [] # pylint: disable=attribute-defined-outside-init
for shape in PathGeom.combineConnectedShapes(self.horiz):
+11 -22
View File
@@ -158,7 +158,7 @@ class CommandPathPost:
if openDialog:
foo = QtGui.QFileDialog.getSaveFileName(QtGui.QApplication.activeWindow(), "Output File", filename)
if foo:
if foo[0]:
filename = foo[0]
else:
filename = None
@@ -236,7 +236,7 @@ class CommandPathPost:
elif hasattr(sel, "Path"):
try:
job = PathUtils.findParentJob(sel)
except Exception: # pylint: disable=broad-except
except Exception:
job = None
else:
job = None
@@ -261,22 +261,9 @@ class CommandPathPost:
PathLog.debug("about to postprocess job: {}".format(job.Name))
# Build up an ordered list of operations and tool changes.
# Then post-the ordered list
if hasattr(job, "Fixtures"):
wcslist = job.Fixtures
else:
wcslist = ['G54']
if hasattr(job, "OrderOutputBy"):
orderby = job.OrderOutputBy
else:
orderby = "Operation"
if hasattr(job, "SplitOutput"):
split = job.SplitOutput
else:
split = False
wcslist = job.Fixtures
orderby = job.OrderOutputBy
split = job.SplitOutput
postlist = []
@@ -301,7 +288,7 @@ class CommandPathPost:
for obj in job.Operations.Group:
tc = PathUtil.toolControllerForOp(obj)
if tc is not None and PathUtil.opProperty(obj, 'Active'):
if tc.ToolNumber != currTool:
if tc.ToolNumber != currTool or split is True:
sublist.append(tc)
PathLog.debug("Appending TC: {}".format(tc.Name))
currTool = tc.ToolNumber
@@ -332,7 +319,7 @@ class CommandPathPost:
for idx, obj in enumerate(job.Operations.Group):
# check if the operation is active
active = PathUtil.opProperty(obj, 'Active')
active = PathUtil.opProperty(obj, 'Active')
tc = PathUtil.toolControllerForOp(obj)
if tc is None or tc.ToolNumber == currTool and active:
@@ -380,17 +367,19 @@ class CommandPathPost:
firstFixture = False
tc = PathUtil.toolControllerForOp(obj)
if tc is not None:
if tc.ToolNumber != currTool:
if job.SplitOutput or (tc.ToolNumber != currTool):
sublist.append(tc)
currTool = tc.ToolNumber
sublist.append(obj)
postlist.append(sublist)
fail = True
rc = '' # pylint: disable=unused-variable
rc = ''
if split:
for slist in postlist:
(fail, rc, filename) = self.exportObjectsWith(slist, job)
if fail:
break
else:
finalpostlist = [item for slist in postlist for item in slist]
(fail, rc, filename) = self.exportObjectsWith(finalpostlist, job)
-1
View File
@@ -448,7 +448,6 @@ class ObjectProfile(PathAreaOp.ObjectOp):
else: # Try to build targets from the job models
# No base geometry selected, so treating operation like a exterior contour operation
self.opUpdateDepths(obj)
obj.Side = 'Outside' # Force outside for whole model profile
if 1 == len(self.model) and hasattr(self.model[0], "Proxy"):
if isinstance(self.model[0].Proxy, ArchPanel.PanelSheet): # process the sheet
+341 -245
View File
@@ -22,11 +22,11 @@
# * *
# ***************************************************************************
'''
"""
This file has utilities for checking and catching common errors in FreeCAD
Path projects. Ideally, the user could execute these utilities from an icon
to make sure tools are selected and configured and defaults have been revised
'''
"""
from __future__ import print_function
from PySide import QtCore, QtGui
@@ -40,6 +40,7 @@ from collections import Counter
from datetime import datetime
import os
import webbrowser
# Qt translation handling
@@ -47,13 +48,12 @@ def translate(context, text, disambig=None):
return QtCore.QCoreApplication.translate(context, text, disambig)
LOG_MODULE = 'PathSanity'
LOG_MODULE = "PathSanity"
# PathLog.setLevel(PathLog.Level.INFO, LOG_MODULE)
# PathLog.trackModule('PathSanity')
class CommandPathSanity:
def resolveOutputPath(self, job):
if job.PostProcessorOutputFile != "":
filepath = job.PostProcessorOutputFile
@@ -62,48 +62,54 @@ class CommandPathSanity:
else:
filepath = PathPreferences.macroFilePath()
if '%D' in filepath:
if "%D" in filepath:
D = FreeCAD.ActiveDocument.FileName
if D:
D = os.path.dirname(D)
# in case the document is in the current working directory
if not D:
D = '.'
D = "."
else:
FreeCAD.Console.PrintError("Please save document in order to resolve output path!\n")
FreeCAD.Console.PrintError(
"Please save document in order to resolve output path!\n"
)
return None
filepath = filepath.replace('%D', D)
filepath = filepath.replace("%D", D)
if '%d' in filepath:
if "%d" in filepath:
d = FreeCAD.ActiveDocument.Label
filepath = filepath.replace('%d', d)
filepath = filepath.replace("%d", d)
if '%j' in filepath:
if "%j" in filepath:
j = job.Label
filepath = filepath.replace('%j', j)
filepath = filepath.replace("%j", j)
if '%M' in filepath:
if "%M" in filepath:
pref = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Macro")
M = pref.GetString("MacroPath", FreeCAD.getUserAppDataDir())
filepath = filepath.replace('%M', M)
filepath = filepath.replace("%M", M)
PathLog.debug('filepath: {}'.format(filepath))
PathLog.debug("filepath: {}".format(filepath))
# starting at the derived filename, iterate up until we have a valid
# directory to write to
while not os.path.isdir(filepath):
filepath = os.path.dirname(filepath)
PathLog.debug('filepath: {}'.format(filepath))
PathLog.debug("filepath: {}".format(filepath))
return filepath + os.sep
def GetResources(self):
return {'Pixmap': 'Path_Sanity',
'MenuText': QtCore.QT_TRANSLATE_NOOP("Path_Sanity",
"Check the path job for common errors"),
'Accel': "P, S",
'ToolTip': QtCore.QT_TRANSLATE_NOOP("Path_Sanity",
"Check the path job for common errors")}
return {
"Pixmap": "Path_Sanity",
"MenuText": QtCore.QT_TRANSLATE_NOOP(
"Path_Sanity", "Check the path job for common errors"
),
"Accel": "P, S",
"ToolTip": QtCore.QT_TRANSLATE_NOOP(
"Path_Sanity", "Check the path job for common errors"
),
}
def IsActive(self):
obj = FreeCADGui.Selection.getSelectionEx()[0].Object
@@ -141,7 +147,7 @@ class CommandPathSanity:
view.showNormal()
view.resize(320, 320)
imagepath = self.outputpath + '{}'.format(imageName)
imagepath = self.outputpath + "{}".format(imageName)
aview.viewIsometric()
FreeCADGui.Selection.clearSelection()
@@ -149,8 +155,8 @@ class CommandPathSanity:
FreeCADGui.Selection.addSelection(obj)
FreeCADGui.SendMsgToActiveView("ViewSelection")
FreeCADGui.Selection.clearSelection()
aview.saveImage(imagepath + '.png', 320, 320, 'Current')
aview.saveImage(imagepath + '_t.png', 320, 320, 'Transparent')
aview.saveImage(imagepath + ".png", 320, 320, "Current")
aview.saveImage(imagepath + "_t.png", 320, 320, "Transparent")
view.showMaximized()
@@ -230,26 +236,36 @@ class CommandPathSanity:
CustomerLabel = translate("Path_Sanity", "Customer")
DesignerLabel = translate("Path_Sanity", "Designer")
d = data['designData']
b = data['baseData']
d = data["designData"]
b = data["baseData"]
jobname = d['JobLabel']
jobname = d["JobLabel"]
basestable = "!===\n"
for key, val in b['bases'].items():
for key, val in b["bases"].items():
basestable += "! " + key + " ! " + val + "\n"
basestable += "!==="
infoTable += "|*" + PartLabel + "* a| " + basestable + " .7+a|" + \
"image::" + b['baseimage'] + "[" + PartLabel + "]\n"
infoTable += "|*" + SequenceLabel + "*|" + d['Sequence']
infoTable += "|*" + JobTypeLabel + "*|" + d['JobType']
infoTable += "|*" + DescriptionLabel + "*|" + d['JobDescription']
infoTable += "|*" + CADLabel + "*|" + d['FileName']
infoTable += "|*" + LastSaveLabel + "*|" + d['LastModifiedDate']
infoTable += "|*" + CustomerLabel + "*|" + d['Customer']
infoTable += "|*" + DesignerLabel + "*|" + d['Designer']
infoTable += (
"|*"
+ PartLabel
+ "* a| "
+ basestable
+ " .7+a|"
+ "image::"
+ b["baseimage"]
+ "["
+ PartLabel
+ "]\n"
)
infoTable += "|*" + SequenceLabel + "*|" + d["Sequence"]
infoTable += "|*" + JobTypeLabel + "*|" + d["JobType"]
infoTable += "|*" + DescriptionLabel + "*|" + d["JobDescription"]
infoTable += "|*" + CADLabel + "*|" + d["FileName"]
infoTable += "|*" + LastSaveLabel + "*|" + d["LastModifiedDate"]
infoTable += "|*" + CustomerLabel + "*|" + d["Customer"]
infoTable += "|*" + DesignerLabel + "*|" + d["Designer"]
# Generate the markup for the Run Summary Section
@@ -261,22 +277,34 @@ class CommandPathSanity:
coolantLabel = translate("Path_Sanity", "Coolant")
jobTotalLabel = translate("Path_Sanity", "TOTAL JOB")
d = data['runData']
d = data["runData"]
runTable += "|*" + opLabel + "*|*" + zMinLabel + "*|*" + zMaxLabel + \
"*|*" + coolantLabel + "*|*" + cycleTimeLabel + "*\n"
runTable += (
"|*"
+ opLabel
+ "*|*"
+ zMinLabel
+ "*|*"
+ zMaxLabel
+ "*|*"
+ coolantLabel
+ "*|*"
+ cycleTimeLabel
+ "*\n"
)
for i in d['items']:
runTable += "|{}".format(i['opName'])
runTable += "|{}".format(i['minZ'])
runTable += "|{}".format(i['maxZ'])
runTable += "|{}".format(i['coolantMode'])
runTable += "|{}".format(i['cycleTime'])
for i in d["items"]:
runTable += "|{}".format(i["opName"])
runTable += "|{}".format(i["minZ"])
runTable += "|{}".format(i["maxZ"])
runTable += "|{}".format(i["coolantMode"])
runTable += "|{}".format(i["cycleTime"])
runTable += "|*" + jobTotalLabel + "* |{} |{} |{}".format(
d['jobMinZ'],
d['jobMaxZ'],
d['cycletotal'])
runTable += (
"|*"
+ jobTotalLabel
+ "* |{} |{} |{}".format(d["jobMinZ"], d["jobMaxZ"], d["cycletotal"])
)
# Generate the markup for the Tool Data Section
toolTables = ""
@@ -294,26 +322,50 @@ class CommandPathSanity:
shapeLabel = translate("Path_Sanity", "Tool Shape")
diameterLabel = translate("Path_Sanity", "Tool Diameter")
d = data['toolData']
d = data["toolData"]
for key, value in d.items():
toolTables += "=== {}: T{}\n".format(toolLabel, key)
toolTables += "|===\n"
toolTables += "|*{}*| {} a| image::{}[{}]\n".format(descriptionLabel, value['description'], value['imagepath'], key)
toolTables += "|*{}* 2+| {}\n".format(manufLabel, value['manufacturer'])
toolTables += "|*{}* 2+| {}\n".format(partNumberLabel, value['partNumber'])
toolTables += "|*{}* 2+| {}\n".format(urlLabel, value['url'])
toolTables += "|*{}* 2+| {}\n".format(inspectionNotesLabel, value['inspectionNotes'])
toolTables += "|*{}* 2+| {}\n".format(shapeLabel, value['shape'])
toolTables += "|*{}* 2+| {}\n".format(diameterLabel, value['diameter'])
toolTables += "|*{}*| {} a| image::{}[{}]\n".format(
descriptionLabel, value["description"], value["imagepath"], key
)
toolTables += "|*{}* 2+| {}\n".format(manufLabel, value["manufacturer"])
toolTables += "|*{}* 2+| {}\n".format(partNumberLabel, value["partNumber"])
toolTables += "|*{}* 2+| {}\n".format(urlLabel, value["url"])
toolTables += "|*{}* 2+| {}\n".format(
inspectionNotesLabel, value["inspectionNotes"]
)
toolTables += "|*{}* 2+| {}\n".format(shapeLabel, value["shape"])
toolTables += "|*{}* 2+| {}\n".format(diameterLabel, value["diameter"])
toolTables += "|===\n"
toolTables += "|===\n"
toolTables += "|*" + opLabel + "*|*" + tcLabel + "*|*" + feedLabel + "*|*" + speedLabel + "*\n"
for o in value['ops']:
toolTables += "|" + o['Operation'] + "|" + o['ToolController'] + "|" + o['Feed'] + "|" + o['Speed'] + "\n"
toolTables += (
"|*"
+ opLabel
+ "*|*"
+ tcLabel
+ "*|*"
+ feedLabel
+ "*|*"
+ speedLabel
+ "*\n"
)
for o in value["ops"]:
toolTables += (
"|"
+ o["Operation"]
+ "|"
+ o["ToolController"]
+ "|"
+ o["Feed"]
+ "|"
+ o["Speed"]
+ "\n"
)
toolTables += "|===\n"
toolTables += "\n"
@@ -325,15 +377,14 @@ class CommandPathSanity:
zDimLabel = translate("Path_Sanity", "Z Size")
materialLabel = translate("Path_Sanity", "Material")
d = data['stockData']
d = data["stockData"]
stockTable += "|*{}*|{} .4+a|image::{}[stock]\n".format(
materialLabel,
d['material'],
d['stockImage'])
stockTable += "|*{}*|{}".format(xDimLabel, d['xLen'])
stockTable += "|*{}*|{}".format(yDimLabel, d['yLen'])
stockTable += "|*{}*|{}".format(zDimLabel, d['zLen'])
materialLabel, d["material"], d["stockImage"]
)
stockTable += "|*{}*|{}".format(xDimLabel, d["xLen"])
stockTable += "|*{}*|{}".format(yDimLabel, d["yLen"])
stockTable += "|*{}*|{}".format(zDimLabel, d["zLen"])
# Generate the markup for the Fixture Section
@@ -342,16 +393,16 @@ class CommandPathSanity:
orderByLabel = translate("Path_Sanity", "Order By")
datumLabel = translate("Path_Sanity", "Part Datum")
d = data['fixtureData']
d = data["fixtureData"]
fixtureTable += "|*{}*|{}\n".format(offsetsLabel, d['fixtures'])
fixtureTable += "|*{}*|{}\n".format(orderByLabel, d['orderBy'])
fixtureTable += "|*{}* a| image::{}[]".format(datumLabel, d['datumImage'])
fixtureTable += "|*{}*|{}\n".format(offsetsLabel, d["fixtures"])
fixtureTable += "|*{}*|{}\n".format(orderByLabel, d["orderBy"])
fixtureTable += "|*{}* a| image::{}[]".format(datumLabel, d["datumImage"])
# Generate the markup for the Output Section
outTable = ""
d = data['outputData']
d = data["outputData"]
gcodeFileLabel = translate("Path_Sanity", "Gcode File")
lastpostLabel = translate("Path_Sanity", "Last Post Process Date")
@@ -363,15 +414,15 @@ class CommandPathSanity:
fileSizeLabel = translate("Path_Sanity", "File Size (kbs)")
lineCountLabel = translate("Path_Sanity", "Line Count")
outTable += "|*{}*|{}\n".format(gcodeFileLabel, d['lastgcodefile'])
outTable += "|*{}*|{}\n".format(lastpostLabel, d['lastpostprocess'])
outTable += "|*{}*|{}\n".format(stopsLabel, d['optionalstops'])
outTable += "|*{}*|{}\n".format(programmerLabel, d['programmer'])
outTable += "|*{}*|{}\n".format(machineLabel, d['machine'])
outTable += "|*{}*|{}\n".format(postLabel, d['postprocessor'])
outTable += "|*{}*|{}\n".format(flagsLabel, d['postprocessorFlags'])
outTable += "|*{}*|{}\n".format(fileSizeLabel, d['filesize'])
outTable += "|*{}*|{}\n".format(lineCountLabel, d['linecount'])
outTable += "|*{}*|{}\n".format(gcodeFileLabel, d["lastgcodefile"])
outTable += "|*{}*|{}\n".format(lastpostLabel, d["lastpostprocess"])
outTable += "|*{}*|{}\n".format(stopsLabel, d["optionalstops"])
outTable += "|*{}*|{}\n".format(programmerLabel, d["programmer"])
outTable += "|*{}*|{}\n".format(machineLabel, d["machine"])
outTable += "|*{}*|{}\n".format(postLabel, d["postprocessor"])
outTable += "|*{}*|{}\n".format(flagsLabel, d["postprocessorFlags"])
outTable += "|*{}*|{}\n".format(fileSizeLabel, d["filesize"])
outTable += "|*{}*|{}\n".format(lineCountLabel, d["linecount"])
# Generate the markup for the Squawk Section
@@ -379,15 +430,13 @@ class CommandPathSanity:
operatorLabel = translate("Path_Sanity", "Operator")
dateLabel = translate("Path_Sanity", "Date")
squawkTable = "|*{}*|*{}*|*{}*\n".format(noteLabel,
operatorLabel,
dateLabel)
squawkTable = "|*{}*|*{}*|*{}*\n".format(noteLabel, operatorLabel, dateLabel)
d = data['squawkData']
for i in d['items']:
squawkTable += "a|{}: {}".format(i['squawkType'], i['Note'])
squawkTable += "|{}".format(i['Operator'])
squawkTable += "|{}".format(i['Date'])
d = data["squawkData"]
for i in d["items"]:
squawkTable += "a|{}: {}".format(i["squawkType"], i["Note"])
squawkTable += "|{}".format(i["Operator"])
squawkTable += "|{}".format(i["Date"])
squawkTable += "\n"
# merge template and custom markup
@@ -400,20 +449,22 @@ class CommandPathSanity:
stockTable=stockTable,
fixtureTable=fixtureTable,
outTable=outTable,
squawkTable=squawkTable)
squawkTable=squawkTable,
)
# Save the report
reportraw = self.outputpath + 'setupreport.asciidoc'
reporthtml = self.outputpath + 'setupreport.html'
with open(reportraw, 'w') as fd:
reportraw = self.outputpath + data["outputData"]["outputfilename"] + ".asciidoc"
reporthtml = self.outputpath + data["outputData"]["outputfilename"] + ".html"
with open(reportraw, "w") as fd:
fd.write(report)
fd.close()
FreeCAD.Console.PrintMessage('asciidoc file written to {}\n'.format(reportraw))
FreeCAD.Console.PrintMessage(
"asciidoc file written to {}\n".format(reportraw)
)
try:
result = os.system('asciidoctor {} -o {}'.format(reportraw,
reporthtml))
result = os.system("asciidoctor {} -o {}".format(reportraw, reporthtml))
if str(result) == "32512":
msg = "asciidoctor not found. html cannot be generated."
QtGui.QMessageBox.information(None, "Path Sanity", msg)
@@ -431,40 +482,46 @@ class CommandPathSanity:
Returns a dictionary of sections
"""
data = {}
data['baseData'] = self.__baseObjectData(obj)
data['designData'] = self.__designData(obj)
data['toolData'] = self.__toolData(obj)
data['runData'] = self.__runData(obj)
data['outputData'] = self.__outputData(obj)
data['fixtureData'] = self.__fixtureData(obj)
data['stockData'] = self.__stockData(obj)
data['squawkData'] = self.squawkData
data["baseData"] = self.__baseObjectData(obj)
data["designData"] = self.__designData(obj)
data["toolData"] = self.__toolData(obj)
data["runData"] = self.__runData(obj)
data["outputData"] = self.__outputData(obj)
data["fixtureData"] = self.__fixtureData(obj)
data["stockData"] = self.__stockData(obj)
data["squawkData"] = self.squawkData
return data
def squawk(self, operator, note, date=datetime.now(), squawkType="NOTE"):
squawkType = squawkType if squawkType in \
["NOTE", "WARNING", "CAUTION", "TIP"] else "NOTE"
squawkType = (
squawkType
if squawkType in ["NOTE", "WARNING", "CAUTION", "TIP"]
else "NOTE"
)
self.squawkData['items'].append({"Date": str(date),
"Operator": operator,
"Note": note,
"squawkType": squawkType})
self.squawkData["items"].append(
{
"Date": str(date),
"Operator": operator,
"Note": note,
"squawkType": squawkType,
}
)
def __baseObjectData(self, obj):
data = {'baseimage': '',
'bases': ''}
data = {"baseimage": "", "bases": ""}
try:
bases = {}
for name, count in \
PathUtil.keyValueIter(Counter([obj.Proxy.baseObject(obj,
o).Label for o in obj.Model.Group])):
for name, count in PathUtil.keyValueIter(
Counter([obj.Proxy.baseObject(obj, o).Label for o in obj.Model.Group])
):
bases[name] = str(count)
data['baseimage'] = self.__makePicture(obj.Model, "baseimage")
data['bases'] = bases
data["baseimage"] = self.__makePicture(obj.Model, "baseimage")
data["bases"] = bases
except Exception as e:
data['errors'] = e
data["errors"] = e
self.squawk("PathSanity(__baseObjectData)", e, squawkType="CAUTION")
return data
@@ -475,21 +532,23 @@ class CommandPathSanity:
Returns information about issues and concerns (squawks)
"""
data = {'FileName': '',
'LastModifiedDate': '',
'Customer': '',
'Designer': '',
'JobDescription': '',
'JobLabel': '',
'Sequence': '',
'JobType': ''}
data = {
"FileName": "",
"LastModifiedDate": "",
"Customer": "",
"Designer": "",
"JobDescription": "",
"JobLabel": "",
"Sequence": "",
"JobType": "",
}
try:
data['FileName'] = obj.Document.FileName
data['LastModifiedDate'] = str(obj.Document.LastModifiedDate)
data['Customer'] = obj.Document.Company
data['Designer'] = obj.Document.LastModifiedBy
data['JobDescription'] = obj.Description
data['JobLabel'] = obj.Label
data["FileName"] = obj.Document.FileName
data["LastModifiedDate"] = str(obj.Document.LastModifiedDate)
data["Customer"] = obj.Document.Company
data["Designer"] = obj.Document.LastModifiedBy
data["JobDescription"] = obj.Description
data["JobLabel"] = obj.Label
n = 0
m = 0
@@ -499,11 +558,11 @@ class CommandPathSanity:
m += 1
if i is obj:
n = m
data['Sequence'] = "{} of {}".format(n, m)
data['JobType'] = "2.5D Milling" # improve after job types added
data["Sequence"] = "{} of {}".format(n, m)
data["JobType"] = "2.5D Milling" # improve after job types added
except Exception as e:
data['errors'] = e
data["errors"] = e
self.squawk("PathSanity(__designData)", e, squawkType="CAUTION")
return data
@@ -518,83 +577,102 @@ class CommandPathSanity:
try:
for TC in obj.Tools.Group:
if not hasattr(TC.Tool, 'BitBody'):
self.squawk("PathSanity",
"Tool number {} is a legacy tool. Legacy tools not \
supported by Path-Sanity".format(TC.ToolNumber),
squawkType="WARNING")
if not hasattr(TC.Tool, "BitBody"):
self.squawk(
"PathSanity",
"Tool number {} is a legacy tool. Legacy tools not \
supported by Path-Sanity".format(
TC.ToolNumber
),
squawkType="WARNING",
)
continue # skip old-style tools
tooldata = data.setdefault(str(TC.ToolNumber), {})
bitshape = tooldata.setdefault('BitShape', "")
bitshape = tooldata.setdefault("BitShape", "")
if bitshape not in ["", TC.Tool.BitShape]:
self.squawk("PathSanity",
"Tool number {} used by multiple tools".format(TC.ToolNumber),
squawkType="CAUTION")
tooldata['bitShape'] = TC.Tool.BitShape
tooldata['description'] = TC.Tool.Label
tooldata['manufacturer'] = ""
tooldata['url'] = ""
tooldata['inspectionNotes'] = ""
tooldata['diameter'] = str(TC.Tool.Diameter)
tooldata['shape'] = TC.Tool.ShapeName
self.squawk(
"PathSanity",
"Tool number {} used by multiple tools".format(TC.ToolNumber),
squawkType="CAUTION",
)
tooldata["bitShape"] = TC.Tool.BitShape
tooldata["description"] = TC.Tool.Label
tooldata["manufacturer"] = ""
tooldata["url"] = ""
tooldata["inspectionNotes"] = ""
tooldata["diameter"] = str(TC.Tool.Diameter)
tooldata["shape"] = TC.Tool.ShapeName
tooldata['partNumber'] = ""
tooldata["partNumber"] = ""
imagedata = TC.Tool.Proxy.getBitThumbnail(TC.Tool)
imagepath = '{}T{}.png'.format(self.outputpath, TC.ToolNumber)
tooldata['feedrate'] = str(TC.HorizFeed)
imagepath = "{}T{}.png".format(self.outputpath, TC.ToolNumber)
tooldata["feedrate"] = str(TC.HorizFeed)
if TC.HorizFeed.Value == 0.0:
self.squawk("PathSanity",
self.squawk(
"PathSanity",
"Tool Controller '{}' has no feedrate".format(TC.Label),
squawkType="WARNING")
squawkType="WARNING",
)
tooldata['spindlespeed'] = str(TC.SpindleSpeed)
tooldata["spindlespeed"] = str(TC.SpindleSpeed)
if TC.SpindleSpeed == 0.0:
self.squawk("PathSanity",
self.squawk(
"PathSanity",
"Tool Controller '{}' has no spindlespeed".format(TC.Label),
squawkType="WARNING")
squawkType="WARNING",
)
with open(imagepath, 'wb') as fd:
with open(imagepath, "wb") as fd:
fd.write(imagedata)
fd.close()
tooldata['imagepath'] = imagepath
tooldata["imagepath"] = imagepath
used = False
for op in obj.Operations.Group:
if hasattr(op, "ToolController") and op.ToolController is TC:
used = True
tooldata.setdefault('ops', []).append(
{"Operation": op.Label,
"ToolController": TC.Label,
"Feed": str(TC.HorizFeed),
"Speed": str(TC.SpindleSpeed)})
tooldata.setdefault("ops", []).append(
{
"Operation": op.Label,
"ToolController": TC.Label,
"Feed": str(TC.HorizFeed),
"Speed": str(TC.SpindleSpeed),
}
)
if used is False:
tooldata.setdefault('ops', [])
self.squawk("PathSanity",
tooldata.setdefault("ops", [])
self.squawk(
"PathSanity",
"Tool Controller '{}' is not used".format(TC.Label),
squawkType="WARNING")
squawkType="WARNING",
)
except Exception as e:
data['errors'] = e
data["errors"] = e
self.squawk("PathSanity(__toolData)", e, squawkType="CAUTION")
return data
def __runData(self, obj):
data = {'cycletotal': '',
'jobMinZ': '',
'jobMaxZ': '',
'jobDescription': '',
'items': []}
data = {
"cycletotal": "",
"jobMinZ": "",
"jobMaxZ": "",
"jobDescription": "",
"items": [],
}
try:
data['cycletotal'] = str(obj.CycleTime)
data['jobMinZ'] = FreeCAD.Units.Quantity(obj.Path.BoundBox.ZMin,
FreeCAD.Units.Length).UserString
data['jobMaxZ'] = FreeCAD.Units.Quantity(obj.Path.BoundBox.ZMax,
FreeCAD.Units.Length).UserString
data['jobDescription'] = obj.Description
data["cycletotal"] = str(obj.CycleTime)
data["jobMinZ"] = FreeCAD.Units.Quantity(
obj.Path.BoundBox.ZMin, FreeCAD.Units.Length
).UserString
data["jobMaxZ"] = FreeCAD.Units.Quantity(
obj.Path.BoundBox.ZMax, FreeCAD.Units.Length
).UserString
data["jobDescription"] = obj.Description
data['items'] = []
data["items"] = []
for op in obj.Operations.Group:
oplabel = op.Label
@@ -605,69 +683,79 @@ class CommandPathSanity:
while len(o.ViewObject.claimChildren()) != 0: # dressup
oplabel = "{}:{}".format(oplabel, o.Base.Label)
o = o.Base
if hasattr(o, 'CycleTime'):
if hasattr(o, "CycleTime"):
ctime = o.CycleTime
cool = o.CoolantMode if hasattr(o, "CoolantMode") else cool
if hasattr(op, 'Active') and not op.Active:
if hasattr(op, "Active") and not op.Active:
oplabel = "{} (INACTIVE)".format(oplabel)
ctime = 0.0
if op.Path.BoundBox.isValid():
zmin = FreeCAD.Units.Quantity(op.Path.BoundBox.ZMin,
FreeCAD.Units.Length).UserString
zmax = FreeCAD.Units.Quantity(op.Path.BoundBox.ZMax,
FreeCAD.Units.Length).UserString
zmin = FreeCAD.Units.Quantity(
op.Path.BoundBox.ZMin, FreeCAD.Units.Length
).UserString
zmax = FreeCAD.Units.Quantity(
op.Path.BoundBox.ZMax, FreeCAD.Units.Length
).UserString
else:
zmin = ''
zmax = ''
zmin = ""
zmax = ""
opdata = {"opName": oplabel,
"minZ": zmin,
"maxZ": zmax,
"cycleTime": ctime,
"coolantMode": cool}
data['items'].append(opdata)
opdata = {
"opName": oplabel,
"minZ": zmin,
"maxZ": zmax,
"cycleTime": ctime,
"coolantMode": cool,
}
data["items"].append(opdata)
except Exception as e:
data['errors'] = e
data["errors"] = e
self.squawk("PathSanity(__runData)", e, squawkType="CAUTION")
return data
def __stockData(self, obj):
data = {'xLen': '',
'yLen': '',
'zLen': '',
'material': '',
'stockImage': ''}
data = {"xLen": "", "yLen": "", "zLen": "", "material": "", "stockImage": ""}
try:
bb = obj.Stock.Shape.BoundBox
data['xLen'] = FreeCAD.Units.Quantity(bb.XLength, FreeCAD.Units.Length).UserString
data['yLen'] = FreeCAD.Units.Quantity(bb.YLength, FreeCAD.Units.Length).UserString
data['zLen'] = FreeCAD.Units.Quantity(bb.ZLength, FreeCAD.Units.Length).UserString
data["xLen"] = FreeCAD.Units.Quantity(
bb.XLength, FreeCAD.Units.Length
).UserString
data["yLen"] = FreeCAD.Units.Quantity(
bb.YLength, FreeCAD.Units.Length
).UserString
data["zLen"] = FreeCAD.Units.Quantity(
bb.ZLength, FreeCAD.Units.Length
).UserString
data['material'] = "Not Specified"
if hasattr(obj.Stock, 'Material'):
data["material"] = "Not Specified"
if hasattr(obj.Stock, "Material"):
if obj.Stock.Material is not None:
data['material'] = obj.Stock.Material.Material['Name']
data["material"] = obj.Stock.Material.Material["Name"]
if data['material'] == "Not Specified":
self.squawk("PathSanity", "Consider Specifying the Stock Material", squawkType="TIP")
if data["material"] == "Not Specified":
self.squawk(
"PathSanity",
"Consider Specifying the Stock Material",
squawkType="TIP",
)
data['stockImage'] = self.__makePicture(obj.Stock, "stockImage")
data["stockImage"] = self.__makePicture(obj.Stock, "stockImage")
except Exception as e:
data['errors'] = e
data["errors"] = e
self.squawk("PathSanity(__stockData)", e, squawkType="CAUTION")
return data
def __fixtureData(self, obj):
data = {'fixtures': '', 'orderBy': '', 'datumImage': ''}
data = {"fixtures": "", "orderBy": "", "datumImage": ""}
try:
data['fixtures'] = str(obj.Fixtures)
data['orderBy'] = str(obj.OrderOutputBy)
data["fixtures"] = str(obj.Fixtures)
data["orderBy"] = str(obj.OrderOutputBy)
aview = FreeCADGui.activeDocument().activeView()
aview.setAnimationEnabled(False)
@@ -681,7 +769,7 @@ class CommandPathSanity:
view.showNormal()
view.resize(320, 320)
imagepath = '{}origin'.format(self.outputpath)
imagepath = "{}origin".format(self.outputpath)
FreeCADGui.Selection.clearSelection()
FreeCADGui.SendMsgToActiveView("PerspectiveCamera")
@@ -691,8 +779,8 @@ class CommandPathSanity:
FreeCADGui.SendMsgToActiveView("ViewSelection")
FreeCADGui.Selection.clearSelection()
obj.ViewObject.Proxy.editObject(obj)
aview.saveImage('{}.png'.format(imagepath), 320, 320, 'Current')
aview.saveImage('{}_t.png'.format(imagepath), 320, 320, 'Transparent')
aview.saveImage("{}.png".format(imagepath), 320, 320, "Current")
aview.saveImage("{}_t.png".format(imagepath), 320, 320, "Transparent")
obj.ViewObject.Proxy.uneditObject(obj)
obj.Visibility = True
obj.Operations.Visibility = True
@@ -700,47 +788,55 @@ class CommandPathSanity:
view.showMaximized()
aview.setAnimationEnabled(True)
data['datumImage'] = '{}_t.png'.format(imagepath)
data["datumImage"] = "{}_t.png".format(imagepath)
except Exception as e:
data['errors'] = e
data["errors"] = e
self.squawk("PathSanity(__fixtureData)", e, squawkType="CAUTION")
return data
def __outputData(self, obj):
data = {'lastpostprocess': '',
'lastgcodefile': '',
'optionalstops': '',
'programmer': '',
'machine': '',
'postprocessor': '',
'postprocessorFlags': '',
'filesize': '',
'linecount': ''}
data = {
"lastpostprocess": "",
"lastgcodefile": "",
"optionalstops": "",
"programmer": "",
"machine": "",
"postprocessor": "",
"postprocessorFlags": "",
"filesize": "",
"linecount": "",
"outputfilename": "setupreport",
}
try:
data['lastpostprocess'] = str(obj.LastPostProcessDate)
data['lastgcodefile'] = str(obj.LastPostProcessOutput)
data['optionalstops'] = "False"
data['programmer'] = ""
data['machine'] = ""
data['postprocessor'] = str(obj.PostProcessor)
data['postprocessorFlags'] = str(obj.PostProcessorArgs)
data["lastpostprocess"] = str(obj.LastPostProcessDate)
data["lastgcodefile"] = str(obj.LastPostProcessOutput)
data["optionalstops"] = "False"
data["programmer"] = ""
data["machine"] = ""
data["postprocessor"] = str(obj.PostProcessor)
data["postprocessorFlags"] = str(obj.PostProcessorArgs)
if obj.PostProcessorOutputFile != "":
fname = obj.PostProcessorOutputFile
data["outputfilename"] = os.path.splitext(os.path.basename(fname))[0]
for op in obj.Operations.Group:
if isinstance(op.Proxy, PathScripts.PathStop.Stop) and op.Stop is True:
data['optionalstops'] = "True"
data["optionalstops"] = "True"
if obj.LastPostProcessOutput == "":
data['filesize'] = str(0.0)
data['linecount'] = str(0)
data["filesize"] = str(0.0)
data["linecount"] = str(0)
self.squawk("PathSanity", "The Job has not been post-processed")
else:
data['filesize'] = str(os.path.getsize(obj.LastPostProcessOutput))
data['linecount'] = str(sum(1 for line in open(obj.LastPostProcessOutput)))
data["filesize"] = str(os.path.getsize(obj.LastPostProcessOutput))
data["linecount"] = str(
sum(1 for line in open(obj.LastPostProcessOutput))
)
except Exception as e:
data['errors'] = e
data["errors"] = e
self.squawk("PathSanity(__outputData)", e, squawkType="CAUTION")
return data
@@ -748,4 +844,4 @@ class CommandPathSanity:
if FreeCAD.GuiUp:
# register the FreeCAD command
FreeCADGui.addCommand('Path_Sanity', CommandPathSanity())
FreeCADGui.addCommand("Path_Sanity", CommandPathSanity())
+1 -1
View File
@@ -34,7 +34,7 @@ __doc__ = "A container for all default values and job specific configuration val
_RegisteredOps = {}
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
# PathLog.trackModule(PathLog.thisModule())
+4 -1
View File
@@ -380,7 +380,10 @@ class ToolBit(object):
if PathPreferences.toolsStoreAbsolutePaths():
attrs['shape'] = obj.BitShape
else:
attrs['shape'] = findRelativePathShape(obj.BitShape)
# attrs['shape'] = findRelativePathShape(obj.BitShape)
# Extract the name of the shape file
__, filShp = os.path.split(obj.BitShape) # __ is an ignored placeholder acknowledged by LGTM
attrs['shape'] = str(filShp)
params = {}
for name in obj.BitPropertyNames:
params[name] = PathUtil.getPropertyValueString(obj, name)
@@ -620,9 +620,13 @@ class ToolBitLibrary(object):
toolNr = self.toolModel.data(self.toolModel.index(row, 0), PySide.QtCore.Qt.EditRole)
toolPath = self.toolModel.data(self.toolModel.index(row, 0), _PathRole)
if PathPreferences.toolsStoreAbsolutePaths():
tools.append({'nr': toolNr, 'path': toolPath})
bitPath = toolPath
else:
tools.append({'nr': toolNr, 'path': PathToolBit.findRelativePathTool(toolPath)})
# bitPath = PathToolBit.findRelativePathTool(toolPath)
# Extract the name of the shape file
__, filShp = os.path.split(toolPath) # __ is an ignored placeholder acknowledged by LGTM
bitPath = str(filShp)
tools.append({'nr': toolNr, 'path': bitPath})
if self.path is not None:
with open(self.path, 'w') as fp:
+1 -1
View File
@@ -186,7 +186,7 @@ class _Geometry(object):
def _calculate_depth(MIC, geom):
# given a maximum inscribed circle (MIC) and tool angle,
# return depth of cut relative to zStart.
depth = geom.start - round(MIC / geom.scale, 4)
depth = geom.start - round(MIC * geom.scale, 4)
PathLog.debug('zStart value: {} depth: {}'.format(geom.start, depth))
return max(depth, geom.stop)
+3 -30
View File
@@ -30,7 +30,6 @@ import datetime
import shlex
import os.path
from PathScripts import PostUtils
from PathScripts import PathUtils
TOOLTIP = '''
This is a postprocessor file for the Path workbench. It is used to
@@ -154,7 +153,7 @@ def processArguments(argstring):
if args.no_tlo:
USE_TLO = False
if args.no_axis_modal:
OUTPUT_DOUBLES = true
OUTPUT_DOUBLES = True
except Exception: # pylint: disable=broad-except
return False
@@ -171,7 +170,7 @@ def export(objectslist, filename, argstring):
global UNIT_SPEED_FORMAT
global HORIZRAPID
global VERTRAPID
for obj in objectslist:
if not hasattr(obj, "Path"):
print("the object " + obj.Name + " is not a path. Please select only path and Compounds.")
@@ -206,34 +205,10 @@ def export(objectslist, filename, argstring):
if not obj.Base.Active:
continue
# fetch machine details
job = PathUtils.findParentJob(obj)
myMachine = 'not set'
if hasattr(job, "MachineName"):
myMachine = job.MachineName
if hasattr(job, "MachineUnits"):
if job.MachineUnits == "Metric":
UNITS = "G21"
UNIT_FORMAT = 'mm'
UNIT_SPEED_FORMAT = 'mm/min'
else:
UNITS = "G20"
UNIT_FORMAT = 'in'
UNIT_SPEED_FORMAT = 'in/min'
if hasattr(job, "SetupSheet"):
if hasattr(job.SetupSheet, "HorizRapid"):
HORIZRAPID = Units.Quantity(job.SetupSheet.HorizRapid, FreeCAD.Units.Velocity)
if hasattr(job.SetupSheet, "VertRapid"):
VERTRAPID = Units.Quantity(job.SetupSheet.HorizRapid, FreeCAD.Units.Velocity)
# do the pre_op
if OUTPUT_COMMENTS:
gcode += linenumber() + "(BEGIN OPERATION: %s)\n" % obj.Label.upper()
gcode += linenumber() + "(MACHINE: %s, %s)\n" % (myMachine.upper(), UNIT_SPEED_FORMAT.upper())
gcode += linenumber() + "(MACHINE UNITS: %s)\n" % (UNIT_SPEED_FORMAT.upper())
for line in PRE_OPERATION.splitlines(True):
gcode += linenumber() + line
@@ -459,7 +434,6 @@ def parse(pathobj):
outstring.append(param + str(int(c.Parameters['D'])))
elif param == 'S':
outstring.append(param + str(int(c.Parameters['S'])))
currentSpeed = int(c.Parameters['S'])
else:
if (not OUTPUT_DOUBLES) and (param in currLocation) and (currLocation[param] == c.Parameters[param]):
continue
@@ -482,7 +456,6 @@ def parse(pathobj):
# Check for Tool Change:
if command == 'M6':
# stop the spindle
currentSpeed = 0
out += linenumber() + "M5\n"
for line in TOOL_CHANGE.splitlines(True):
out += linenumber() + line
+5 -5
View File
@@ -223,9 +223,9 @@ def export(objectslist, filename, argstring):
# Check canned cycles for drilling
if TRANSLATE_DRILL_CYCLES:
if len(SUPPRESS_COMMANDS) == 0:
SUPPRESS_COMMANDS = ['G98', 'G80']
SUPPRESS_COMMANDS = ['G99', 'G98', 'G80']
else:
SUPPRESS_COMMANDS += ['G98', 'G80']
SUPPRESS_COMMANDS += ['G99', 'G98', 'G80']
# Write the preamble
if OUTPUT_COMMENTS:
@@ -305,6 +305,9 @@ def export(objectslist, filename, argstring):
gcode += linenumber() + '(Coolant Off:' + coolantMode + ')\n'
gcode += linenumber() +'M9' + '\n'
if RETURN_TO:
gcode += linenumber() + "G0 X%s Y%s\n" % tuple(RETURN_TO)
# do the post_amble
if OUTPUT_BCNC:
gcode += linenumber() + "(Block-name: post_amble)\n"
@@ -315,9 +318,6 @@ def export(objectslist, filename, argstring):
for line in POSTAMBLE.splitlines(True):
gcode += linenumber() + line
if RETURN_TO:
gcode += linenumber() + "G0 X%s Y%s" % tuple(RETURN_TO)
# show the gCode result dialog
if FreeCAD.GuiUp and SHOW_EDITOR:
dia = PostUtils.GCodeEditorDialog()
@@ -20,14 +20,10 @@
# HEDENHAIN Post-Processor for FreeCAD
import FreeCAD
from FreeCAD import Units
import argparse
import time
import shlex
import PathScripts
from PathScripts import PostUtils
from PathScripts import PathUtils
import math
#**************************************************************************#
@@ -261,11 +257,8 @@ def export(objectslist, filename, argstring):
global LBLIZE_STAUS
Object_Kind = None
Feed_Rapid = False
Feed = 0
Spindle_RPM = 0
Spindle_Active = False
ToolId = ""
Compensation = "0"
params = [
'X', 'Y', 'Z',
@@ -360,22 +353,14 @@ def export(objectslist, filename, argstring):
for c in obj.Path.Commands:
Cmd_Count += 1
outstring = []
command = c.Name
if command != 'G0':
command = command.replace('G0','G') # normalize: G01 -> G1
Feed_Rapid = False
else:
Feed_Rapid = True
for param in params:
if param in c.Parameters:
if param == 'F':
Feed = c.Parameters['F']
elif param == 'S':
Spindle_RPM = c.Parameters['S'] # Could be deleted if tool it's OK
elif param == 'T':
ToolId = c.Parameters['T'] # Could be deleted if tool it's OK
if command == 'G90':
G_FUNCTION_STORE['G90'] = True
@@ -474,21 +459,21 @@ def export(objectslist, filename, argstring):
def HEIDEN_Begin(ActualJob): #use Label for program name
global UNITS
JobParent = PathUtils.findParentJob(ActualJob[0])
if hasattr(JobParent, "Label"):
program_id = JobParent.Label
else:
program_id = "NEW"
return "BEGIN PGM " + str(program_id) + " " + UNITS
# JobParent = PathUtils.findParentJob(ActualJob[0])
# if hasattr(JobParent, "Label"):
# program_id = JobParent.Label
# else:
# program_id = "NEW"
return "BEGIN PGM {}".format(UNITS)
def HEIDEN_End(ActualJob): #use Label for program name
global UNITS
JobParent = PathUtils.findParentJob(ActualJob[0])
if hasattr(JobParent, "Label"):
program_id = JobParent.Label
else:
program_id = "NEW"
return "END PGM " + program_id + " " + UNITS
# JobParent = PathUtils.findParentJob(ActualJob[0])
# if hasattr(JobParent, "Label"):
# program_id = JobParent.Label
# else:
# program_id = "NEW"
return "END PGM {}".format(UNITS)
#def HEIDEN_ToolDef(tool_id, tool_length, tool_radius): # old machines don't have tool table, need tooldef list
# return "TOOL DEF " + tool_id + " R" + "{:.3f}".format(tool_length) + " L" + "{:.3f}".format(tool_radius)
+1 -20
View File
@@ -29,7 +29,6 @@ import argparse
import datetime
import shlex
from PathScripts import PostUtils
from PathScripts import PathUtils
TOOLTIP = '''
This is a postprocessor file for the Path workbench. It is used to
@@ -193,28 +192,10 @@ def export(objectslist, filename, argstring):
if not obj.Base.Active:
continue
# fetch machine details
job = PathUtils.findParentJob(obj)
myMachine = 'not set'
if hasattr(job, "MachineName"):
myMachine = job.MachineName
if hasattr(job, "MachineUnits"):
if job.MachineUnits == "Metric":
UNITS = "G21"
UNIT_FORMAT = 'mm'
UNIT_SPEED_FORMAT = 'mm/min'
else:
UNITS = "G20"
UNIT_FORMAT = 'in'
UNIT_SPEED_FORMAT = 'in/min'
# do the pre_op
if OUTPUT_COMMENTS:
gcode += linenumber() + "(begin operation: %s)\n" % obj.Label
gcode += linenumber() + "(machine: %s, %s)\n" % (myMachine, UNIT_SPEED_FORMAT)
gcode += linenumber() + "(machine units: %s)\n" % (UNIT_SPEED_FORMAT)
for line in PRE_OPERATION.splitlines(True):
gcode += linenumber() + line
@@ -28,7 +28,6 @@ import argparse
import datetime
import shlex
from PathScripts import PostUtils
from PathScripts import PathUtils
TOOLTIP = '''
This is a postprocessor file for the Path workbench. It is used to
@@ -161,8 +160,6 @@ def export(objectslist, filename, argstring):
global UNITS
global UNIT_FORMAT
global UNIT_SPEED_FORMAT
global HORIZRAPID
global VERTRAPID
for obj in objectslist:
if not hasattr(obj, "Path"):
@@ -195,34 +192,10 @@ def export(objectslist, filename, argstring):
if not obj.Base.Active:
continue
# fetch machine details
job = PathUtils.findParentJob(obj)
myMachine = 'not set'
if hasattr(job, "MachineName"):
myMachine = job.MachineName
if hasattr(job, "MachineUnits"):
if job.MachineUnits == "Metric":
UNITS = "G21"
UNIT_FORMAT = 'mm'
UNIT_SPEED_FORMAT = 'mm/min'
else:
UNITS = "G20"
UNIT_FORMAT = 'in'
UNIT_SPEED_FORMAT = 'in/min'
if hasattr(job, "SetupSheet"):
if hasattr(job.SetupSheet, "HorizRapid"):
HORIZRAPID = Units.Quantity(job.SetupSheet.HorizRapid, FreeCAD.Units.Velocity)
if hasattr(job.SetupSheet, "VertRapid"):
VERTRAPID = Units.Quantity(job.SetupSheet.HorizRapid, FreeCAD.Units.Velocity)
# do the pre_op
if OUTPUT_COMMENTS:
gcode += linenumber() + "(begin operation: %s)\n" % obj.Label
gcode += linenumber() + "(machine: %s, %s)\n" % (myMachine, UNIT_SPEED_FORMAT)
gcode += linenumber() + "(machine: %s, %s)\n" % (MACHINE_NAME, UNIT_SPEED_FORMAT)
for line in PRE_OPERATION.splitlines(True):
gcode += linenumber() + line
@@ -20,15 +20,14 @@
#* *
#***************************************************************************
# reload in python console:
# import generic_post
# reload(generic_post)
# 03-24-2021 Sliptonic: I've removed teh PathUtils import and job lookup
# post processors shouldn't be reaching back to the job. This can cause a
# proxy error.
import FreeCAD
import argparse
import time
from PathScripts import PostUtils
from PathScripts import PathUtils
import math
TOOLTIP = '''Post processor for Maho M 600E mill
@@ -232,16 +231,17 @@ def processArguments(argstring):
SHOW_EDITOR = False
def mkHeader(selection):
job = PathUtils.findParentJob(selection[0])
# job = PathUtils.findParentJob(selection[0])
# this is within a function, because otherwise filename and time don't change when changing the FreeCAD project
# now = datetime.datetime.now()
now = time.strftime("%Y-%m-%d %H:%M")
originfile = FreeCAD.ActiveDocument.FileName
headerNoNumber = "%PM\n" # this line gets no linenumber
if hasattr(job, "Description"):
description = job.Description
else:
description = ""
# if hasattr(job, "Description"):
# description = job.Description
# else:
# description = ""
description = ""
# this line gets no linenumber, it is already a specially numbered
headerNoNumber += "N9XXX (" + description + ", " + now + ")\n"
header = ""
+3 -3
View File
@@ -52,7 +52,7 @@ class TestPathDeburr(PathTestUtils.PathTestBase):
self.assertFalse(info)
def test01(self):
'''Verify chamfer depth and offset for a 90° v-bit.'''
'''Verify chamfer depth and offset for a 90 deg v-bit.'''
tool = Path.Tool()
tool.FlatRadius = 0
tool.CuttingEdgeAngle = 90
@@ -68,7 +68,7 @@ class TestPathDeburr(PathTestUtils.PathTestBase):
self.assertFalse(info)
def test02(self):
'''Verify chamfer depth and offset for a 90° v-bit with non 0 flat radius.'''
'''Verify chamfer depth and offset for a 90 deg v-bit with non 0 flat radius.'''
tool = Path.Tool()
tool.FlatRadius = 0.3
tool.CuttingEdgeAngle = 90
@@ -84,7 +84,7 @@ class TestPathDeburr(PathTestUtils.PathTestBase):
self.assertFalse(info)
def test03(self):
'''Verify chamfer depth and offset for a 60° v-bit with non 0 flat radius.'''
'''Verify chamfer depth and offset for a 60 deg v-bit with non 0 flat radius.'''
tool = Path.Tool()
tool.FlatRadius = 10
tool.CuttingEdgeAngle = 60
+2 -2
View File
@@ -7,8 +7,8 @@
"CuttingEdgeHeight": "6.3500 mm",
"Diameter": "12.3323 mm",
"Length": "30.0000 mm",
"ShankDiameter": "6.3500 mm"
"TipDiameter": "5.0000 mm",
"ShankDiameter": "6.3500 mm",
"TipDiameter": "5.0000 mm"
},
"attribute": {}
}
@@ -48,6 +48,7 @@
#include <pcl/surface/marching_cubes_hoppe.h>
#include <pcl/surface/ear_clipping.h>
#include <pcl/common/common.h>
#include <pcl/common/io.h>
#include <boost/random.hpp>
#include <boost/math/special_functions/fpclassify.hpp>
+26 -337
View File
@@ -4864,6 +4864,7 @@ void CmdSketcherConstrainRadius::activated(int iMsg)
if (geoIdRadiusMap.empty() && externalGeoIdRadiusMap.empty()) {
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
QObject::tr("Select one or more arcs or circles from the sketch."));
return;
}
if(poles && nonpoles) {
@@ -4922,28 +4923,20 @@ void CmdSketcherConstrainRadius::activated(int iMsg)
if(!geoIdRadiusMap.empty())
{
bool constrainEqual = false;
if (geoIdRadiusMap.size() > 1 && constraintCreationMode==Driving) {
int ret = QMessageBox::question(Gui::getMainWindow(), QObject::tr("Constrain equal"),
QObject::tr("Do you want to share the same radius for all selected elements?"),
QMessageBox::Yes, QMessageBox::No, QMessageBox::Cancel);
// use an equality constraint
if (ret == QMessageBox::Yes) {
constrainEqual = true;
}
else if (ret == QMessageBox::Cancel) {
// do nothing
return;
}
}
if (constrainEqual) {
// Create the one radius constraint now
int refGeoId = geoIdRadiusMap.front().first;
double radius = geoIdRadiusMap.front().second;
if(!commandopened)
openCommand(QT_TRANSLATE_NOOP("Command", "Add radius constraint"));
// Add the equality constraints
for (std::vector< std::pair<int, double> >::iterator it = geoIdRadiusMap.begin()+1; it != geoIdRadiusMap.end(); ++it) {
Gui::cmdAppObjectArgs(selection[0].getObject(),
"addConstraint(Sketcher.Constraint('Equal',%d,%d)) ",
refGeoId,it->first);
}
if(nonpoles)
Gui::cmdAppObjectArgs(selection[0].getObject(), "addConstraint(Sketcher.Constraint('Radius',%d,%f)) ",
@@ -4951,13 +4944,6 @@ void CmdSketcherConstrainRadius::activated(int iMsg)
else
Gui::cmdAppObjectArgs(selection[0].getObject(), "addConstraint(Sketcher.Constraint('Weight',%d,%f)) ",
refGeoId,radius);
// Add the equality constraints
for (std::vector< std::pair<int, double> >::iterator it = geoIdRadiusMap.begin()+1; it != geoIdRadiusMap.end(); ++it) {
Gui::cmdAppObjectArgs(selection[0].getObject(),
"addConstraint(Sketcher.Constraint('Equal',%d,%d)) ",
refGeoId,it->first);
}
}
else {
// Create the radius constraints now
@@ -4995,88 +4981,8 @@ void CmdSketcherConstrainRadius::activated(int iMsg)
vp->draw(false,false); // Redraw
}
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");
bool show = hGrp->GetBool("ShowDialogOnDistanceConstraint", true);
// Ask for the value of the radius immediately
if (show && constraintCreationMode==Driving) {
QDialog dlg(Gui::getMainWindow());
Ui::InsertDatum ui_Datum;
ui_Datum.setupUi(&dlg);
Base::Quantity init_val;
init_val.setValue(geoIdRadiusMap.front().second);
if(poles) {
dlg.setWindowTitle(EditDatumDialog::tr("Change weight"));
ui_Datum.label->setText(EditDatumDialog::tr("Weight:"));
}
else{
dlg.setWindowTitle(EditDatumDialog::tr("Change radius"));
ui_Datum.label->setText(EditDatumDialog::tr("Radius:"));
init_val.setUnit(Base::Unit::Length);
}
ui_Datum.labelEdit->setValue(init_val);
ui_Datum.labelEdit->selectNumber();
if (constrainEqual || geoIdRadiusMap.size() == 1)
ui_Datum.labelEdit->bind(Obj->Constraints.createPath(indexConstr));
else
ui_Datum.name->setDisabled(true);
if (dlg.exec() == QDialog::Accepted) {
Base::Quantity newQuant = ui_Datum.labelEdit->value();
double newRadius = newQuant.getValue();
try {
if (constrainEqual || geoIdRadiusMap.size() == 1) {
Gui::cmdAppObjectArgs(Obj, "setDatum(%i,App.Units.Quantity('%f %s'))",
indexConstr, newRadius, (const char*)newQuant.getUnit().getString().toUtf8());
QString constraintName = ui_Datum.name->text().trimmed();
if (Base::Tools::toStdString(constraintName) != Obj->Constraints[indexConstr]->Name) {
std::string escapedstr = Base::Tools::escapedUnicodeFromUtf8(constraintName.toUtf8().constData());
Gui::cmdAppObjectArgs(Obj, "renameConstraint(%d, u'%s')",
indexConstr, escapedstr.c_str());
}
}
else {
for (std::size_t i=0; i<geoIdRadiusMap.size();i++) {
Gui::cmdAppObjectArgs(Obj, "setDatum(%i,App.Units.Quantity('%f %s'))",
indexConstr+i, newRadius, (const char*)newQuant.getUnit().getString().toUtf8());
}
}
commitCommand();
if (Obj->noRecomputes && Obj->ExpressionEngine.depsAreTouched()) {
Obj->ExpressionEngine.execute();
Obj->solve();
}
tryAutoRecompute(Obj);
commitNeeded=false;
updateNeeded=false;
}
catch (const Base::Exception& e) {
QMessageBox::critical(qApp->activeWindow(), QObject::tr("Dimensional constraint"), QString::fromUtf8(e.what()));
abortCommand();
tryAutoRecomputeIfNotSolve(Obj); // we have to update the solver after this aborted addition.
}
}
else {
// command canceled
abortCommand();
updateNeeded=true;
}
}
else {
// now dialog was shown so commit the command
commitCommand();
commitNeeded=false;
}
finishDistanceConstraint(this, Obj, constraintCreationMode==Driving);
//updateActive();
getSelection().clearSelection();
}
@@ -5133,7 +5039,6 @@ void CmdSketcherConstrainRadius::applyConstraint(std::vector<SelIdPair> &selSeq,
const std::vector<Sketcher::Constraint *> &ConStr = Obj->Constraints.getValues();
int indexConstr = ConStr.size() - 1;
bool fixed = isPointOrSegmentFixed(Obj,GeoId);
if(fixed || constraintCreationMode==Reference) {
Gui::cmdAppObjectArgs(Obj, "setDriving(%i,%s)",
@@ -5154,78 +5059,9 @@ void CmdSketcherConstrainRadius::applyConstraint(std::vector<SelIdPair> &selSeq,
vp->draw(); // Redraw
}
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");
bool show = hGrp->GetBool("ShowDialogOnDistanceConstraint", true);
// Ask for the value of the radius immediately
if (show && constraintCreationMode==Driving && !fixed) {
QDialog dlg(Gui::getMainWindow());
Ui::InsertDatum ui_Datum;
ui_Datum.setupUi(&dlg);
Base::Quantity init_val;
if(ispole) {
dlg.setWindowTitle(EditDatumDialog::tr("Change weight"));
ui_Datum.label->setText(EditDatumDialog::tr("Weight:"));
}
else{
dlg.setWindowTitle(EditDatumDialog::tr("Change radius"));
ui_Datum.label->setText(EditDatumDialog::tr("Radius:"));
init_val.setUnit(Base::Unit::Length);
}
init_val.setValue(radius);
ui_Datum.labelEdit->setValue(init_val);
ui_Datum.labelEdit->selectNumber();
ui_Datum.labelEdit->bind(Obj->Constraints.createPath(indexConstr));
if (dlg.exec() == QDialog::Accepted) {
Base::Quantity newQuant = ui_Datum.labelEdit->value();
double newRadius = newQuant.getValue();
try {
Gui::cmdAppObjectArgs(Obj, "setDatum(%i,App.Units.Quantity('%f %s'))",
indexConstr, newRadius, (const char*)newQuant.getUnit().getString().toUtf8());
QString constraintName = ui_Datum.name->text().trimmed();
if (Base::Tools::toStdString(constraintName) != Obj->Constraints[indexConstr]->Name) {
std::string escapedstr = Base::Tools::escapedUnicodeFromUtf8(constraintName.toUtf8().constData());
Gui::cmdAppObjectArgs(Obj, "renameConstraint(%d, u'%s')",
indexConstr, escapedstr.c_str());
}
commitCommand();
if (Obj->noRecomputes && Obj->ExpressionEngine.depsAreTouched()) {
Obj->ExpressionEngine.execute();
Obj->solve();
}
tryAutoRecompute(Obj);
commitNeeded=false;
updateNeeded=false;
}
catch (const Base::Exception& e) {
QMessageBox::critical(qApp->activeWindow(), QObject::tr("Dimensional constraint"), QString::fromUtf8(e.what()));
abortCommand();
tryAutoRecomputeIfNotSolve(Obj); // we have to update the solver after this aborted addition.
}
}
else {
// command canceled
abortCommand();
updateNeeded=true;
}
}
else {
// now dialog was shown so commit the command
commitCommand();
commitNeeded=false;
}
if(!fixed)
finishDistanceConstraint(this, Obj, constraintCreationMode==Driving);
//updateActive();
getSelection().clearSelection();
@@ -5371,6 +5207,7 @@ void CmdSketcherConstrainDiameter::activated(int iMsg)
if (geoIdDiameterMap.empty() && externalGeoIdDiameterMap.empty()) {
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
QObject::tr("Select one or more arcs or circles from the sketch."));
return;
}
bool commitNeeded=false;
@@ -5417,35 +5254,20 @@ void CmdSketcherConstrainDiameter::activated(int iMsg)
if(!geoIdDiameterMap.empty())
{
bool constrainEqual = false;
if (geoIdDiameterMap.size() > 1 && constraintCreationMode==Driving) {
int ret = QMessageBox::question(Gui::getMainWindow(), QObject::tr("Constrain equal"),
QObject::tr("Do you want to share the same diameter for all selected elements?"),
QMessageBox::Yes, QMessageBox::No, QMessageBox::Cancel);
// use an equality constraint
if (ret == QMessageBox::Yes) {
constrainEqual = true;
}
else if (ret == QMessageBox::Cancel) {
// do nothing
return;
}
}
if (constrainEqual) {
// Create the one radius constraint now
int refGeoId = geoIdDiameterMap.front().first;
double diameter = geoIdDiameterMap.front().second;
if(!commandopened)
openCommand(QT_TRANSLATE_NOOP("Command", "Add diameter constraint"));
Gui::cmdAppObjectArgs(Obj, "addConstraint(Sketcher.Constraint('Diameter',%d,%f)) ", refGeoId,diameter);
// Add the equality constraints
for (std::vector< std::pair<int, double> >::iterator it = geoIdDiameterMap.begin()+1; it != geoIdDiameterMap.end(); ++it) {
Gui::cmdAppObjectArgs(Obj, "addConstraint(Sketcher.Constraint('Equal',%d,%d)) ", refGeoId,it->first);
}
Gui::cmdAppObjectArgs(Obj, "addConstraint(Sketcher.Constraint('Diameter',%d,%f)) ", refGeoId,diameter);
}
else {
// Create the diameter constraints now
@@ -5482,80 +5304,8 @@ void CmdSketcherConstrainDiameter::activated(int iMsg)
}
vp->draw(false,false); // Redraw
}
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");
bool show = hGrp->GetBool("ShowDialogOnDistanceConstraint", true);
// Ask for the value of the diameter immediately
if (show && constraintCreationMode==Driving) {
QDialog dlg(Gui::getMainWindow());
Ui::InsertDatum ui_Datum;
ui_Datum.setupUi(&dlg);
dlg.setWindowTitle(EditDatumDialog::tr("Change diameter"));
ui_Datum.label->setText(EditDatumDialog::tr("Diameter:"));
Base::Quantity init_val;
init_val.setUnit(Base::Unit::Length);
init_val.setValue(geoIdDiameterMap.front().second);
ui_Datum.labelEdit->setValue(init_val);
ui_Datum.labelEdit->selectNumber();
if (constrainEqual || geoIdDiameterMap.size() == 1)
ui_Datum.labelEdit->bind(Obj->Constraints.createPath(indexConstr));
else
ui_Datum.name->setDisabled(true);
if (dlg.exec() == QDialog::Accepted) {
Base::Quantity newQuant = ui_Datum.labelEdit->value();
double newDiameter = newQuant.getValue();
try {
if (constrainEqual || geoIdDiameterMap.size() == 1) {
Gui::cmdAppObjectArgs(Obj, "setDatum(%i,App.Units.Quantity('%f %s'))",
indexConstr, newDiameter, (const char*)newQuant.getUnit().getString().toUtf8());
QString constraintName = ui_Datum.name->text().trimmed();
if (Base::Tools::toStdString(constraintName) != Obj->Constraints[indexConstr]->Name) {
std::string escapedstr = Base::Tools::escapedUnicodeFromUtf8(constraintName.toUtf8().constData());
Gui::cmdAppObjectArgs(Obj, "renameConstraint(%d, u'%s')", indexConstr, escapedstr.c_str());
}
}
else {
for (std::size_t i=0; i<geoIdDiameterMap.size();i++) {
Gui::cmdAppObjectArgs(Obj, "setDatum(%i,App.Units.Quantity('%f %s'))",
indexConstr+i, newDiameter, (const char*)newQuant.getUnit().getString().toUtf8());
}
}
commitCommand();
if (Obj->noRecomputes && Obj->ExpressionEngine.depsAreTouched()) {
Obj->ExpressionEngine.execute();
Obj->solve();
}
tryAutoRecompute(Obj);
commitNeeded=false;
updateNeeded=false;
}
catch (const Base::Exception& e) {
QMessageBox::critical(qApp->activeWindow(), QObject::tr("Dimensional constraint"), QString::fromUtf8(e.what()));
abortCommand();
tryAutoRecomputeIfNotSolve(Obj); // we have to update the solver after this aborted addition.
}
}
else {
// command canceled
abortCommand();
updateNeeded=true;
}
}
else {
// now dialog was shown so commit the command
commitCommand();
commitNeeded=false;
}
finishDistanceConstraint(this, Obj, constraintCreationMode==Driving);
//updateActive();
getSelection().clearSelection();
}
@@ -5611,7 +5361,6 @@ void CmdSketcherConstrainDiameter::applyConstraint(std::vector<SelIdPair> &selSe
const std::vector<Sketcher::Constraint *> &ConStr = Obj->Constraints.getValues();
int indexConstr = ConStr.size() - 1;
bool fixed = isPointOrSegmentFixed(Obj,GeoId);
if(fixed || constraintCreationMode==Reference) {
Gui::cmdAppObjectArgs(Obj, "setDriving(%i,%s)", ConStr.size()-1, "False");
@@ -5629,69 +5378,9 @@ void CmdSketcherConstrainDiameter::applyConstraint(std::vector<SelIdPair> &selSe
constr->LabelDistance = 2. * sf;
vp->draw(); // Redraw
}
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");
bool show = hGrp->GetBool("ShowDialogOnDistanceConstraint", true);
// Ask for the value of the diameter immediately
if (show && constraintCreationMode==Driving && !fixed) {
QDialog dlg(Gui::getMainWindow());
Ui::InsertDatum ui_Datum;
ui_Datum.setupUi(&dlg);
dlg.setWindowTitle(EditDatumDialog::tr("Change diameter"));
ui_Datum.label->setText(EditDatumDialog::tr("Diameter:"));
Base::Quantity init_val;
init_val.setUnit(Base::Unit::Length);
init_val.setValue(diameter);
ui_Datum.labelEdit->setValue(init_val);
ui_Datum.labelEdit->selectNumber();
ui_Datum.labelEdit->bind(Obj->Constraints.createPath(indexConstr));
if (dlg.exec() == QDialog::Accepted) {
Base::Quantity newQuant = ui_Datum.labelEdit->value();
double newDiameter = newQuant.getValue();
try {
Gui::cmdAppObjectArgs(Obj, "setDatum(%i,App.Units.Quantity('%f %s'))",
indexConstr, newDiameter, (const char*)newQuant.getUnit().getString().toUtf8());
QString constraintName = ui_Datum.name->text().trimmed();
if (Base::Tools::toStdString(constraintName) != Obj->Constraints[indexConstr]->Name) {
std::string escapedstr = Base::Tools::escapedUnicodeFromUtf8(constraintName.toUtf8().constData());
Gui::cmdAppObjectArgs(Obj, "renameConstraint(%d, u'%s')", indexConstr, escapedstr.c_str());
}
commitCommand();
if (Obj->noRecomputes && Obj->ExpressionEngine.depsAreTouched()) {
Obj->ExpressionEngine.execute();
Obj->solve();
}
tryAutoRecompute(Obj);
commitNeeded=false;
updateNeeded=false;
}
catch (const Base::Exception& e) {
QMessageBox::critical(qApp->activeWindow(), QObject::tr("Dimensional constraint"), QString::fromUtf8(e.what()));
abortCommand();
tryAutoRecomputeIfNotSolve(Obj); // we have to update the solver after this aborted addition.
}
}
else {
// command canceled
abortCommand();
updateNeeded=true;
}
}
else {
// now dialog was shown so commit the command
commitCommand();
commitNeeded=false;
}
if(!fixed)
finishDistanceConstraint(this, Obj, constraintCreationMode==Driving);
//updateActive();
getSelection().clearSelection();
@@ -7121,13 +6810,13 @@ void CmdSketcherConstrainInternalAlignment::activated(int iMsg)
const Part::Geometry *geo = Obj->getGeometry(GeoId);
if (geo->getTypeId() == Part::GeomPoint::getClassTypeId())
if (geo && geo->getTypeId() == Part::GeomPoint::getClassTypeId())
pointids.push_back(GeoId);
else if (geo->getTypeId() == Part::GeomLineSegment::getClassTypeId())
else if (geo && geo->getTypeId() == Part::GeomLineSegment::getClassTypeId())
lineids.push_back(GeoId);
else if (geo->getTypeId() == Part::GeomEllipse::getClassTypeId())
else if (geo && geo->getTypeId() == Part::GeomEllipse::getClassTypeId())
ellipseids.push_back(GeoId);
else if (geo->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId())
else if (geo && geo->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId())
arcsofellipseids.push_back(GeoId);
else {
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
+8 -2
View File
@@ -106,6 +106,9 @@ Base::Vector2d GetCircleCenter (const Base::Vector2d &p1, const Base::Vector2d &
double vv = v*v;
double ww = w*w;
if (uu * vv * ww == 0)
THROWM(Base::ValueError,"Two points are coincident");
double uv = -(u*v);
double vw = -(v*w);
double uw = -(u*w);
@@ -507,7 +510,8 @@ public:
"conList.append(Sketcher.Constraint('Horizontal',%i))\n"
"conList.append(Sketcher.Constraint('Vertical',%i))\n"
"conList.append(Sketcher.Constraint('Vertical',%i))\n"
"%s.addConstraint(conList)\n",
"%s.addConstraint(conList)\n"
"del geoList, conList\n",
EditCurve[0].x,EditCurve[0].y,EditCurve[1].x,EditCurve[1].y, // line 1
EditCurve[1].x,EditCurve[1].y,EditCurve[2].x,EditCurve[2].y, // line 2
EditCurve[2].x,EditCurve[2].y,EditCurve[3].x,EditCurve[3].y, // line 3
@@ -4251,6 +4255,7 @@ public:
}
cstream << Gui::Command::getObjectCmd(sketchgui->getObject()) << ".addConstraint(conList)\n";
cstream << "del conList\n";
Gui::Command::doCommand(Gui::Command::Doc, cstream.str().c_str());
@@ -6366,7 +6371,8 @@ public:
"conList.append(Sketcher.Constraint('Tangent',%i,2,%i,2))\n"
"conList.append(Sketcher.Constraint('%s',%i))\n"
"conList.append(Sketcher.Constraint('Equal',%i,%i))\n"
"%s.addConstraint(conList)\n",
"%s.addConstraint(conList)\n"
"del geoList, conList\n",
StartPos.x,StartPos.y, // center of the arc1
fabs(r), // radius arc1
start,end, // start and end angle of arc1
@@ -375,7 +375,7 @@ public:
class ExpressionDelegate : public QStyledItemDelegate
{
public:
ExpressionDelegate(QListWidget * _view) : view(_view) { }
ExpressionDelegate(QListWidget* _view) : QStyledItemDelegate(_view), view(_view) { }
protected:
QPixmap getIcon(const char* name, const QSize& size) const
{
@@ -266,9 +266,6 @@ void TaskSketcherGeneral::onChangedSketchView(const Gui::ViewProvider& vp,
QSignalBlocker block(widget);
widget->checkGridView(sketchView->ShowGrid.getValue());
widget->enableGridSettings(sketchView->ShowGrid.getValue());
if (sketchView->ShowGrid.getValue()) {
sketchView->createGrid();
}
}
else if (&sketchView->GridSize == &prop) {
QSignalBlocker block(widget);
@@ -295,7 +292,6 @@ void TaskSketcherGeneral::onToggleGridView(bool on)
Base::ConnectionBlocker block(changedSketchView);
sketchView->ShowGrid.setValue(on);
widget->enableGridSettings(on);
if (on) sketchView->createGrid();
}
void TaskSketcherGeneral::onSetGridSize(double val)
@@ -6275,6 +6275,7 @@ bool ViewProviderSketch::setEdit(int ModNum)
" tv.show([ref[0] for ref in ActiveSketch.ExternalGeometry])\n"
"tv.hide(ActiveSketch)\n"
"del(tv)\n"
"del(ActiveSketch)\n"
).arg(QString::fromLatin1(getDocument()->getDocument()->getName()),
QString::fromLatin1(getSketchObject()->getNameInDocument()),
QString::fromLatin1(Gui::Command::getObjectCmd(editObj).c_str()),
@@ -6790,6 +6791,7 @@ void ViewProviderSketch::unsetEdit(int ModNum)
" tv.restore()\n"
"ActiveSketch.ViewObject.TempoVis = None\n"
"del(tv)\n"
"del(ActiveSketch)\n"
).arg(QString::fromLatin1(getDocument()->getDocument()->getName())).arg(
QString::fromLatin1(getSketchObject()->getNameInDocument()));
QByteArray cmdstr_bytearray = cmdstr.toLatin1();
+5 -4
View File
@@ -391,14 +391,16 @@ void PropertySheet::pasteCells(XMLReader &reader, const CellAddress &addr) {
roffset = addr.row() - from.row();
coffset = addr.col() - from.col();
}else
range.next();
if (!range.next())
break;
while(src!=*range) {
CellAddress dst(*range);
dst.setRow(dst.row()+roffset);
dst.setCol(dst.col()+coffset);
owner->clear(dst);
owner->cellUpdated(dst);
range.next();
if (!range.next())
break;
}
CellAddress dst(src.row()+roffset, src.col()+coffset);
auto cell = owner->getNewCell(dst);
@@ -537,12 +539,11 @@ void PropertySheet::setAlias(CellAddress address, const std::string &alias)
const Cell * aliasedCell = getValueFromAlias(alias);
Cell * cell = nonNullCellAt(address);
assert(cell != 0);
if (aliasedCell != 0 && cell != aliasedCell)
throw Base::ValueError("Alias already defined.");
assert(cell != 0);
/* Mark cells depending on this cell dirty; they need to be resolved when an alias changes or disappears */
std::string fullName = owner->getFullName() + "." + address.toString();
+1 -1
View File
@@ -47,5 +47,5 @@ INSTALL(
FILES
${Start_Tests}
DESTINATION
Mod/Start/testStart
Mod/Start/TestStart
)
+14 -10
View File
@@ -26,6 +26,7 @@
import six
import sys,os,FreeCAD,FreeCADGui,tempfile,time,zipfile,re
import urllib.parse
from . import TranslationTexts
from PySide import QtCore,QtGui
@@ -248,7 +249,7 @@ def buildCard(filename,method,arg=None):
infostring += "\n\n" + encode(finfo[5])
if size:
result += '<li class="icon">'
result += '<a href="'+method+arg+'" title="'+infostring+'">'
result += '<a href="'+method+urllib.parse.quote(arg)+'" title="'+infostring+'">'
result += '<img src="file:///'+image.replace('\\','/')+'" alt="'+encode(basename)+'">'
result += '<div class="caption">'
result += '<h4>'+encode(basename)+'</h4>'
@@ -415,15 +416,18 @@ def handle():
for cfolder in cfolders.split(";;"): # allow several paths separated by ;;
if not os.path.isdir(cfolder):
cfolder = os.path.dirname(cfolder)
SECTION_CUSTOM += encode("<h2>"+os.path.basename(os.path.normpath(cfolder))+"</h2>")
SECTION_CUSTOM += "<ul>"
for basename in os.listdir(cfolder):
filename = os.path.join(cfolder,basename)
SECTION_CUSTOM += encode(buildCard(filename,method="LoadCustom.py?filename="+str(dn)+"_"))
SECTION_CUSTOM += "</ul>"
# hide the custom section tooltip if custom section is set (users know about it if they enabled it)
HTML = HTML.replace("id=\"customtip\"","id=\"customtip\" style=\"display:none;\"")
dn += 1
if not os.path.exists(cfolder):
FreeCAD.Console.PrintWarning("Custom folder not found: %s" % cfolder)
else:
SECTION_CUSTOM += encode("<h2>"+os.path.basename(os.path.normpath(cfolder))+"</h2>")
SECTION_CUSTOM += "<ul>"
for basename in os.listdir(cfolder):
filename = os.path.join(cfolder,basename)
SECTION_CUSTOM += encode(buildCard(filename,method="LoadCustom.py?filename="+str(dn)+"_"))
SECTION_CUSTOM += "</ul>"
# hide the custom section tooltip if custom section is set (users know about it if they enabled it)
HTML = HTML.replace("id=\"customtip\" class","id=\"customtip\" style=\"display:none;\" class")
dn += 1
HTML = HTML.replace("SECTION_CUSTOM",SECTION_CUSTOM)
# build IMAGE_SRC paths
+4 -1
View File
@@ -324,7 +324,10 @@ short DrawViewPart::mustExecute() const
SeamHidden.isTouched() ||
IsoHidden.isTouched() ||
IsoCount.isTouched() ||
CoarseView.isTouched());
CoarseView.isTouched() ||
CosmeticVertexes.isTouched() ||
CosmeticEdges.isTouched() ||
CenterLines.isTouched());
}
if (result) {
+10
View File
@@ -348,6 +348,8 @@ void execMidpoints(Gui::Command* cmd)
return;
}
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add Midpont Vertices"));
const std::vector<TechDraw::BaseGeom*> edges = dvp->getEdgeGeometry();
double scale = dvp->getScale();
for (auto& s: selectedEdges) {
@@ -357,6 +359,9 @@ void execMidpoints(Gui::Command* cmd)
mid = DrawUtil::invertY(mid);
dvp->addCosmeticVertex(mid / scale);
}
Gui::Command::commitCommand();
dvp->recomputeFeature();
}
@@ -371,6 +376,8 @@ void execQuadrants(Gui::Command* cmd)
return;
}
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add Quadrant Vertices"));
const std::vector<TechDraw::BaseGeom*> edges = dvp->getEdgeGeometry();
double scale = dvp->getScale();
for (auto& s: selectedEdges) {
@@ -382,6 +389,9 @@ void execQuadrants(Gui::Command* cmd)
dvp->addCosmeticVertex(iq / scale);
}
}
Gui::Command::commitCommand();
dvp->recomputeFeature();
}
+4
View File
@@ -161,11 +161,15 @@ void TaskCosVertex::updateUi(void)
void TaskCosVertex::addCosVertex(QPointF qPos)
{
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add Cosmetic Vertex"));
// Base::Console().Message("TCV::addCosVertex(%s)\n", TechDraw::DrawUtil::formatVector(qPos).c_str());
Base::Vector3d pos(qPos.x(), -qPos.y());
// int idx =
(void) m_baseFeat->addCosmeticVertex(pos);
m_baseFeat->requestPaint();
Gui::Command::commitCommand();
}
@@ -203,6 +203,8 @@ void TaskCosmeticLine::setUiEdit()
//******************************************************************************
void TaskCosmeticLine::createCosmeticLine(void)
{
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Create Cosmetic Line"));
double x = ui->qsbx1->value().getValue();
double y = ui->qsby1->value().getValue();
double z = ui->qsbz1->value().getValue();
@@ -227,6 +229,8 @@ void TaskCosmeticLine::createCosmeticLine(void)
m_tag = m_partFeat->addCosmeticEdge(p0, p1);
m_ce = m_partFeat->getCosmeticEdge(m_tag);
Gui::Command::commitCommand();
}
void TaskCosmeticLine::updateCosmeticLine(void)
@@ -191,6 +191,10 @@ bool ViewProviderTemplate::onDelete(const std::vector<std::string> &)
// get the page
auto page = getTemplate()->getParentPage();
// If no parent page is given then just go ahead
if (!page)
return true;
// generate dialog
QString bodyMessage;
QTextStream bodyMessageStream(&bodyMessage);
+1 -1
View File
@@ -1307,7 +1307,7 @@ class DocumentFileIncludeCases(unittest.TestCase):
# copy file from L5 which is in the same directory
L7 = doc2.addObject("App::DocumentObjectFileIncluded","FileObject3")
L7.File = (L5.File,"Copy.txt")
self.failUnless(os.path.exists(L5.File))
self.failUnless(os.path.exists(L7.File))
FreeCAD.closeDocument("Doc2")
+3 -2
View File
@@ -125,9 +125,10 @@ class UnitBasicCases(unittest.TestCase):
try:
q2 = FreeCAD.Units.Quantity(t[0])
if math.fabs(q1.Value - q2.Value) > 0.01:
print (q1, " : ", q2, " : ", t, " : ", i, " : ", val)
print (u" {} : {} : {} : {} : {}".format(q1,q2, t, i, val).encode("utf-8").strip())
except Exception as e:
print ("{}: {}".format(str(e), t[0]))
s = "{}: {}".format(e, t[0])
print (u" ".join(e).encode("utf-8").strip())
def testVoltage(self):
q1 = FreeCAD.Units.Quantity("1e20 V")
+2
View File
@@ -239,6 +239,7 @@ Py::Object BrowserViewPy::setHtml(const Py::Tuple& args)
WebView::WebView(QWidget *parent)
: QWEBVIEW(parent)
{
#ifdef QTWEBKIT
// Increase html font size for high DPI displays
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
QRect mainScreenSize = QApplication::primaryScreen()->geometry();
@@ -248,6 +249,7 @@ WebView::WebView(QWidget *parent)
if (mainScreenSize.width() > 1920){
setTextSizeMultiplier (mainScreenSize.width()/1920.0);
}
#endif
}
#ifdef QTWEBENGINE
+4 -4
View File
@@ -20,14 +20,14 @@ To build the installer you can do the following:
(You can alternatively get nsProcess from https://nsis.sourceforge.io/NsProcess_plugin)
7. Copy all FreeCAD files to the folder "~\FreeCAD"
e.g. "C:\FreeCAD\Installer\FreeCAD"
8. If you use a version of FreeCAD that was compiled using another MSVC version than MSVC 2017,
8. If you use a version of FreeCAD that was compiled using another MSVC version than MSVC 2019,
copy its distributable DLLs to the folder FILES_DEPS (see step 3).
9. Right-click on the file FreeCAD-installer.nsi and choose "Compile NSIS script"
to compile the installer.
10. The folder ~\MSVCRedist contains already all MSVC 2017 x64 redistributable DLLs necessary
for FreeCAD 0.19dev. If another MSVC version was used to compile FreeCAD, replace the DLLs by
10. The folder ~\MSVCRedist contains already all MSVC 2019 x64 redistributable DLLs necessary
for FreeCAD 0.19. If another MSVC version was used to compile FreeCAD, replace the DLLs by
the ones of the used MSVC. (They are usually available in the folder
C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Redist\MSVC)
C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Redist\MSVC)
For test builds of the installer you can turn off the compression. This speeds up
the build time for the installer a lot but increases its file size. The compression
+7 -7
View File
@@ -17,19 +17,19 @@ SetCompressor /SOLID lzma
!define APP_VERSION_MAJOR 0
!define APP_VERSION_MINOR 19
!define APP_VERSION_REVISION 0
!define APP_VERSION_EMERGENCY "rev18731" # use "1" for an emergency release of FreeCAD otherwise ""
!define APP_VERSION_REVISION 3
!define APP_VERSION_EMERGENCY "" # use "1" for an emergency release of FreeCAD otherwise ""
# alternatively you can use APP_VERSION_EMERGENCY for a custom suffix of the version number
!define APP_EMERGENCY_DOT "" # use "." for an emergency release of FreeCAD otherwise ""
!define APP_VERSION_BUILD 1 # Start with 1 for the installer releases of each version
!define APP_VERSION "${APP_VERSION_MAJOR}.${APP_VERSION_MINOR}.${APP_VERSION_REVISION}${APP_EMERGENCY_DOT}${APP_VERSION_EMERGENCY}" # Version to display
!define COPYRIGHT_YEAR 2020
!define COPYRIGHT_YEAR 2021
#--------------------------------
# Installer file name
# Typical names for the release are "FreeCAD-018-Installer-1.exe" etc.
# Typical names for the release are "FreeCAD-019-Installer-1.exe" etc.
!define ExeFile "${APP_NAME}-${APP_VERSION_MAJOR}${APP_VERSION_MINOR}${APP_VERSION_REVISION}${APP_VERSION_EMERGENCY}-Installer-${APP_VERSION_BUILD}.exe"
@@ -43,6 +43,6 @@ SetCompressor /SOLID lzma
# File locations
# !!! you need to adjust them to the folders in your Windows system !!!
!define FILES_FREECAD "D:\usti\FreeCAD\Installer\FreeCAD"
!define FILES_DEPS "D:\usti\FreeCAD\Installer\MSVCRedist"
!define FILES_THUMBS "D:\usti\FreeCAD\Installer\thumbnail"
!define FILES_FREECAD "G:\FreeCADInst\Installer\FreeCAD"
!define FILES_DEPS "G:\FreeCADInst\Installer\MSVCRedist"
!define FILES_THUMBS "G:\FreeCADInst\Installer\thumbnail"
@@ -42,8 +42,14 @@ Configuration and variables of FreeCAD installer
!define APP_REGNAME_DOC "${APP_NAME}.Document"
!define APP_EXT ".FCStd"
!define APP_EXT1 ".FCStd1"
!define APP_MIME_TYPE "application/x-zip-compressed"
!define APP_EXT_BAK ".FCBak"
!define APP_EXT_MACRO ".FCMacro"
!define APP_EXT_MAT ".FCMat"
!define APP_EXT_SCRIPT ".FCScript"
!define APP_UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${SETUP_UNINSTALLER_KEY}"
#--------------------------------

Some files were not shown because too many files have changed in this diff Show More