├── .github ├── dependabot.yml └── workflows │ ├── commit-message.yml │ └── lint.yml ├── .gitignore ├── .pre-commit-config.yaml ├── .ruff.toml ├── LICENSE ├── README.md ├── cookiecutter.json ├── noxfile.py └── {{cookiecutter.project_name}} ├── .github ├── dependabot.yml └── workflows │ ├── commit-message.yml │ └── lint.yml ├── .pre-commit-config.yaml ├── .ruff.toml ├── Applications ├── CMakeLists.txt └── {{cookiecutter.app_name}}App │ ├── CMakeLists.txt │ ├── Main.cxx │ ├── Resources │ ├── App.qrc │ ├── Icons │ │ ├── DesktopIcon.icns │ │ ├── DesktopIcon.ico │ │ ├── Large │ │ │ └── DesktopIcon.png │ │ ├── Medium │ │ │ └── DesktopIcon.png │ │ ├── Small │ │ │ └── DesktopIcon.png │ │ └── XLarge │ │ │ └── DesktopIcon.png │ ├── Images │ │ ├── Logo.png │ │ ├── LogoFull.png │ │ └── SplashScreen.png │ ├── Installer │ │ ├── Header.bmp │ │ └── WelcomeFinishPage.bmp │ └── Settings │ │ └── DefaultSettings.ini │ ├── Widgets │ ├── qAppStyle.cxx │ └── qAppStyle.h │ ├── q{{cookiecutter.app_name}}AppMainWindow.cxx │ ├── q{{cookiecutter.app_name}}AppMainWindow.h │ ├── q{{cookiecutter.app_name}}AppMainWindow_p.h │ └── slicer-application-properties.cmake ├── BUILD.md ├── CMakeLists.txt ├── CONTRIBUTING.md ├── CTestConfig.cmake ├── LICENSE ├── Modules └── Scripted │ └── Home │ ├── CMakeLists.txt │ ├── Home.py │ └── Resources │ ├── Home.qrc │ ├── Home.qss │ ├── Icons │ ├── Gears.png │ └── Home.png │ └── UI │ ├── Home.ui │ └── Settings.ui └── README.md /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | # Maintain dependencies for GitHub Actions 4 | - package-ecosystem: "github-actions" 5 | directory: "/" 6 | schedule: 7 | interval: "daily" 8 | groups: 9 | actions: 10 | patterns: 11 | - "*" 12 | commit-message: 13 | # Prefix all commit messages with "COMP: " 14 | prefix: "COMP" 15 | -------------------------------------------------------------------------------- /.github/workflows/commit-message.yml: -------------------------------------------------------------------------------- 1 | name: "Commit Message Check" 2 | on: 3 | pull_request: 4 | types: 5 | - opened 6 | - edited 7 | - reopened 8 | - synchronize 9 | push: 10 | branches: 11 | - main 12 | 13 | permissions: 14 | contents: read 15 | pull-requests: read 16 | 17 | jobs: 18 | check-commit-message: 19 | runs-on: ubuntu-latest 20 | steps: 21 | - name: Check Commit Message 22 | uses: Slicer/slicer-check-commit-message-action@26db1d4a0580194025a00bff6f5c24a88c0efb9f # v1.0.0 23 | with: 24 | token: ${{ secrets.GITHUB_TOKEN }} 25 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: CI (Lint) 2 | 3 | on: 4 | workflow_dispatch: 5 | pull_request: 6 | push: 7 | branches: 8 | - main 9 | 10 | concurrency: 11 | group: ${{ github.workflow }}-${{ github.ref }} 12 | cancel-in-progress: true 13 | 14 | jobs: 15 | pre-commit: 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 19 | 20 | - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 21 | 22 | - uses: pre-commit/action@2c7b3805fd2a0fd8c1884dcaebf91fc102a13ecd # v3.0.1 23 | 24 | pre-commit-cookie: 25 | runs-on: ubuntu-latest 26 | steps: 27 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 28 | 29 | - run: pipx run nox -s 'pre-commit-cookie' 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | cover/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | .pybuilder/ 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | # For a library or package, you might want to ignore these files since the code is 87 | # intended to run in multiple environments; otherwise, check them in: 88 | # .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | #Pipfile.lock 96 | 97 | # poetry 98 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 99 | # This is especially recommended for binary packages to ensure reproducibility, and is more 100 | # commonly ignored for libraries. 101 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 102 | #poetry.lock 103 | 104 | # pdm 105 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 106 | #pdm.lock 107 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 108 | # in version control. 109 | # https://pdm.fming.dev/#use-with-ide 110 | .pdm.toml 111 | 112 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 113 | __pypackages__/ 114 | 115 | # Celery stuff 116 | celerybeat-schedule 117 | celerybeat.pid 118 | 119 | # SageMath parsed files 120 | *.sage.py 121 | 122 | # Environments 123 | .env 124 | .venv 125 | env/ 126 | venv/ 127 | ENV/ 128 | env.bak/ 129 | venv.bak/ 130 | 131 | # Spyder project settings 132 | .spyderproject 133 | .spyproject 134 | 135 | # Rope project settings 136 | .ropeproject 137 | 138 | # mkdocs documentation 139 | /site 140 | 141 | # mypy 142 | .mypy_cache/ 143 | .dmypy.json 144 | dmypy.json 145 | 146 | # Pyre type checker 147 | .pyre/ 148 | 149 | # pytype static type analyzer 150 | .pytype/ 151 | 152 | # Cython debug symbols 153 | cython_debug/ 154 | 155 | # PyCharm 156 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 157 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 158 | # and can be added to the global gitignore or merged into this file. For a more nuclear 159 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 160 | #.idea/ 161 | 162 | ### Python Patch ### 163 | # Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration 164 | poetry.toml 165 | 166 | # ruff 167 | .ruff_cache/ 168 | 169 | # LSP config files 170 | pyrightconfig.json 171 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | exclude: "^{{cookiecutter\\.project_name}}/" 2 | 3 | repos: 4 | - repo: https://github.com/pre-commit/pre-commit-hooks 5 | rev: "v4.6.0" 6 | hooks: 7 | - id: check-added-large-files 8 | - id: check-case-conflict 9 | - id: check-merge-conflict 10 | - id: check-symlinks 11 | - id: check-toml 12 | - id: check-yaml 13 | - id: debug-statements 14 | - id: end-of-file-fixer 15 | - id: mixed-line-ending 16 | - id: trailing-whitespace 17 | - id: requirements-txt-fixer 18 | 19 | - repo: https://github.com/adamchainz/blacken-docs 20 | rev: "1.18.0" 21 | hooks: 22 | - id: blacken-docs 23 | additional_dependencies: [black==24.*] 24 | 25 | - repo: https://github.com/astral-sh/ruff-pre-commit 26 | rev: "v0.5.7" 27 | hooks: 28 | # Run the linter. 29 | - id: ruff 30 | args: ["--fix", "--show-fixes"] 31 | # Run the formatter. 32 | - id: ruff-format 33 | 34 | - repo: https://github.com/rbubley/mirrors-prettier 35 | rev: "v3.3.3" 36 | hooks: 37 | - id: prettier 38 | types_or: [yaml, markdown, html, css, scss, javascript, json] 39 | 40 | - repo: https://github.com/codespell-project/codespell 41 | rev: "v2.3.0" 42 | hooks: 43 | - id: codespell 44 | args: ["-Lnd", "-w"] 45 | 46 | - repo: https://github.com/python-jsonschema/check-jsonschema 47 | rev: "0.29.1" 48 | hooks: 49 | - id: check-dependabot 50 | - id: check-github-workflows 51 | -------------------------------------------------------------------------------- /.ruff.toml: -------------------------------------------------------------------------------- 1 | [lint] 2 | select = [ 3 | "ARG", # flake8-unused-arguments 4 | "ANN", # flake8-annotations 5 | "B", # flake8-bugbear 6 | "C4", # flake8-comprehensions 7 | "D", # pydocstyle 8 | "E", "F", "W", # flake8 9 | "EXE", # flake8-executable 10 | "G", # flake8-logging-format 11 | "I", # isort 12 | "ICN", # flake8-import-conventions 13 | "ISC", # flake8-implicit-str-concat 14 | "NPY", # NumPy specific rules 15 | "PGH", # pygrep-hooks 16 | "PIE", # flake8-pie 17 | "PL", # pylint 18 | "PT", # flake8-pytest-style 19 | "RET", # flake8-return 20 | "RUF", # Ruff-specific 21 | "S", # flake8-bandit 22 | "SIM", # flake8-simplify 23 | "UP", # pyupgrade 24 | "YTT", # flake8-2020 25 | "W", # Warning 26 | ] 27 | extend-ignore = [ 28 | "ANN101", # missing-type-self 29 | "G004", # logging-f-string 30 | "PIE790", # unnecessary-pass 31 | 32 | "D10", # undocumented-public-* 33 | "D200", # One-line docstring should fit on one line 34 | 35 | # Disable linting rules conflicting with "ruff formatter" 36 | # See https://docs.astral.sh/ruff/formatter/#conflicting-lint-rules 37 | "COM812", 38 | "COM819", 39 | "D206", 40 | "D300", 41 | "E111", 42 | "E114", 43 | "E117", 44 | "ISC001", 45 | "ISC002", 46 | "Q000", 47 | "Q002", 48 | "Q003", 49 | "W191", 50 | ] 51 | flake8-annotations.suppress-dummy-args = true 52 | flake8-annotations.suppress-none-returning = true 53 | pydocstyle.convention = "pep257" 54 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SlicerCustomAppTemplate by Kitware 2 | 3 | This project is a template to be used as a starting point for creating a custom 3D Slicer application. 4 | 5 | [3D Slicer](https://slicer.org) is an open-source and extensible application for visualization and medical image 6 | analysis. 3D Slicer works with optical imaging, MRI, CT, and ultrasound data. 7 | 8 | At [Kitware](https://www.kitware.com), we help customers develop commercial products based on 3D Slicer and we have used the platform to rapidly prototype solutions in nearly every aspect of medical imaging. 9 | 10 | ## Getting Started 11 | 12 | The following will get a custom 3D Slicer application started in a new repository: 13 | 14 | ```bash 15 | pip install cookiecutter jinja2-github 16 | cookiecutter gh:KitwareMedical/SlicerCustomAppTemplate 17 | # Fill in the information requested at the prompts 18 | ``` 19 | 20 | We suggest to use the following commit message for checking in the results of these commands: 21 | 22 | ``` 23 | ENH: Add application sources 24 | 25 | This commit adds source files generated using KitwareMedical/SlicerCustomAppTemplate@ 26 | and referencing Slicer/Slicer@. 27 | 28 | Features and improvements specific to the custom application will be integrated 29 | in follow-up commits. 30 | ``` 31 | 32 | This will mark the version of the template file used and the Slicer version in the git history 33 | 34 | ## FAQ 35 | 36 | ### Which version of Slicer do the generated application build against ? 37 | 38 | Leveraging the `jinja2-github` plugin, the `cookiecutter` template generator will retrieve the latest commit associated with the Slicer repository default branch and it will set it as `GIT_TAG` value in the generated `CMakeLists.txt`. 39 | 40 | ## License 41 | 42 | This project template is distributed under the Apache 2.0 license. Please see 43 | the _LICENSE_ file for details. 44 | 45 | ## Authors 46 | 47 | - Julien Finet 48 | - Jean-Christophe Fillion-Robin 49 | - Dženan Zukić 50 | - Johan Andruejol 51 | - Alexis Girault 52 | - Cory Quammen 53 | - Jamie Snape 54 | -------------------------------------------------------------------------------- /cookiecutter.json: -------------------------------------------------------------------------------- 1 | { 2 | "project_name": "SlicerCustomAppTemplate", 3 | "github_organization": "Kitware", 4 | "github_project": "{{ cookiecutter.project_name }}", 5 | "org_domain": "kitware.com", 6 | "org_name": "Kitware, Inc.", 7 | "app_name": "{{ cookiecutter.project_name }}", 8 | "bundle_identifier": "{{ cookiecutter.org_domain.split('.') | reverse | join('.') }}.{{ cookiecutter.project_name | lower }}", 9 | "app_version_major": "0", 10 | "app_version_minor": "1", 11 | "app_version_patch": "0", 12 | "app_description_summary": "Customized version of Slicer", 13 | "cdash_drop_site": "open.cdash.org", 14 | "_extensions": ["jinja2_github.GitHubRepoBranchShaExtension"] 15 | } 16 | -------------------------------------------------------------------------------- /noxfile.py: -------------------------------------------------------------------------------- 1 | import os 2 | import shutil 3 | import stat 4 | import sys 5 | from collections.abc import Callable 6 | from pathlib import Path 7 | 8 | import nox 9 | 10 | nox.needs_version = ">=2024.3.2" 11 | nox.options.sessions = ["pre-commit", "pre-commit-cookie"] 12 | nox.options.default_venv_backend = "uv|virtualenv" 13 | 14 | 15 | DIR = Path(__file__).parent.resolve() 16 | 17 | JOB_FILE = """\ 18 | default_context: 19 | project_name: {project_name} 20 | """ 21 | 22 | 23 | def _remove_readonly(func: Callable[[str], None], path: str, _: object) -> None: 24 | os.chmod(path, stat.S_IWRITE) 25 | func(path) 26 | 27 | 28 | def rmtree_ro(path: Path) -> None: 29 | if sys.version_info >= (3, 12): 30 | shutil.rmtree(path, onexc=_remove_readonly) 31 | else: 32 | shutil.rmtree(path, onerror=_remove_readonly) 33 | 34 | 35 | def make_cookie(session: nox.Session) -> None: 36 | project_name = "Awesome" 37 | package_dir = Path(project_name) 38 | if package_dir.exists(): 39 | rmtree_ro(package_dir) 40 | 41 | Path("input.yml").write_text( 42 | JOB_FILE.format(project_name=project_name), encoding="utf-8" 43 | ) 44 | 45 | session.run( 46 | "cookiecutter", 47 | "--no-input", 48 | f"{DIR}", 49 | "--config-file=input.yml", 50 | ) 51 | 52 | init_git(session, package_dir) 53 | 54 | return package_dir 55 | 56 | 57 | def init_git(session: nox.Session, package_dir: Path) -> None: 58 | session.run("git", "-C", f"{package_dir}", "init", "-q", external=True) 59 | session.run("git", "-C", f"{package_dir}", "add", ".", external=True) 60 | session.run( 61 | "git", 62 | "-C", 63 | f"{package_dir}", 64 | "-c", 65 | "user.name=Kitware Bot", 66 | "-c", 67 | "user.email=kwrobot@kitware.com", 68 | "commit", 69 | "-qm", 70 | "feat: initial version", 71 | external=True, 72 | ) 73 | 74 | 75 | @nox.session(name="pre-commit-cookie") 76 | def pre_commit_cookie(session: nox.Session) -> None: 77 | session.install("cookiecutter", "jinja2-github", "pre-commit") 78 | 79 | tmp_dir = session.create_tmp() 80 | session.cd(tmp_dir) 81 | cookie = make_cookie(session) 82 | session.chdir(cookie) 83 | 84 | session.run( 85 | "pre-commit", 86 | "run", 87 | "--all-files", 88 | "--hook-stage=manual", 89 | "--show-diff-on-failure", 90 | ) 91 | 92 | 93 | @nox.session(name="pre-commit") 94 | def pre_commit(session: nox.Session) -> str: 95 | """ 96 | Run linters on the codebase. 97 | """ 98 | session.install("pre-commit") 99 | session.run("pre-commit", "run", "-a") 100 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | # Maintain dependencies for GitHub Actions 4 | - package-ecosystem: "github-actions" 5 | directory: "/" 6 | schedule: 7 | interval: "weekly" 8 | groups: 9 | actions: 10 | patterns: 11 | - "*" 12 | commit-message: 13 | # Prefix all commit messages with "COMP: " 14 | prefix: "COMP" 15 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/.github/workflows/commit-message.yml: -------------------------------------------------------------------------------- 1 | name: "Commit Message Check" 2 | on: 3 | pull_request: 4 | types: 5 | - opened 6 | - edited 7 | - reopened 8 | - synchronize 9 | push: 10 | branches: 11 | - main 12 | 13 | permissions: 14 | contents: read 15 | pull-requests: read 16 | 17 | jobs: 18 | check-commit-message: 19 | runs-on: ubuntu-latest 20 | steps: 21 | - name: Check Commit Message 22 | uses: Slicer/slicer-check-commit-message-action@26db1d4a0580194025a00bff6f5c24a88c0efb9f # v1.0.0 23 | with: 24 | token: {% raw %}${{ secrets.GITHUB_TOKEN }}{% endraw %} 25 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: CI (Lint) 2 | 3 | on: 4 | workflow_dispatch: 5 | pull_request: 6 | push: 7 | branches: 8 | - main 9 | 10 | concurrency: 11 | group: {% raw %}${{ github.workflow }}-${{ github.ref }}{% endraw %} 12 | cancel-in-progress: true 13 | 14 | jobs: 15 | pre-commit: 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 19 | 20 | - uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f # v5.1.1 21 | with: 22 | python-version: "3.9" 23 | 24 | - uses: pre-commit/action@2c7b3805fd2a0fd8c1884dcaebf91fc102a13ecd # v3.0.1 25 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/pre-commit/pre-commit-hooks 3 | rev: "v4.6.0" 4 | hooks: 5 | - id: check-added-large-files 6 | - id: check-case-conflict 7 | - id: check-merge-conflict 8 | - id: check-symlinks 9 | - id: check-toml 10 | - id: check-yaml 11 | - id: debug-statements 12 | - id: end-of-file-fixer 13 | exclude: "\\.(md5|svg|vtk|vtp)$|^Resources\\/[^\\/]+\\.h$|Data\\/Input\\/.+$" 14 | - id: mixed-line-ending 15 | exclude: "\\.(svg|vtk|vtp)$" 16 | - id: trailing-whitespace 17 | exclude: "\\.(svg|vtk|vtp)$" 18 | - id: requirements-txt-fixer 19 | 20 | - repo: https://github.com/adamchainz/blacken-docs 21 | rev: "1.18.0" 22 | hooks: 23 | - id: blacken-docs 24 | additional_dependencies: [black==24.*] 25 | 26 | - repo: https://github.com/astral-sh/ruff-pre-commit 27 | rev: v0.6.1 28 | hooks: 29 | # Run the linter. 30 | - id: ruff 31 | args: ["--fix", "--show-fixes"] 32 | # Run the formatter. 33 | - id: ruff-format 34 | 35 | - repo: https://github.com/rbubley/mirrors-prettier 36 | rev: "v3.3.3" 37 | hooks: 38 | - id: prettier 39 | types_or: [yaml, markdown, html, css, scss, javascript, json] 40 | 41 | - repo: https://github.com/codespell-project/codespell 42 | rev: "v2.3.0" 43 | hooks: 44 | - id: codespell 45 | exclude: "(.png|.svg)$" 46 | args: ["-L", "dependees,normaly,therefrom"] 47 | 48 | - repo: https://github.com/python-jsonschema/check-jsonschema 49 | rev: "0.29.1" 50 | hooks: 51 | - id: check-dependabot 52 | - id: check-github-workflows 53 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/.ruff.toml: -------------------------------------------------------------------------------- 1 | target-version = "py39" 2 | line-length = 120 3 | 4 | [lint] 5 | select = [ 6 | "ARG", # flake8-unused-arguments 7 | "ANN", # flake8-annotations 8 | "B", # flake8-bugbear 9 | "C4", # flake8-comprehensions 10 | "D", # pydocstyle 11 | "E", "F", "W", # flake8 12 | "EXE", # flake8-executable 13 | "G", # flake8-logging-format 14 | "I", # isort 15 | "ICN", # flake8-import-conventions 16 | "ISC", # flake8-implicit-str-concat 17 | "NPY", # NumPy specific rules 18 | "PGH", # pygrep-hooks 19 | "PIE", # flake8-pie 20 | "PL", # pylint 21 | "PT", # flake8-pytest-style 22 | "RET", # flake8-return 23 | "RUF", # Ruff-specific 24 | "S", # flake8-bandit 25 | "SIM", # flake8-simplify 26 | "UP", # pyupgrade 27 | "YTT", # flake8-2020 28 | "W", # Warning 29 | ] 30 | extend-ignore = [ 31 | "ANN101", # missing-type-self 32 | "G004", # logging-f-string 33 | "PIE790", # unnecessary-pass 34 | 35 | "D10", # undocumented-public-* 36 | "D200", # One-line docstring should fit on one line 37 | "D205", # 1 blank line required between summary line and description 38 | "D400", # First line should end with a period 39 | "D401", # First line of docstring should be in imperative mood 40 | 41 | # Disable linting rules conflicting with "ruff formatter" 42 | # See https://docs.astral.sh/ruff/formatter/#conflicting-lint-rules 43 | "COM812", 44 | "COM819", 45 | "D206", 46 | "D300", 47 | "E111", 48 | "E114", 49 | "E117", 50 | "ISC001", 51 | "ISC002", 52 | "Q000", 53 | "Q002", 54 | "Q003", 55 | "W191", 56 | ] 57 | flake8-annotations.suppress-dummy-args = true 58 | flake8-annotations.suppress-none-returning = true 59 | isort.known-third-party = [ 60 | "ctk", 61 | "qt", 62 | "slicer", 63 | "SlicerCustomAppUtilities", 64 | "vtk", 65 | ] 66 | isort.known-first-party = [ 67 | "Home", 68 | "HomeLib", 69 | "Resources", 70 | ] 71 | pydocstyle.convention = "pep257" 72 | 73 | [lint.per-file-ignores] 74 | # Disable "RUF012: Mutable class attributes should be annotated" 75 | # to allow convenient declaration of instance variables in the HomeWidget class. 76 | "Modules/Scripted/Home/Home.py" = ["RUF012"] 77 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/Applications/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | #============================================================================ 2 | # 3 | # Copyright (c) Kitware, Inc. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0.txt 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | #============================================================================ 18 | 19 | add_subdirectory({{cookiecutter.app_name}}App) 20 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/Applications/{{cookiecutter.app_name}}App/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | #============================================================================ 2 | # 3 | # Copyright (c) Kitware, Inc. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0.txt 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | #============================================================================ 18 | 19 | project({{cookiecutter.app_name}}App) 20 | 21 | include(SlicerMacroBuildApplication) 22 | 23 | # -------------------------------------------------------------------------- 24 | # Properties 25 | # -------------------------------------------------------------------------- 26 | SlicerReadApplicationProperties() 27 | 28 | # -------------------------------------------------------------------------- 29 | # Folder 30 | # -------------------------------------------------------------------------- 31 | set(${PROJECT_NAME}_FOLDER "App-${PROJECT_NAME}") 32 | 33 | # -------------------------------------------------------------------------- 34 | # Application library 35 | # -------------------------------------------------------------------------- 36 | set(APPLIB_NAME "q${PROJECT_NAME}") 37 | 38 | set(APPLIB_SRCS 39 | q{{cookiecutter.app_name}}AppMainWindow.cxx 40 | q{{cookiecutter.app_name}}AppMainWindow.h 41 | Widgets/qAppStyle.cxx 42 | Widgets/qAppStyle.h 43 | ) 44 | 45 | set(APPLIB_MOC_SRCS 46 | q{{cookiecutter.app_name}}AppMainWindow.h 47 | Widgets/qAppStyle.h 48 | ) 49 | 50 | set(APPLIB_UI_SRCS 51 | ) 52 | 53 | set(APPLIB_RESOURCES 54 | Resources/App.qrc 55 | ) 56 | 57 | slicerMacroBuildAppLibrary( 58 | NAME ${APPLIB_NAME} 59 | DESCRIPTION_SUMMARY ${${PROJECT_NAME}_DESCRIPTION_SUMMARY} 60 | DESCRIPTION_FILE ${${PROJECT_NAME}_DESCRIPTION_FILE} 61 | APPLICATION_NAME ${${PROJECT_NAME}_APPLICATION_NAME} 62 | EXPORT_DIRECTIVE "Q_{{cookiecutter.app_name|upper}}_APP_EXPORT" 63 | FOLDER ${${PROJECT_NAME}_FOLDER} 64 | SRCS ${APPLIB_SRCS} 65 | MOC_SRCS ${APPLIB_MOC_SRCS} 66 | UI_SRCS ${APPLIB_UI_SRCS} 67 | RESOURCES ${APPLIB_RESOURCES} 68 | ) 69 | 70 | # -------------------------------------------------------------------------- 71 | # Application executable 72 | # -------------------------------------------------------------------------- 73 | 74 | # Configure launcher only for the main application 75 | set(extra_args) 76 | if(${PROJECT_NAME} STREQUAL ${Slicer_MAIN_PROJECT}) 77 | set(extra_args CONFIGURE_LAUNCHER) 78 | endif() 79 | 80 | set(APP_SRCS 81 | Main.cxx 82 | ) 83 | 84 | set(APP_TARGET_LIBRARIES ${APPLIB_NAME}) 85 | 86 | slicerMacroBuildApplication( 87 | NAME ${PROJECT_NAME} 88 | APPLICATION_NAME ${${PROJECT_NAME}_APPLICATION_NAME} 89 | LAUNCHER_SPLASHSCREEN_FILE ${${PROJECT_NAME}_LAUNCHER_SPLASHSCREEN_FILE} 90 | APPLE_ICON_FILE ${${PROJECT_NAME}_APPLE_ICON_FILE} 91 | WIN_ICON_FILE ${${PROJECT_NAME}_WIN_ICON_FILE} 92 | LICENSE_FILE ${${PROJECT_NAME}_LICENSE_FILE} 93 | FOLDER ${${PROJECT_NAME}_FOLDER} 94 | SRCS ${APP_SRCS} 95 | TARGET_LIBRARIES ${APP_TARGET_LIBRARIES} 96 | TARGET_NAME_VAR "APP_TARGET_NAME" 97 | DEFAULT_SETTINGS_FILE Resources/Settings/DefaultSettings.ini 98 | ${extra_args} 99 | ) 100 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/Applications/{{cookiecutter.app_name}}App/Main.cxx: -------------------------------------------------------------------------------- 1 | /*============================================================================== 2 | 3 | Copyright (c) Kitware, Inc. 4 | 5 | See http://www.slicer.org/copyright/copyright.txt for details. 6 | 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | 13 | This file was originally developed by Jean-Christophe Fillion-Robin, Kitware, Inc. 14 | and was partially funded by NIH grant 3P41RR013218-12S1 15 | 16 | ==============================================================================*/ 17 | 18 | // {{cookiecutter.app_name}} includes 19 | #include "q{{cookiecutter.app_name}}AppMainWindow.h" 20 | #include "Widgets/qAppStyle.h" 21 | 22 | // Slicer includes 23 | #include "qSlicerApplication.h" 24 | #include "qSlicerApplicationHelper.h" 25 | #include "vtkSlicerConfigure.h" // For Slicer_MAIN_PROJECT_APPLICATION_NAME 26 | #include "vtkSlicerVersionConfigure.h" // For Slicer_MAIN_PROJECT_VERSION_FULL 27 | 28 | namespace 29 | { 30 | 31 | //---------------------------------------------------------------------------- 32 | int SlicerAppMain(int argc, char* argv[]) 33 | { 34 | typedef q{{cookiecutter.app_name}}AppMainWindow SlicerMainWindowType; 35 | 36 | qSlicerApplicationHelper::preInitializeApplication(argv[0], new qAppStyle); 37 | 38 | qSlicerApplication app(argc, argv); 39 | if (app.returnCode() != -1) 40 | { 41 | return app.returnCode(); 42 | } 43 | 44 | QScopedPointer window; 45 | QScopedPointer splashScreen; 46 | 47 | qSlicerApplicationHelper::postInitializeApplication( 48 | app, splashScreen, window); 49 | 50 | if (!window.isNull()) 51 | { 52 | QString windowTitle = QString("%1 %2").arg(Slicer_MAIN_PROJECT_APPLICATION_NAME).arg(Slicer_MAIN_PROJECT_VERSION_FULL); 53 | window->setWindowTitle(windowTitle); 54 | } 55 | 56 | return app.exec(); 57 | } 58 | 59 | } // end of anonymous namespace 60 | 61 | #include "qSlicerApplicationMainWrapper.cxx" 62 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/Applications/{{cookiecutter.app_name}}App/Resources/App.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | Settings/DefaultSettings.ini 4 | Icons/Small/DesktopIcon.png 5 | Icons/Medium/DesktopIcon.png 6 | Icons/Large/DesktopIcon.png 7 | Icons/XLarge/DesktopIcon.png 8 | Images/Logo.png 9 | Images/LogoFull.png 10 | Images/SplashScreen.png 11 | 12 | 13 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/Applications/{{cookiecutter.app_name}}App/Resources/Icons/DesktopIcon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KitwareMedical/SlicerCustomAppTemplate/c2abc0ce3eeea2b5d59239cdf38da4a64bac0b90/{{cookiecutter.project_name}}/Applications/{{cookiecutter.app_name}}App/Resources/Icons/DesktopIcon.icns -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/Applications/{{cookiecutter.app_name}}App/Resources/Icons/DesktopIcon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KitwareMedical/SlicerCustomAppTemplate/c2abc0ce3eeea2b5d59239cdf38da4a64bac0b90/{{cookiecutter.project_name}}/Applications/{{cookiecutter.app_name}}App/Resources/Icons/DesktopIcon.ico -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/Applications/{{cookiecutter.app_name}}App/Resources/Icons/Large/DesktopIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KitwareMedical/SlicerCustomAppTemplate/c2abc0ce3eeea2b5d59239cdf38da4a64bac0b90/{{cookiecutter.project_name}}/Applications/{{cookiecutter.app_name}}App/Resources/Icons/Large/DesktopIcon.png -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/Applications/{{cookiecutter.app_name}}App/Resources/Icons/Medium/DesktopIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KitwareMedical/SlicerCustomAppTemplate/c2abc0ce3eeea2b5d59239cdf38da4a64bac0b90/{{cookiecutter.project_name}}/Applications/{{cookiecutter.app_name}}App/Resources/Icons/Medium/DesktopIcon.png -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/Applications/{{cookiecutter.app_name}}App/Resources/Icons/Small/DesktopIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KitwareMedical/SlicerCustomAppTemplate/c2abc0ce3eeea2b5d59239cdf38da4a64bac0b90/{{cookiecutter.project_name}}/Applications/{{cookiecutter.app_name}}App/Resources/Icons/Small/DesktopIcon.png -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/Applications/{{cookiecutter.app_name}}App/Resources/Icons/XLarge/DesktopIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KitwareMedical/SlicerCustomAppTemplate/c2abc0ce3eeea2b5d59239cdf38da4a64bac0b90/{{cookiecutter.project_name}}/Applications/{{cookiecutter.app_name}}App/Resources/Icons/XLarge/DesktopIcon.png -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/Applications/{{cookiecutter.app_name}}App/Resources/Images/Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KitwareMedical/SlicerCustomAppTemplate/c2abc0ce3eeea2b5d59239cdf38da4a64bac0b90/{{cookiecutter.project_name}}/Applications/{{cookiecutter.app_name}}App/Resources/Images/Logo.png -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/Applications/{{cookiecutter.app_name}}App/Resources/Images/LogoFull.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KitwareMedical/SlicerCustomAppTemplate/c2abc0ce3eeea2b5d59239cdf38da4a64bac0b90/{{cookiecutter.project_name}}/Applications/{{cookiecutter.app_name}}App/Resources/Images/LogoFull.png -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/Applications/{{cookiecutter.app_name}}App/Resources/Images/SplashScreen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KitwareMedical/SlicerCustomAppTemplate/c2abc0ce3eeea2b5d59239cdf38da4a64bac0b90/{{cookiecutter.project_name}}/Applications/{{cookiecutter.app_name}}App/Resources/Images/SplashScreen.png -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/Applications/{{cookiecutter.app_name}}App/Resources/Installer/Header.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KitwareMedical/SlicerCustomAppTemplate/c2abc0ce3eeea2b5d59239cdf38da4a64bac0b90/{{cookiecutter.project_name}}/Applications/{{cookiecutter.app_name}}App/Resources/Installer/Header.bmp -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/Applications/{{cookiecutter.app_name}}App/Resources/Installer/WelcomeFinishPage.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KitwareMedical/SlicerCustomAppTemplate/c2abc0ce3eeea2b5d59239cdf38da4a64bac0b90/{{cookiecutter.project_name}}/Applications/{{cookiecutter.app_name}}App/Resources/Installer/WelcomeFinishPage.bmp -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/Applications/{{cookiecutter.app_name}}App/Resources/Settings/DefaultSettings.ini: -------------------------------------------------------------------------------- 1 | [General] 2 | 3 | [ApplicationUpdate] 4 | ServerUrl= 5 | 6 | [MainWindow] 7 | DontConfirmExit=1024 8 | DontConfirmRestart=1024 9 | DontShowDisclaimerMessage=1024 10 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/Applications/{{cookiecutter.app_name}}App/Widgets/qAppStyle.cxx: -------------------------------------------------------------------------------- 1 | /*============================================================================== 2 | 3 | Copyright (c) Kitware, Inc. 4 | 5 | See http://www.slicer.org/copyright/copyright.txt for details. 6 | 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | 13 | This file was originally developed by Julien Finet, Kitware, Inc. 14 | and was partially funded by NIH grant 3P41RR013218-12S1 15 | 16 | ==============================================================================*/ 17 | 18 | // Qt includes 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | // CTK includes 30 | #include 31 | 32 | // {{cookiecutter.app_name}} includes 33 | #include "qAppStyle.h" 34 | 35 | // -------------------------------------------------------------------------- 36 | // qAppStyle methods 37 | 38 | // -------------------------------------------------------------------------- 39 | qAppStyle::qAppStyle() 40 | { 41 | // Slicer uses a QCleanlooksStyle as base style. 42 | this->setBaseStyle(new QProxyStyle(QStyleFactory::create("fusion"))); 43 | } 44 | 45 | // -------------------------------------------------------------------------- 46 | qAppStyle::~qAppStyle() 47 | { 48 | } 49 | 50 | //------------------------------------------------------------------------------ 51 | QPalette qAppStyle::standardPalette()const 52 | { 53 | QPalette palette = this->Superclass::standardPalette(); 54 | 55 | palette.setColor(QPalette::Active, QPalette::Window, "#eaebee"); 56 | palette.setColor(QPalette::Inactive, QPalette::Window, "#eaebee"); 57 | palette.setColor(QPalette::Disabled, QPalette::Window, "#dedfe1"); 58 | palette.setColor(QPalette::Active, QPalette::WindowText, "#002f4f"); 59 | palette.setColor(QPalette::Inactive, QPalette::WindowText, "#002f4f"); 60 | palette.setColor(QPalette::Disabled, QPalette::WindowText, "#2a404f"); 61 | palette.setColor(QPalette::Active, QPalette::Text, "#002f4f"); 62 | palette.setColor(QPalette::Inactive, QPalette::Text, "#002f4f"); 63 | palette.setColor(QPalette::Disabled, QPalette::Text, "#2a404f"); 64 | palette.setColor(QPalette::Active, QPalette::Base, "#ffffff"); 65 | palette.setColor(QPalette::Inactive, QPalette::Base, "#ffffff"); 66 | palette.setColor(QPalette::Disabled, QPalette::Base, "#eaebee"); 67 | 68 | 69 | palette.setColor(QPalette::Light, "#ffffff"); 70 | palette.setColor(QPalette::Button, "#dedfe1"); 71 | palette.setColor(QPalette::Mid, "#005f9e"); 72 | palette.setColor(QPalette::Dark, "#005f9e"); 73 | palette.setColor(QPalette::Active, QPalette::ButtonText, "#005f9e"); 74 | palette.setColor(QPalette::Inactive, QPalette::ButtonText, "#005f9e"); 75 | palette.setColor(QPalette::Disabled, QPalette::ButtonText, "#003050"); 76 | palette.setColor(QPalette::Shadow, "#002f4f"); 77 | 78 | palette.setColor(QPalette::Highlight, "#009d49"); 79 | palette.setColor(QPalette::HighlightedText, "#ffffff"); 80 | 81 | return palette; 82 | } 83 | 84 | //------------------------------------------------------------------------------ 85 | void qAppStyle::drawComplexControl(ComplexControl control, 86 | const QStyleOptionComplex* option, 87 | QPainter* painter, 88 | const QWidget* widget )const 89 | { 90 | const_cast(option)->palette = 91 | this->tweakWidgetPalette(option->palette, widget); 92 | this->Superclass::drawComplexControl(control, option, painter, widget); 93 | } 94 | 95 | //------------------------------------------------------------------------------ 96 | void qAppStyle::drawControl(ControlElement element, 97 | const QStyleOption* option, 98 | QPainter* painter, 99 | const QWidget* widget )const 100 | { 101 | const_cast(option)->palette = 102 | this->tweakWidgetPalette(option->palette, widget); 103 | 104 | // For some reason the toolbar paint routine is not respecting the palette. 105 | // here we make sure the background is correctly drawn. 106 | if (element == QStyle::CE_ToolBar && 107 | qobject_cast(widget)) 108 | { 109 | painter->fillRect(option->rect, option->palette.brush(QPalette::Window)); 110 | } 111 | this->Superclass::drawControl(element, option, painter, widget); 112 | } 113 | 114 | //------------------------------------------------------------------------------ 115 | void qAppStyle::drawPrimitive(PrimitiveElement element, 116 | const QStyleOption* option, 117 | QPainter* painter, 118 | const QWidget* widget )const 119 | { 120 | const_cast(option)->palette = 121 | this->tweakWidgetPalette(option->palette, widget); 122 | this->Superclass::drawPrimitive(element, option, painter, widget); 123 | } 124 | 125 | //------------------------------------------------------------------------------ 126 | QPalette qAppStyle::tweakWidgetPalette(QPalette widgetPalette, 127 | const QWidget* widget)const 128 | { 129 | if (!widget) 130 | { 131 | return widgetPalette; 132 | } 133 | const QPushButton* pushButton = 134 | qobject_cast(widget); 135 | if (pushButton && 136 | !pushButton->text().isEmpty()) 137 | { 138 | QColor buttonColor = this->standardPalette().color(QPalette::Dark); 139 | widgetPalette.setColor(QPalette::Active, QPalette::Button, buttonColor); 140 | widgetPalette.setColor(QPalette::Inactive, QPalette::Button, buttonColor); 141 | QColor disabledButtonColor = buttonColor.toHsv(); 142 | disabledButtonColor.setHsvF(disabledButtonColor.hueF(), 143 | disabledButtonColor.saturationF() * 0.8, 144 | disabledButtonColor.valueF() * 0.9); 145 | widgetPalette.setColor(QPalette::Disabled, QPalette::Button, disabledButtonColor); 146 | QColor buttonTextColor = 147 | this->standardPalette().color(QPalette::Light); 148 | widgetPalette.setColor(QPalette::Active, QPalette::ButtonText, buttonTextColor); 149 | widgetPalette.setColor(QPalette::Inactive, QPalette::ButtonText, buttonTextColor); 150 | QColor disabledButtonTextColor = buttonTextColor.toHsv(); 151 | disabledButtonTextColor.setHsvF(buttonColor.hueF(), 152 | disabledButtonTextColor.saturationF() * 0.3, 153 | disabledButtonTextColor.valueF() * 0.8); 154 | widgetPalette.setColor(QPalette::Disabled, QPalette::ButtonText, disabledButtonColor); 155 | } 156 | if (qobject_cast(widget)) 157 | { 158 | QColor highlightColor = this->standardPalette().color(QPalette::Dark); 159 | //QBrush highlightBrush = this->standardPalette().brush(QPalette::Dark); 160 | QColor highlightTextColor = 161 | this->standardPalette().color(QPalette::Light); 162 | QBrush highlightTextBrush = 163 | this->standardPalette().brush(QPalette::Light); 164 | QColor darkColor = this->standardPalette().color(QPalette::Highlight); 165 | QColor lightColor = 166 | this->standardPalette().color(QPalette::HighlightedText); 167 | 168 | QLinearGradient hilightGradient(0., 0., 0., 1.); 169 | hilightGradient.setCoordinateMode(QGradient::ObjectBoundingMode); 170 | hilightGradient.setColorAt(0., highlightColor); 171 | hilightGradient.setColorAt(1., highlightColor.darker(120)); 172 | QBrush highlightBrush(hilightGradient); 173 | 174 | widgetPalette.setColor(QPalette::Highlight, darkColor); 175 | widgetPalette.setColor(QPalette::HighlightedText, lightColor); 176 | 177 | widgetPalette.setColor(QPalette::Window, highlightColor); 178 | widgetPalette.setColor(QPalette::WindowText, highlightTextColor); 179 | widgetPalette.setColor(QPalette::Base, highlightColor); 180 | widgetPalette.setColor(QPalette::Text, highlightTextColor); 181 | widgetPalette.setColor(QPalette::Button, highlightColor); 182 | widgetPalette.setColor(QPalette::ButtonText, highlightTextColor); 183 | 184 | widgetPalette.setBrush(QPalette::Window, highlightBrush); 185 | widgetPalette.setBrush(QPalette::WindowText, highlightTextBrush); 186 | widgetPalette.setBrush(QPalette::Base, highlightBrush); 187 | widgetPalette.setBrush(QPalette::Text, highlightTextBrush); 188 | widgetPalette.setBrush(QPalette::Button, highlightBrush); 189 | widgetPalette.setBrush(QPalette::ButtonText, highlightTextBrush); 190 | } 191 | /* 192 | QWidget* parentWidget = widget->parentWidget(); 193 | QWidget* grandParentWidget = parentWidget? parentWidget->parentWidget() : 0; 194 | if (qobject_cast(widget) || 195 | qobject_cast(parentWidget) || 196 | qobject_cast(grandParentWidget)) 197 | { 198 | QColor windowColor = this->standardPalette().color(QPalette::Window); 199 | 200 | //QColor highlightColor = this->standardPalette().color(QPalette::Highlight); 201 | //QColor highlightTextColor = 202 | // this->standardPalette().color(QPalette::HighlightedText); 203 | //QColor darkColor = this->standardPalette().color(QPalette::Dark); 204 | //QColor lightColor = 205 | // this->standardPalette().color(QPalette::Light); 206 | QColor highlightColor = this->standardPalette().color(QPalette::Dark); 207 | //QBrush highlightBrush = this->standardPalette().brush(QPalette::Dark); 208 | QBrush highlightTextBrush = 209 | this->standardPalette().brush(QPalette::Light); 210 | QColor darkColor = this->standardPalette().color(QPalette::Highlight); 211 | QColor lightColor = 212 | this->standardPalette().color(QPalette::HighlightedText); 213 | 214 | QLinearGradient hilightGradient(0., 0., 0., 1.); 215 | hilightGradient.setCoordinateMode(QGradient::ObjectBoundingMode); 216 | 217 | hilightGradient.setColorAt(0., highlightColor); 218 | hilightGradient.setColorAt(1., highlightColor.darker(140)); 219 | QBrush highlightBrush(hilightGradient); 220 | 221 | widgetPalette.setColor(QPalette::Highlight, darkColor); 222 | widgetPalette.setColor(QPalette::HighlightedText, lightColor); 223 | widgetPalette.setBrush(QPalette::Window, highlightBrush); 224 | widgetPalette.setBrush(QPalette::WindowText, highlightTextBrush); 225 | widgetPalette.setBrush(QPalette::Base, highlightBrush); 226 | widgetPalette.setBrush(QPalette::Text, highlightTextBrush); 227 | widgetPalette.setBrush(QPalette::Button, highlightBrush); 228 | widgetPalette.setBrush(QPalette::ButtonText, highlightTextBrush); 229 | } 230 | */ 231 | return widgetPalette; 232 | } 233 | 234 | //------------------------------------------------------------------------------ 235 | void qAppStyle::polish(QWidget* widget) 236 | { 237 | this->Superclass::polish(widget); 238 | ctkCollapsibleButton* collapsibleButton = 239 | qobject_cast(widget); 240 | if (collapsibleButton) 241 | { 242 | collapsibleButton->setFlat(true); 243 | collapsibleButton->setContentsFrameShadow(QFrame::Sunken); 244 | } 245 | } 246 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/Applications/{{cookiecutter.app_name}}App/Widgets/qAppStyle.h: -------------------------------------------------------------------------------- 1 | /*============================================================================== 2 | 3 | Copyright (c) Kitware, Inc. 4 | 5 | See http://www.slicer.org/copyright/copyright.txt for details. 6 | 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | 13 | This file was originally developed by Julien Finet, Kitware, Inc. 14 | and was partially funded by NIH grant 3P41RR013218-12S1 15 | 16 | ==============================================================================*/ 17 | 18 | #ifndef __qAppStyle_h 19 | #define __qAppStyle_h 20 | 21 | // {{cookiecutter.app_name}} includes 22 | #include "q{{cookiecutter.app_name}}AppExport.h" 23 | 24 | // Slicer includes 25 | #include "qSlicerStyle.h" 26 | 27 | class Q_{{cookiecutter.app_name|upper}}_APP_EXPORT qAppStyle 28 | : public qSlicerStyle 29 | { 30 | Q_OBJECT 31 | public: 32 | /// Superclass typedef 33 | typedef qSlicerStyle Superclass; 34 | 35 | /// Constructors 36 | qAppStyle(); 37 | virtual ~qAppStyle(); 38 | 39 | /// Reimplemented to customize colors. 40 | /// \sa QStyle::standardPalette() 41 | virtual QPalette standardPalette() const; 42 | 43 | /// Reimplemented to apply custom palette to widgets 44 | /// \sa QStyle::drawComplexControl() 45 | void drawComplexControl(ComplexControl control, 46 | const QStyleOptionComplex* option, 47 | QPainter* painter, 48 | const QWidget* widget = 0)const; 49 | /// Reimplemented to apply custom palette to widgets 50 | /// \sa QStyle::drawControl() 51 | virtual void drawControl(ControlElement element, 52 | const QStyleOption* option, 53 | QPainter* painter, 54 | const QWidget* widget = 0 )const; 55 | 56 | /// Reimplemented to apply custom palette to widgets 57 | /// \sa QStyle::drawPrimitive() 58 | virtual void drawPrimitive(PrimitiveElement element, 59 | const QStyleOption* option, 60 | QPainter* painter, 61 | const QWidget* widget = 0 )const; 62 | 63 | /// Tweak the colors of some widgets. 64 | virtual QPalette tweakWidgetPalette(QPalette palette, 65 | const QWidget* widget)const; 66 | 67 | /// Reimplemented to apply styling to widgets. 68 | /// \sa QStyle::polish() 69 | virtual void polish(QWidget* widget); 70 | using Superclass::polish; 71 | }; 72 | 73 | #endif 74 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/Applications/{{cookiecutter.app_name}}App/q{{cookiecutter.app_name}}AppMainWindow.cxx: -------------------------------------------------------------------------------- 1 | /*============================================================================== 2 | 3 | Copyright (c) Kitware, Inc. 4 | 5 | See http://www.slicer.org/copyright/copyright.txt for details. 6 | 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | 13 | This file was originally developed by Julien Finet, Kitware, Inc. 14 | and was partially funded by NIH grant 3P41RR013218-12S1 15 | 16 | ==============================================================================*/ 17 | 18 | // {{cookiecutter.app_name}} includes 19 | #include "q{{cookiecutter.app_name}}AppMainWindow.h" 20 | #include "q{{cookiecutter.app_name}}AppMainWindow_p.h" 21 | 22 | // Qt includes 23 | #include 24 | #include 25 | 26 | // Slicer includes 27 | #include "qSlicerApplication.h" 28 | #include "qSlicerAboutDialog.h" 29 | #include "qSlicerMainWindow_p.h" 30 | #include "qSlicerModuleSelectorToolBar.h" 31 | 32 | //----------------------------------------------------------------------------- 33 | // q{{cookiecutter.app_name}}AppMainWindowPrivate methods 34 | 35 | q{{cookiecutter.app_name}}AppMainWindowPrivate::q{{cookiecutter.app_name}}AppMainWindowPrivate(q{{cookiecutter.app_name}}AppMainWindow& object) 36 | : Superclass(object) 37 | { 38 | } 39 | 40 | //----------------------------------------------------------------------------- 41 | q{{cookiecutter.app_name}}AppMainWindowPrivate::~q{{cookiecutter.app_name}}AppMainWindowPrivate() 42 | { 43 | } 44 | 45 | //----------------------------------------------------------------------------- 46 | void q{{cookiecutter.app_name}}AppMainWindowPrivate::init() 47 | { 48 | #if (QT_VERSION >= QT_VERSION_CHECK(5, 7, 0)) 49 | QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); 50 | #endif 51 | Q_Q(q{{cookiecutter.app_name}}AppMainWindow); 52 | this->Superclass::init(); 53 | } 54 | 55 | //----------------------------------------------------------------------------- 56 | void q{{cookiecutter.app_name}}AppMainWindowPrivate::setupUi(QMainWindow * mainWindow) 57 | { 58 | qSlicerApplication * app = qSlicerApplication::application(); 59 | 60 | //---------------------------------------------------------------------------- 61 | // Add actions 62 | //---------------------------------------------------------------------------- 63 | QAction* helpAboutSlicerAppAction = new QAction(mainWindow); 64 | helpAboutSlicerAppAction->setObjectName("HelpAbout{{cookiecutter.app_name}}AppAction"); 65 | helpAboutSlicerAppAction->setText("About " + app->applicationName()); 66 | 67 | //---------------------------------------------------------------------------- 68 | // Calling "setupUi()" after adding the actions above allows the call 69 | // to "QMetaObject::connectSlotsByName()" done in "setupUi()" to 70 | // successfully connect each slot with its corresponding action. 71 | this->Superclass::setupUi(mainWindow); 72 | 73 | // Add Help Menu Action 74 | this->HelpMenu->addAction(helpAboutSlicerAppAction); 75 | 76 | //---------------------------------------------------------------------------- 77 | // Configure 78 | //---------------------------------------------------------------------------- 79 | mainWindow->setWindowIcon(QIcon(":/Icons/Medium/DesktopIcon.png")); 80 | 81 | QLabel* logoLabel = new QLabel(); 82 | logoLabel->setObjectName("LogoLabel"); 83 | logoLabel->setPixmap(qMRMLWidget::pixmapFromIcon(QIcon(":/LogoFull.png"))); 84 | this->PanelDockWidget->setTitleBarWidget(logoLabel); 85 | 86 | // Hide the menus 87 | //this->menubar->setVisible(false); 88 | //this->FileMenu->setVisible(false); 89 | //this->EditMenu->setVisible(false); 90 | //this->ViewMenu->setVisible(false); 91 | //this->LayoutMenu->setVisible(false); 92 | //this->HelpMenu->setVisible(false); 93 | } 94 | 95 | //----------------------------------------------------------------------------- 96 | // q{{cookiecutter.app_name}}AppMainWindow methods 97 | 98 | //----------------------------------------------------------------------------- 99 | q{{cookiecutter.app_name}}AppMainWindow::q{{cookiecutter.app_name}}AppMainWindow(QWidget* windowParent) 100 | : Superclass(new q{{cookiecutter.app_name}}AppMainWindowPrivate(*this), windowParent) 101 | { 102 | Q_D(q{{cookiecutter.app_name}}AppMainWindow); 103 | d->init(); 104 | } 105 | 106 | //----------------------------------------------------------------------------- 107 | q{{cookiecutter.app_name}}AppMainWindow::q{{cookiecutter.app_name}}AppMainWindow( 108 | q{{cookiecutter.app_name}}AppMainWindowPrivate* pimpl, QWidget* windowParent) 109 | : Superclass(pimpl, windowParent) 110 | { 111 | // init() is called by derived class. 112 | } 113 | 114 | //----------------------------------------------------------------------------- 115 | q{{cookiecutter.app_name}}AppMainWindow::~q{{cookiecutter.app_name}}AppMainWindow() 116 | { 117 | } 118 | 119 | //----------------------------------------------------------------------------- 120 | void q{{cookiecutter.app_name}}AppMainWindow::on_HelpAbout{{cookiecutter.app_name}}AppAction_triggered() 121 | { 122 | qSlicerAboutDialog about(this); 123 | about.setLogo(QPixmap(":/Logo.png")); 124 | about.exec(); 125 | } 126 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/Applications/{{cookiecutter.app_name}}App/q{{cookiecutter.app_name}}AppMainWindow.h: -------------------------------------------------------------------------------- 1 | /*============================================================================== 2 | 3 | Copyright (c) Kitware, Inc. 4 | 5 | See http://www.slicer.org/copyright/copyright.txt for details. 6 | 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | 13 | This file was originally developed by Julien Finet, Kitware, Inc. 14 | and was partially funded by NIH grant 3P41RR013218-12S1 15 | 16 | ==============================================================================*/ 17 | 18 | #ifndef __q{{cookiecutter.app_name}}AppMainWindow_h 19 | #define __q{{cookiecutter.app_name}}AppMainWindow_h 20 | 21 | // {{cookiecutter.app_name}} includes 22 | #include "q{{cookiecutter.app_name}}AppExport.h" 23 | class q{{cookiecutter.app_name}}AppMainWindowPrivate; 24 | 25 | // Slicer includes 26 | #include "qSlicerMainWindow.h" 27 | 28 | class Q_{{cookiecutter.app_name|upper}}_APP_EXPORT q{{cookiecutter.app_name}}AppMainWindow : public qSlicerMainWindow 29 | { 30 | Q_OBJECT 31 | public: 32 | typedef qSlicerMainWindow Superclass; 33 | 34 | q{{cookiecutter.app_name}}AppMainWindow(QWidget *parent=0); 35 | virtual ~q{{cookiecutter.app_name}}AppMainWindow(); 36 | 37 | public slots: 38 | void on_HelpAbout{{cookiecutter.app_name}}AppAction_triggered(); 39 | 40 | protected: 41 | q{{cookiecutter.app_name}}AppMainWindow(q{{cookiecutter.app_name}}AppMainWindowPrivate* pimpl, QWidget* parent); 42 | 43 | private: 44 | Q_DECLARE_PRIVATE(q{{cookiecutter.app_name}}AppMainWindow); 45 | Q_DISABLE_COPY(q{{cookiecutter.app_name}}AppMainWindow); 46 | }; 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/Applications/{{cookiecutter.app_name}}App/q{{cookiecutter.app_name}}AppMainWindow_p.h: -------------------------------------------------------------------------------- 1 | /*============================================================================== 2 | 3 | Copyright (c) Kitware, Inc. 4 | 5 | See http://www.slicer.org/copyright/copyright.txt for details. 6 | 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | 13 | This file was originally developed by Julien Finet, Kitware, Inc. 14 | and was partially funded by NIH grant 3P41RR013218-12S1 15 | 16 | ==============================================================================*/ 17 | 18 | #ifndef __q{{cookiecutter.app_name}}AppMainWindow_p_h 19 | #define __q{{cookiecutter.app_name}}AppMainWindow_p_h 20 | 21 | // {{cookiecutter.app_name}} includes 22 | #include "q{{cookiecutter.app_name}}AppMainWindow.h" 23 | 24 | // Slicer includes 25 | #include "qSlicerMainWindow_p.h" 26 | 27 | //----------------------------------------------------------------------------- 28 | class Q_{{cookiecutter.app_name|upper}}_APP_EXPORT q{{cookiecutter.app_name}}AppMainWindowPrivate 29 | : public qSlicerMainWindowPrivate 30 | { 31 | Q_DECLARE_PUBLIC(q{{cookiecutter.app_name}}AppMainWindow); 32 | public: 33 | typedef qSlicerMainWindowPrivate Superclass; 34 | q{{cookiecutter.app_name}}AppMainWindowPrivate(q{{cookiecutter.app_name}}AppMainWindow& object); 35 | virtual ~q{{cookiecutter.app_name}}AppMainWindowPrivate(); 36 | 37 | virtual void init(); 38 | /// Reimplemented for custom behavior 39 | virtual void setupUi(QMainWindow * mainWindow); 40 | }; 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/Applications/{{cookiecutter.app_name}}App/slicer-application-properties.cmake: -------------------------------------------------------------------------------- 1 | 2 | set(APPLICATION_NAME 3 | {{cookiecutter.app_name}} 4 | ) 5 | 6 | set(VERSION_MAJOR 7 | {{cookiecutter.app_version_major}} 8 | ) 9 | set(VERSION_MINOR 10 | {{cookiecutter.app_version_minor}} 11 | ) 12 | set(VERSION_PATCH 13 | {{cookiecutter.app_version_patch}} 14 | ) 15 | 16 | set(DESCRIPTION_SUMMARY 17 | "{{cookiecutter.app_description_summary}}" 18 | ) 19 | set(DESCRIPTION_FILE 20 | ${Slicer_SOURCE_DIR}/README.md 21 | ) 22 | 23 | set(LAUNCHER_SPLASHSCREEN_FILE 24 | "${CMAKE_CURRENT_LIST_DIR}/Resources/Images/SplashScreen.png" 25 | ) 26 | set(APPLE_ICON_FILE 27 | "${CMAKE_CURRENT_LIST_DIR}/Resources/Icons/DesktopIcon.icns" 28 | ) 29 | set(WIN_ICON_FILE 30 | "${CMAKE_CURRENT_LIST_DIR}/Resources/Icons/DesktopIcon.ico" 31 | ) 32 | 33 | set(LICENSE_FILE 34 | "${{ "{" }}{{cookiecutter.project_name}}_SOURCE_DIR}/LICENSE" 35 | ) 36 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/BUILD.md: -------------------------------------------------------------------------------- 1 | # Build and Package {{cookiecutter.project_name}} 2 | 3 | This document summarizes how to build and package {{cookiecutter.project_name}} on Windows. 4 | 5 | {{cookiecutter.project_name}} is a custom Slicer application. Reading the [3D Slicer Developer Documentation](https://slicer.readthedocs.io/en/latest/developer_guide/index.html) may help answer additional questions. 6 | 7 | The initial source files were created using [KitwareMedical/SlicerCustomAppTemplate](https://github.com/KitwareMedical/SlicerCustomAppTemplate). 8 | 9 | ## Prerequisites 10 | 11 | - Setting up your git account: 12 | 13 | - Create a [Github](https://github.com) account. 14 | 15 | - Setup your SSH keys following [these](https://help.github.com/articles/generating-ssh-keys) instructions. 16 | 17 | - Setup [your git username](https://help.github.com/articles/setting-your-username-in-git) and [your git email](https://help.github.com/articles/setting-your-email-in-git). 18 | 19 | - If not already done, email `FirstName LastName ` to be granted access to 20 | the [{{cookiecutter.github_organization}}/{{cookiecutter.github_project}}](https://github.com/{{cookiecutter.github_organization}}/{{cookiecutter.github_project}}) repository. 21 | 22 | ## Checkout 23 | 24 | 1. Start `Git Bash` 25 | 2. Checkout the source code into a directory `C:\W\` by typing the following commands: 26 | 27 | ```bat 28 | cd /c 29 | mkdir W 30 | cd /c/W 31 | git clone https://github.com/{{cookiecutter.github_organization}}/{{cookiecutter.github_project}}.git {{cookiecutter.github_project|first}} 32 | ``` 33 | 34 | Note: use short source and build directory names to avoid the [maximum path length limitation](https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file#maximum-path-length-limitation). 35 | 36 | ## Build 37 | 38 | Note: The build process will take approximately 3 hours. 39 | 40 | Option 1: CMake GUI and Visual Studio (Recommended) 41 | 42 | 1. Start [CMake GUI](https://cmake.org/runningcmake/), select source directory `C:\W\{{cookiecutter.github_project|first}}` and set build directory to `C:\W\{{cookiecutter.github_project|first}}R`. 43 | 2. Add an entry `Qt5_DIR` pointing to `C:/Qt/${QT_VERSION}/${COMPILER}/lib/cmake/Qt5`. 44 | 3. Generate the project. 45 | 4. Open `C:\W\{{cookiecutter.github_project|first}}R\{{cookiecutter.project_name}}.sln`, select `Release` and build the project. 46 | 47 | Option 2: Command Line 48 | 49 | 1. Start the [Command Line Prompt](http://windows.microsoft.com/en-us/windows/command-prompt-faq) 50 | 2. Configure and build the project in `C:\W\{{cookiecutter.github_project|first}}R` by typing the following commands: 51 | 52 | ```bat 53 | cd C:\W\ 54 | mkdir {{cookiecutter.github_project|first}}R 55 | cd {{cookiecutter.github_project|first}}R 56 | cmake -G "Visual Studio 17 2022" -A x64 -DQt5_DIR:PATH=`C:/Qt/${QT_VERSION}/${COMPILER}/lib/cmake/Qt5 ..\{{cookiecutter.github_project|first}} 57 | cmake --build . --config Release -- /maxcpucount:4 58 | ``` 59 | 60 | ## Package 61 | 62 | Install [NSIS 2](http://sourceforge.net/projects/nsis/files/) 63 | 64 | Option 1: CMake and Visual Studio 65 | 66 | 1. In the `C:\W\{{cookiecutter.github_project|first}}R\Slicer-build` directory, open `Slicer.sln` and build the `PACKAGE` target 67 | 68 | Option 2: Command Line 69 | 70 | 1. Start the [Command Line Prompt](http://windows.microsoft.com/en-us/windows/command-prompt-faq) 71 | 2. Build the `PACKAGE` target by typing the following commands: 72 | 73 | ```bat 74 | cd C:\W\{{cookiecutter.github_project|first}}R\Slicer-build 75 | cmake --build . --config Release --target PACKAGE 76 | ``` 77 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16.3) 2 | 3 | # Enable C++14 4 | if(NOT DEFINED CMAKE_CXX_STANDARD) 5 | set(CMAKE_CXX_STANDARD 17) 6 | endif() 7 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 8 | set(CMAKE_CXX_EXTENSIONS OFF) 9 | 10 | set (EP_GIT_PROTOCOL "https") 11 | 12 | # Slicer sources 13 | include(FetchContent) 14 | if(NOT DEFINED slicersources_SOURCE_DIR) 15 | # Download Slicer sources and set variables slicersources_SOURCE_DIR and slicersources_BINARY_DIR 16 | FetchContent_Populate(slicersources 17 | GIT_REPOSITORY ${EP_GIT_PROTOCOL}://github.com/Slicer/Slicer 18 | GIT_TAG {% github_repo_branch_sha "Slicer/Slicer", "main" %} 19 | GIT_PROGRESS 1 20 | ) 21 | else() 22 | set(slicersources_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/slicersources-subbuild) 23 | endif() 24 | 25 | # macOS initialization 26 | set(CMAKE_MODULE_PATH ${slicersources_SOURCE_DIR}/CMake ${CMAKE_MODULE_PATH}) 27 | include(SlicerInitializeOSXVariables) 28 | 29 | project({{cookiecutter.project_name}}) 30 | 31 | # Configure Application 32 | set(Slicer_APPLICATIONS_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Applications) 33 | set(Slicer_MAIN_PROJECT "{{cookiecutter.app_name}}App") 34 | 35 | # Set organization 36 | set(Slicer_ORGANIZATION_DOMAIN "{{cookiecutter.org_domain}}") 37 | set(Slicer_ORGANIZATION_NAME "{{cookiecutter.org_name}}") 38 | 39 | # Default home and favorite modules 40 | set(Slicer_DEFAULT_HOME_MODULE "Home") 41 | set(Slicer_DEFAULT_FAVORITE_MODULES "Data, Volumes, Models, Transforms, Markups, SegmentEditor") 42 | 43 | # Configure SuperBuild 44 | set(SUPERBUILD_TOPLEVEL_PROJECT Slicer) 45 | set(EXTERNAL_PROJECT_ADDITIONAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/SuperBuild") 46 | include(ExternalProjectDependency) 47 | 48 | # Additional Slicer dependencies looked up in EXTERNAL_PROJECT_ADDITIONAL_DIR 49 | set(Slicer_ADDITIONAL_DEPENDENCIES 50 | ) 51 | 52 | # Enable listed remote modules from ITK 53 | set(Slicer_ITK_ADDITIONAL_MODULES 54 | ) 55 | 56 | if(NOT CMAKE_CONFIGURATION_TYPES) 57 | set(Slicer_DEFAULT_BUILD_TYPE "Release") 58 | endif() 59 | include(SlicerInitializeBuildType) 60 | include(SlicerInitializeReleaseType) 61 | 62 | # Set application bundle identifier for macOS 63 | if(APPLE) 64 | set(Slicer_MACOSX_BUNDLE_GUI_IDENTIFIER "{{cookiecutter.bundle_identifier}}") 65 | endif() 66 | 67 | # Installation folder and admin account requirement for Windows 68 | if(WIN32) 69 | # Note: To avoid escaping issue, make sure to use forward slash when setting 70 | # "Slicer_CPACK_NSIS_INSTALL_ROOT". It is replaced by "\\\\" in SlicerCPack. 71 | set(Slicer_CPACK_NSIS_INSTALL_REQUIRES_ADMIN_ACCOUNT ON) 72 | if(Slicer_CPACK_NSIS_INSTALL_REQUIRES_ADMIN_ACCOUNT) 73 | # User has administrative privileges, therefore we can install to shared folder 74 | # "C:\Program Files" or "C:\Program Files (x86)". 75 | if(CMAKE_CL_64) 76 | set(Slicer_CPACK_NSIS_INSTALL_ROOT "$PROGRAMFILES64") 77 | else() 78 | set(Slicer_CPACK_NSIS_INSTALL_ROOT "$PROGRAMFILES") 79 | endif() 80 | else() 81 | # We do not require administrative privileges, therefore we install to user folder 82 | # "C:\Users\\AppData\Local". 83 | set(Slicer_CPACK_NSIS_INSTALL_ROOT "$LOCALAPPDATA/${Slicer_ORGANIZATION_NAME}") 84 | endif() 85 | endif() 86 | 87 | # Slicer options 88 | option(BUILD_TESTING "Build application test suite" ON) 89 | option(Slicer_BUILD_APPLICATIONUPDATE_SUPPORT "Build application update support" OFF) 90 | option(Slicer_BUILD_DOCUMENTATION "Build documentation (Doxygen, sphinx, ...)" OFF) 91 | if(WIN32) 92 | option(Slicer_BUILD_WIN32_CONSOLE_LAUNCHER "Build ${PROJECT_NAME} launcher executable as a console app on windows (displays console at application start)" OFF) 93 | option(Slicer_BUILD_WIN32_CONSOLE "Build application executable as a console app (allows capturing and piping console output)" ON) 94 | endif() 95 | 96 | option(Slicer_BUILD_DICOM_SUPPORT "Build application with DICOM support" ON) 97 | option(Slicer_BUILD_DIFFUSION_SUPPORT "Build application with Diffusion support" OFF) 98 | option(Slicer_BUILD_EXTENSIONMANAGER_SUPPORT "Build application with ExtensionManager support" OFF) 99 | option(Slicer_BUILD_MULTIVOLUME_SUPPORT "Build application with MultiVolume support" OFF) 100 | option(Slicer_BUILD_PARAMETERSERIALIZER_SUPPORT "Build application with parameter serializer support" OFF) 101 | option(Slicer_USE_PYTHONQT "Build application with Python support" ON) 102 | option(Slicer_USE_QtTesting "Build application with QtTesting support" OFF) 103 | option(Slicer_USE_SimpleITK "Build application with SimpleITK support" OFF) 104 | 105 | option(Slicer_BUILD_BRAINSTOOLS "Build application with BRAINSTools module" OFF) 106 | option(Slicer_BUILD_DataStore "Build application with DataStore module" OFF) 107 | option(Slicer_BUILD_CompareVolumes "Build application with ChangeTrackerPy module" OFF) 108 | option(Slicer_BUILD_LandmarkRegistration "Build application with LandmarkRegistration module" OFF) 109 | option(Slicer_BUILD_SurfaceToolbox "Build application with SurfaceToolbox module" OFF) 110 | 111 | # Enable Slicer built-in modules 112 | set(Slicer_CLIMODULES_ENABLED 113 | ResampleDTIVolume # Needed by ResampleScalarVectorDWIVolume 114 | ResampleScalarVectorDWIVolume # Depends on DiffusionApplications, needed by CropVolume 115 | ) 116 | set(Slicer_QTLOADABLEMODULES_ENABLED 117 | ) 118 | set(Slicer_QTSCRIPTEDMODULES_ENABLED 119 | ) 120 | 121 | # Disable Slicer built-in modules 122 | set(Slicer_CLIMODULES_DISABLED 123 | ) 124 | set(Slicer_QTLOADABLEMODULES_DISABLED 125 | SceneViews 126 | SlicerWelcome 127 | ViewControllers 128 | ) 129 | set(Slicer_QTSCRIPTEDMODULES_DISABLED 130 | DataProbe 131 | DMRIInstall 132 | Endoscopy 133 | LabelStatistics 134 | PerformanceTests 135 | SampleData 136 | VectorToScalarVolume 137 | ) 138 | 139 | # Enable/Disable Slicer custom modules: To create a new module, use the SlicerExtensionWizard. 140 | set(Slicer_EXTENSION_SOURCE_DIRS 141 | #${{ '{' }}{{cookiecutter.project_name}}_SOURCE_DIR}/Modules/CLI/MyCLIModule 142 | #${{ '{' }}{{cookiecutter.project_name}}_SOURCE_DIR}/Modules/Loadable/MyLoadableModule 143 | ${{ '{' }}{{cookiecutter.project_name}}_SOURCE_DIR}/Modules/Scripted/Home 144 | ) 145 | 146 | # Add remote extension source directories 147 | 148 | # SlicerCustomAppUtilities 149 | set(extension_name "SlicerCustomAppUtilities") 150 | set(${extension_name}_SOURCE_DIR "${CMAKE_BINARY_DIR}/${extension_name}") 151 | FetchContent_Populate(${extension_name} 152 | SOURCE_DIR ${${extension_name}_SOURCE_DIR} 153 | GIT_REPOSITORY ${EP_GIT_PROTOCOL}://github.com/KitwareMedical/SlicerCustomAppUtilities.git 154 | GIT_TAG {% github_repo_branch_sha "KitwareMedical/SlicerCustomAppUtilities", "main" %} 155 | GIT_PROGRESS 1 156 | QUIET 157 | ) 158 | message(STATUS "Remote - ${extension_name} [OK]") 159 | list(APPEND Slicer_EXTENSION_SOURCE_DIRS ${${extension_name}_SOURCE_DIR}/Modules/Scripted/SlicerCustomAppUtilities) 160 | 161 | # SlicerOpenIGTLink 162 | #set(extension_name "SlicerOpenIGTLink") 163 | #set(${extension_name}_SOURCE_DIR "${CMAKE_BINARY_DIR}/${extension_name}") 164 | #FetchContent_Populate(${extension_name} 165 | # SOURCE_DIR ${${extension_name}_SOURCE_DIR} 166 | # GIT_REPOSITORY ${EP_GIT_PROTOCOL}://github.com/openigtlink/SlicerOpenIGTLink.git 167 | # GIT_TAG 2b92f7b1ffe02403109b671f28424e8770e902a0 168 | # GIT_PROGRESS 1 169 | # QUIET 170 | # ) 171 | #message(STATUS "Remote - ${extension_name} [OK]") 172 | #list(APPEND Slicer_EXTENSION_SOURCE_DIRS ${${extension_name}_SOURCE_DIR}) 173 | 174 | 175 | # Add Slicer sources 176 | add_subdirectory(${slicersources_SOURCE_DIR} ${slicersources_BINARY_DIR}) 177 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to {{cookiecutter.project_name}} 2 | 3 | There are many ways to contribute to {{cookiecutter.project_name}}. 4 | 5 | - Submit a feature request or bug, or add to the discussion on the [{{cookiecutter.project_name}} issue tracker][is] 6 | - Submit a [Pull Request][pr] to improve {{cookiecutter.project_name}}. 7 | 8 | ## The PR Process, and Related Gotchas 9 | 10 | ### How to submit a PR ? 11 | 12 | If you are new to {{cookiecutter.project_name}} development and you don't have push access to the {{cookiecutter.project_name}} 13 | repository, here are the steps: 14 | 15 | 1. [Fork and clone][fk] the repository. 16 | 2. Create a branch. 17 | 3. [Push][push] the branch to your GitHub fork. 18 | 4. Create a [Pull Request][pr]. 19 | 20 | This corresponds to the `Fork & Pull Model` described in the [GitHub documentation](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/getting-started/about-collaborative-development-models) 21 | guides. 22 | 23 | If you have push access to this repository, you could simply push your branch 24 | and create a [Pull Request][pr]. This corresponds to the `Shared Repository Model` 25 | and will facilitate other developers to checkout your topic without having to 26 | [configure a remote](https://help.github.com/articles/configuring-a-remote-for-a-fork/). 27 | It will also simplify the workflow when you are _co-developing_ a branch. 28 | 29 | When submitting a PR, make sure to add a `Cc: @{{cookiecutter.github_project}}/developers` comment to 30 | notify {{cookiecutter.project_name}} developers of your awesome contributions. Based on the 31 | comments posted by the reviewers, you may have to revisit your patches. 32 | 33 | ### How to integrate a PR ? 34 | 35 | Getting your contributions integrated is relatively straightforward, here 36 | is the checklist: 37 | 38 | - All tests pass 39 | - Consensus is reached. This usually means that at least one reviewer added a `LGTM` comment 40 | and a reasonable amount of time passed without anyone objecting. `LGTM` is an 41 | acronym for _Looks Good to Me_. 42 | 43 | Next, there are two scenarios: 44 | 45 | - You do NOT have push access: A {{cookiecutter.project_name}} core developer will integrate your PR. 46 | - You have push access: Simply click on the "Merge pull request" button. 47 | 48 | Then, click on the "Delete branch" button that appears afterward. 49 | 50 | [fk]: http://help.github.com/forking/ 51 | [push]: https://help.github.com/articles/pushing-to-a-remote/ 52 | [pr]: https://github.com/{{cookiecutter.github_organization}}/{{cookiecutter.github_project}}/merge_requests 53 | [is]: https://github.com/{{cookiecutter.github_organization}}/{{cookiecutter.github_project}}/issues 54 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/CTestConfig.cmake: -------------------------------------------------------------------------------- 1 | set(CTEST_PROJECT_NAME "{{ cookiecutter.project_name }}") 2 | set(CTEST_NIGHTLY_START_TIME "3:00:00 UTC") 3 | 4 | set(CTEST_DROP_METHOD "https") 5 | set(CTEST_DROP_SITE "{{ cookiecutter.cdash_drop_site }}") 6 | set(CTEST_DROP_LOCATION "/submit.php?project=${CTEST_PROJECT_NAME}") 7 | set(CTEST_DROP_SITE_CDASH TRUE) 8 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/LICENSE: -------------------------------------------------------------------------------- 1 | {{cookiecutter.project_name}} 2 | ------------ 3 | 4 | Copyright (c) {{cookiecutter.org_name}} 5 | 6 | 7 | Slicer Custom App Template 8 | -------------------------- 9 | 10 | Copyright (c) Kitware, Inc. 11 | 12 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at 13 | 14 | http://www.apache.org/licenses/LICENSE-2.0.txt 15 | 16 | Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. 17 | 18 | 19 | 3D Slicer 20 | --------- 21 | 22 | Licensed under the 3D Slicer Contribution and Software License Agreement. The 3D Slicer license is a BSD style license, with extensions to cover contributions and other issues specific to 3D Slicer. You may obtain a copy of the License at 23 | 24 | https://www.slicer.org/LICENSE 25 | 26 | The Software is provided "AS IS" and neither Brigham nor any contributor to the software (each a "Contributor") shall have any obligation to provide maintenance, support, updates, enhancements or modifications thereto. BRIGHAM AND ALL CONTRIBUTORS SPECIFICALLY DISCLAIM ALL EXPRESS AND IMPLIED WARRANTIES OF ANY KIND INCLUDING, BUT NOT LIMITED TO, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL BRIGHAM OR ANY CONTRIBUTOR BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY ARISING IN ANY WAY RELATED TO THE SOFTWARE, EVEN IF BRIGHAM OR ANY CONTRIBUTOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. TO THE MAXIMUM EXTENT NOT PROHIBITED BY LAW OR REGULATION, YOU FURTHER ASSUME ALL LIABILITY FOR YOUR USE, REPRODUCTION, MAKING OF DERIVATIVE WORKS, DISPLAY, LICENSE OR DISTRIBUTION OF THE SOFTWARE AND AGREE TO INDEMNIFY AND HOLD HARMLESS BRIGHAM AND ALL CONTRIBUTORS FROM AND AGAINST ANY AND ALL CLAIMS, SUITS, ACTIONS, DEMANDS AND JUDGMENTS ARISING THEREFROM. 27 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/Modules/Scripted/Home/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | #----------------------------------------------------------------------------- 2 | set(MODULE_NAME Home) 3 | 4 | #----------------------------------------------------------------------------- 5 | set(MODULE_PYTHON_SCRIPTS 6 | ${MODULE_NAME}.py 7 | ) 8 | 9 | set(MODULE_PYTHON_RESOURCES 10 | Resources/Icons/${MODULE_NAME}.png 11 | Resources/Icons/Gears.png 12 | Resources/UI/${MODULE_NAME}.ui 13 | Resources/UI/Settings.ui 14 | Resources/Home.qss 15 | ) 16 | 17 | slicerFunctionAddPythonQtResources(MODULE_PYTHON_QRC_RESOURCES 18 | Resources/${MODULE_NAME}.qrc 19 | ) 20 | 21 | #----------------------------------------------------------------------------- 22 | slicerMacroBuildScriptedModule( 23 | NAME ${MODULE_NAME} 24 | SCRIPTS ${MODULE_PYTHON_SCRIPTS} ${MODULE_PYTHON_QRC_RESOURCES} 25 | RESOURCES ${MODULE_PYTHON_RESOURCES} 26 | ) 27 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/Modules/Scripted/Home/Home.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | import qt 4 | import slicer 5 | import SlicerCustomAppUtilities 6 | from slicer.ScriptedLoadableModule import ( 7 | ScriptedLoadableModule, 8 | ScriptedLoadableModuleLogic, 9 | ScriptedLoadableModuleWidget, 10 | ) 11 | from slicer.util import VTKObservationMixin 12 | 13 | # Import to ensure the files are available through the Qt resource system 14 | from Resources import HomeResources # noqa: F401 15 | 16 | 17 | class Home(ScriptedLoadableModule): 18 | """The home module allows to orchestrate and style the overall application workflow. 19 | 20 | It is a "special" module in the sense that its role is to customize the application and 21 | coordinate a workflow between other "regular" modules. 22 | 23 | Associated widget and logic are not intended to be initialized multiple times. 24 | """ 25 | 26 | def __init__(self, parent: Optional[qt.QWidget]): 27 | ScriptedLoadableModule.__init__(self, parent) 28 | self.parent.title = "Home" 29 | self.parent.categories = [""] 30 | self.parent.dependencies = [] 31 | self.parent.contributors = ["Sam Horvath (Kitware Inc.)", "Jean-Christophe Fillion-Robin (Kitware Inc.)"] 32 | self.parent.helpText = """This module orchestrates and styles the overall application workflow.""" 33 | self.parent.helpText += self.getDefaultModuleDocumentationLink() 34 | self.parent.acknowledgementText = """...""" # replace with organization, grant and thanks. 35 | 36 | 37 | class HomeWidget(ScriptedLoadableModuleWidget, VTKObservationMixin): 38 | """Uses ScriptedLoadableModuleWidget base class, available at: 39 | https://github.com/Slicer/Slicer/blob/main/Base/Python/slicer/ScriptedLoadableModule.py 40 | """ 41 | 42 | @property 43 | def toolbarNames(self) -> list[str]: 44 | return [str(k) for k in self._toolbars] 45 | 46 | _toolbars: dict[str, qt.QToolBar] = {} 47 | 48 | def __init__(self, parent: Optional[qt.QWidget]): 49 | """Called when the application opens the module the first time and the widget is initialized.""" 50 | ScriptedLoadableModuleWidget.__init__(self, parent) 51 | VTKObservationMixin.__init__(self) 52 | 53 | def setup(self): 54 | """Called when the application opens the module the first time and the widget is initialized.""" 55 | ScriptedLoadableModuleWidget.setup(self) 56 | 57 | # Load widget from .ui file (created by Qt Designer) 58 | self.uiWidget = slicer.util.loadUI(self.resourcePath("UI/Home.ui")) 59 | self.layout.addWidget(self.uiWidget) 60 | self.ui = slicer.util.childWidgetVariables(self.uiWidget) 61 | 62 | # Get references to relevant underlying modules 63 | # NA 64 | 65 | # Create logic class 66 | self.logic = HomeLogic() 67 | 68 | # Dark palette does not propagate on its own 69 | # See https://github.com/KitwareMedical/SlicerCustomAppTemplate/issues/72 70 | self.uiWidget.setPalette(slicer.util.mainWindow().style().standardPalette()) 71 | 72 | # Remove unneeded UI elements 73 | self.modifyWindowUI() 74 | self.setCustomUIVisible(True) 75 | 76 | # Apply style 77 | self.applyApplicationStyle() 78 | 79 | def cleanup(self): 80 | """Called when the application closes and the module widget is destroyed.""" 81 | pass 82 | 83 | def setSlicerUIVisible(self, visible: bool): 84 | exemptToolbars = [ 85 | "MainToolBar", 86 | "ViewToolBar", 87 | *self.toolbarNames, 88 | ] 89 | slicer.util.setDataProbeVisible(visible) 90 | slicer.util.setMenuBarsVisible(visible, ignore=exemptToolbars) 91 | slicer.util.setModuleHelpSectionVisible(visible) 92 | slicer.util.setModulePanelTitleVisible(visible) 93 | slicer.util.setPythonConsoleVisible(visible) 94 | slicer.util.setApplicationLogoVisible(visible) 95 | keepToolbars = [slicer.util.findChild(slicer.util.mainWindow(), toolbarName) for toolbarName in exemptToolbars] 96 | slicer.util.setToolbarsVisible(visible, keepToolbars) 97 | 98 | def modifyWindowUI(self): 99 | """Customize the entire user interface to resemble the custom application""" 100 | # Custom toolbars 101 | self.initializeSettingsToolBar() 102 | 103 | def insertToolBar(self, beforeToolBarName: str, name: str, title: Optional[str] = None) -> qt.QToolBar: 104 | """Helper method to insert a new toolbar between existing ones""" 105 | beforeToolBar = slicer.util.findChild(slicer.util.mainWindow(), beforeToolBarName) 106 | 107 | if title is None: 108 | title = name 109 | 110 | toolBar = qt.QToolBar(title) 111 | toolBar.name = name 112 | slicer.util.mainWindow().insertToolBar(beforeToolBar, toolBar) 113 | 114 | self._toolbars[name] = toolBar 115 | 116 | return toolBar 117 | 118 | def initializeSettingsToolBar(self): 119 | """Create toolbar and dialog for app settings""" 120 | settingsToolBar = self.insertToolBar("MainToolBar", "SettingsToolBar", title="Settings") 121 | 122 | gearIcon = qt.QIcon(self.resourcePath("Icons/Gears.png")) 123 | self.settingsAction = settingsToolBar.addAction(gearIcon, "") 124 | 125 | # Settings dialog 126 | self.settingsDialog = slicer.util.loadUI(self.resourcePath("UI/Settings.ui")) 127 | self.settingsUI = slicer.util.childWidgetVariables(self.settingsDialog) 128 | self.settingsUI.CustomUICheckBox.toggled.connect(self.setCustomUIVisible) 129 | self.settingsUI.CustomStyleCheckBox.toggled.connect(self.toggleStyle) 130 | self.settingsAction.triggered.connect(self.raiseSettings) 131 | 132 | def toggleStyle(self, visible: bool): 133 | if visible: 134 | self.applyApplicationStyle() 135 | else: 136 | slicer.app.styleSheet = "" 137 | 138 | def raiseSettings(self, _): 139 | self.settingsDialog.exec() 140 | 141 | def setCustomUIVisible(self, visible: bool): 142 | self.setSlicerUIVisible(not visible) 143 | 144 | def applyApplicationStyle(self): 145 | SlicerCustomAppUtilities.applyStyle([slicer.app], self.resourcePath("Home.qss")) 146 | self.styleThreeDWidget() 147 | self.styleSliceWidgets() 148 | 149 | def styleThreeDWidget(self): 150 | viewNode = slicer.app.layoutManager().threeDWidget(0).mrmlViewNode() # noqa: F841 151 | # viewNode.SetBackgroundColor(0.0, 0.0, 0.0) 152 | # viewNode.SetBackgroundColor2(0.0, 0.0, 0.0) 153 | # viewNode.SetBoxVisible(False) 154 | # viewNode.SetAxisLabelsVisible(False) 155 | # viewNode.SetOrientationMarkerType(slicer.vtkMRMLViewNode.OrientationMarkerTypeAxes) 156 | 157 | def styleSliceWidgets(self): 158 | for name in slicer.app.layoutManager().sliceViewNames(): 159 | sliceWidget = slicer.app.layoutManager().sliceWidget(name) 160 | self.styleSliceWidget(sliceWidget) 161 | 162 | def styleSliceWidget(self, sliceWidget: slicer.qMRMLSliceWidget): 163 | controller = sliceWidget.sliceController() # noqa: F841 164 | # controller.sliceViewLabel = "" 165 | # slicer.util.findChild(sliceWidget, "PinButton").visible = False 166 | # slicer.util.findChild(sliceWidget, "ViewLabel").visible = False 167 | # slicer.util.findChild(sliceWidget, "FitToWindowToolButton").visible = False 168 | # slicer.util.findChild(sliceWidget, "SliceOffsetSlider").spinBoxVisible = False 169 | 170 | 171 | class HomeLogic(ScriptedLoadableModuleLogic): 172 | """ 173 | Implements underlying logic for the Home module. 174 | """ 175 | 176 | pass 177 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/Modules/Scripted/Home/Resources/Home.qrc: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | ./Icons/Gears.png 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/Modules/Scripted/Home/Resources/Home.qss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KitwareMedical/SlicerCustomAppTemplate/c2abc0ce3eeea2b5d59239cdf38da4a64bac0b90/{{cookiecutter.project_name}}/Modules/Scripted/Home/Resources/Home.qss -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/Modules/Scripted/Home/Resources/Icons/Gears.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KitwareMedical/SlicerCustomAppTemplate/c2abc0ce3eeea2b5d59239cdf38da4a64bac0b90/{{cookiecutter.project_name}}/Modules/Scripted/Home/Resources/Icons/Gears.png -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/Modules/Scripted/Home/Resources/Icons/Home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KitwareMedical/SlicerCustomAppTemplate/c2abc0ce3eeea2b5d59239cdf38da4a64bac0b90/{{cookiecutter.project_name}}/Modules/Scripted/Home/Resources/Icons/Home.png -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/Modules/Scripted/Home/Resources/UI/Home.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Home 4 | 5 | 6 | 7 | 0 8 | 0 9 | 300 10 | 434 11 | 12 | 13 | 14 | 15 | 16 | 17 | qSlicerWidget 18 | QWidget 19 |
qSlicerWidget.h
20 | 1 21 |
22 |
23 | 24 | 25 |
26 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/Modules/Scripted/Home/Resources/UI/Settings.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Dialog 4 | 5 | 6 | 7 | 0 8 | 0 9 | 222 10 | 73 11 | 12 | 13 | 14 | Dialog 15 | 16 | 17 | 18 | 19 | 20 | Qt::Horizontal 21 | 22 | 23 | QDialogButtonBox::Close 24 | 25 | 26 | 27 | 28 | 29 | 30 | Use custom QSS 31 | 32 | 33 | true 34 | 35 | 36 | 37 | 38 | 39 | 40 | Hide Slicer Interface 41 | 42 | 43 | true 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | buttonBox 53 | accepted() 54 | Dialog 55 | accept() 56 | 57 | 58 | 248 59 | 254 60 | 61 | 62 | 157 63 | 274 64 | 65 | 66 | 67 | 68 | buttonBox 69 | rejected() 70 | Dialog 71 | reject() 72 | 73 | 74 | 316 75 | 260 76 | 77 | 78 | 286 79 | 274 80 | 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/README.md: -------------------------------------------------------------------------------- 1 | # {{cookiecutter.project_name}} by {{cookiecutter.org_name}} 2 | 3 | {{cookiecutter.app_description_summary}} 4 | 5 | _This project is in active development and may change from version to version without notice,_ 6 | 7 | ## Table of contents 8 | 9 | - [Features](#features) 10 | - [Development](#development) 11 | 12 | ## Features 13 | 14 | _List key features_ 15 | 16 | ## Development 17 | 18 | - [Contributing](CONTRIBUTING.md) 19 | - [Building](BUILD.md) 20 | 21 | ![{{cookiecutter.project_name}} by {{cookiecutter.org_name}}](Applications/{{cookiecutter.app_name}}App/Resources/Images/LogoFull.png?raw=true) 22 | --------------------------------------------------------------------------------