├── sciplot ├── tests │ ├── __init__.py │ ├── test_utils.py │ └── test_generic.py ├── ui │ ├── models │ │ ├── __init__.py │ │ ├── abstract.py │ │ ├── images.py │ │ ├── fillbetween.py │ │ ├── bars.py │ │ └── lines.py │ ├── qt5-creator │ │ ├── __init__.py │ │ ├── Old │ │ │ ├── ui_Plotter_dialog.ui │ │ │ └── ui_Plotter_main.ui │ │ └── ui_Plotter.ui │ ├── __init__.py │ ├── dialogs.py │ ├── widget_mpl.py │ └── qt_Plotter.py ├── utils │ ├── __init__.py │ ├── general.py │ └── mplstyle.py ├── data │ ├── __init__.py │ ├── special.py │ ├── generic.py │ ├── images.py │ ├── lines.py │ ├── bars.py │ └── polycollections.py ├── __main__.py ├── __init__.py └── sciplotUI_addon.py ├── Screenshot.png ├── Screenshot.xcf ├── Screenshot2.png ├── Screenshot3.png ├── docs ├── source │ ├── modules.rst │ ├── sciplot.ui.qt5-creator.rst │ ├── index.rst │ ├── sciplot.utils.rst │ ├── sciplot.rst │ ├── sciplot.ui.rst │ ├── sciplot.ui.models.rst │ ├── sciplot.data.rst │ └── conf.py ├── sphinx-apidoc-run.sh ├── make.bat └── Makefile ├── MANIFEST.in ├── setup.cfg ├── TODO.md ├── .travis └── build_pyqt5.sh ├── appveyor.yml ├── .gitignore ├── .travis.yml ├── LICENSE.md ├── setup.py ├── CHANGELOG.rst └── README.rst /sciplot/tests/__init__.py: -------------------------------------------------------------------------------- 1 | """ Unit testing files """ -------------------------------------------------------------------------------- /sciplot/ui/models/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- -------------------------------------------------------------------------------- /Screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CCampJr/SciPlot-PyQt/HEAD/Screenshot.png -------------------------------------------------------------------------------- /Screenshot.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CCampJr/SciPlot-PyQt/HEAD/Screenshot.xcf -------------------------------------------------------------------------------- /Screenshot2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CCampJr/SciPlot-PyQt/HEAD/Screenshot2.png -------------------------------------------------------------------------------- /Screenshot3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CCampJr/SciPlot-PyQt/HEAD/Screenshot3.png -------------------------------------------------------------------------------- /sciplot/utils/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Utility modules 4 | """ 5 | -------------------------------------------------------------------------------- /sciplot/ui/qt5-creator/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Qt-Creator/Designer files 4 | """ -------------------------------------------------------------------------------- /sciplot/ui/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | UI-related items: windows, widgets, and models 4 | """ 5 | -------------------------------------------------------------------------------- /docs/source/modules.rst: -------------------------------------------------------------------------------- 1 | sciplot_pyqt5 2 | ============= 3 | 4 | .. toctree:: 5 | :maxdepth: 4 6 | 7 | sciplot 8 | -------------------------------------------------------------------------------- /docs/sphinx-apidoc-run.sh: -------------------------------------------------------------------------------- 1 | ./make.bat clean 2 | sphinx-apidoc.exe -f -o ./source/ .. ../setup.py 3 | ./make.bat html 4 | -------------------------------------------------------------------------------- /sciplot/data/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Contains modules that describe the plot data for various format of plots, graphs, etc. 4 | """ -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include *.rst 2 | recursive-include docs * 3 | include LICENSE.md 4 | include README.rst 5 | include CHANGELOG.rst 6 | include *.png 7 | recursive-include docs * 8 | prune docs/build 9 | include sciplot/ui/qt5-creator/*.ui -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | description-file = README.rst 3 | 4 | [build_sphinx] 5 | source-dir = docs/source 6 | build-dir = docs/build 7 | all_files = 1 8 | 9 | [aliases] 10 | test=pytest 11 | 12 | [tool:pytest] 13 | addopts = -v -------------------------------------------------------------------------------- /sciplot/utils/general.py: -------------------------------------------------------------------------------- 1 | """ Just some general utilities """ 2 | 3 | def round_list(input, ndigits=3): 4 | """ Takes in a list of numbers and rounds them to a particular number of digits """ 5 | return [round(i, ndigits) for i in input] -------------------------------------------------------------------------------- /docs/source/sciplot.ui.qt5-creator.rst: -------------------------------------------------------------------------------- 1 | sciplot.ui.qt5\-creator package 2 | =============================== 3 | 4 | Module contents 5 | --------------- 6 | 7 | .. automodule:: sciplot.ui.qt5-creator 8 | :members: 9 | :undoc-members: 10 | :show-inheritance: 11 | -------------------------------------------------------------------------------- /sciplot/tests/test_utils.py: -------------------------------------------------------------------------------- 1 | """ 2 | Test utility functions 3 | """ 4 | from sciplot.utils.general import round_list 5 | 6 | def test_round_list(): 7 | list_under_test = [0.111, 0.222, 0.555] 8 | 9 | list_round_3 = [0.111, 0.222, 0.555] 10 | list_round_2 = [0.11, 0.22, 0.56] 11 | 12 | assert round_list(list_under_test, 3) == list_round_3 13 | assert round_list(list_under_test, 2) == list_round_2 -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | # Plotting functionalities 2 | * Image high and low compression parameters 3 | * Add ordering (bottom-to-top) 4 | 5 | # Plotting type adds 6 | * Scatter plots 7 | * Whisker plots 8 | 9 | # Style functionality 10 | * Load mpl-style sheets 11 | * Custom style sheets 12 | * Journal-specific styles/sizes/etc 13 | * Manually adjust figsize, dpi, font, etc 14 | 15 | # Other 16 | * Documentation (it's just API currently) -------------------------------------------------------------------------------- /docs/source/index.rst: -------------------------------------------------------------------------------- 1 | .. SciPlot-PyQt documentation master file, created by 2 | sphinx-quickstart on Wed Jul 20 23:57:50 2016. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to SciPlot-PyQt's documentation! 7 | ======================================== 8 | 9 | Contents: 10 | 11 | .. toctree:: 12 | :maxdepth: 2 13 | 14 | 15 | 16 | Indices and tables 17 | ================== 18 | 19 | * :ref:`genindex` 20 | * :ref:`modindex` 21 | * :ref:`search` 22 | 23 | -------------------------------------------------------------------------------- /docs/source/sciplot.utils.rst: -------------------------------------------------------------------------------- 1 | sciplot.utils package 2 | ===================== 3 | 4 | Submodules 5 | ---------- 6 | 7 | sciplot.utils.general module 8 | ---------------------------- 9 | 10 | .. automodule:: sciplot.utils.general 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | sciplot.utils.mplstyle module 16 | ----------------------------- 17 | 18 | .. automodule:: sciplot.utils.mplstyle 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | 23 | 24 | Module contents 25 | --------------- 26 | 27 | .. automodule:: sciplot.utils 28 | :members: 29 | :undoc-members: 30 | :show-inheritance: 31 | -------------------------------------------------------------------------------- /sciplot/__main__.py: -------------------------------------------------------------------------------- 1 | """ 2 | 3 | """ 4 | 5 | import sys as _sys 6 | import os as _os 7 | from PyQt5.QtWidgets import (QApplication as _QApplication) 8 | # from crikit import CRIkitUI 9 | from sciplot import sciplotUI 10 | 11 | def main(args=None): 12 | """ 13 | The main routine. 14 | """ 15 | 16 | if args is None: 17 | args = _sys.argv[1:] 18 | 19 | print('Starting up sciplot...') 20 | 21 | app = _QApplication(_sys.argv) 22 | app.setStyle('Cleanlooks') 23 | win = sciplotUI.SciPlotUI() ### EDIT ### 24 | 25 | win.showMaximized() 26 | 27 | app.exec_() 28 | 29 | if __name__ == '__main__': 30 | _sys.exit(main()) -------------------------------------------------------------------------------- /sciplot/tests/test_generic.py: -------------------------------------------------------------------------------- 1 | """ 2 | Just a basic test 3 | """ 4 | import sys 5 | import numpy as np 6 | 7 | from PyQt5.QtWidgets import QApplication 8 | 9 | from sciplot.sciplotUI import SciPlotUI 10 | 11 | def test_pass(): 12 | pass 13 | # app = QApplication(sys.argv) 14 | # winPlotter = SciPlotUI(limit_to=['lines','bars', 'fill betweens', 15 | # 'images']) 16 | 17 | # x = np.arange(100) 18 | # y = x**2 19 | # winPlotter.plot(x, y, x_label='X', label='Plot') 20 | # winPlotter.fill_between(x, y-1000, y+1000, label='Fill Between') 21 | # winPlotter.imshow(np.random.randn(100,100), label='Imshow', cbar=True) 22 | # winPlotter.bar(x[::10],y[::10],label='Bar') 23 | # app.exec_() -------------------------------------------------------------------------------- /docs/source/sciplot.rst: -------------------------------------------------------------------------------- 1 | sciplot package 2 | =============== 3 | 4 | Subpackages 5 | ----------- 6 | 7 | .. toctree:: 8 | 9 | sciplot.data 10 | sciplot.ui 11 | sciplot.utils 12 | 13 | Submodules 14 | ---------- 15 | 16 | sciplot.sciplotUI module 17 | ------------------------ 18 | 19 | .. automodule:: sciplot.sciplotUI 20 | :members: 21 | :undoc-members: 22 | :show-inheritance: 23 | 24 | sciplot.sciplotUI\_addon module 25 | ------------------------------- 26 | 27 | .. automodule:: sciplot.sciplotUI_addon 28 | :members: 29 | :undoc-members: 30 | :show-inheritance: 31 | 32 | 33 | Module contents 34 | --------------- 35 | 36 | .. automodule:: sciplot 37 | :members: 38 | :undoc-members: 39 | :show-inheritance: 40 | -------------------------------------------------------------------------------- /docs/source/sciplot.ui.rst: -------------------------------------------------------------------------------- 1 | sciplot.ui package 2 | ================== 3 | 4 | Subpackages 5 | ----------- 6 | 7 | .. toctree:: 8 | 9 | sciplot.ui.models 10 | sciplot.ui.qt5-creator 11 | 12 | Submodules 13 | ---------- 14 | 15 | sciplot.ui.dialogs module 16 | ------------------------- 17 | 18 | .. automodule:: sciplot.ui.dialogs 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | 23 | sciplot.ui.qt\_Plotter module 24 | ----------------------------- 25 | 26 | .. automodule:: sciplot.ui.qt_Plotter 27 | :members: 28 | :undoc-members: 29 | :show-inheritance: 30 | 31 | sciplot.ui.widget\_mpl module 32 | ----------------------------- 33 | 34 | .. automodule:: sciplot.ui.widget_mpl 35 | :members: 36 | :undoc-members: 37 | :show-inheritance: 38 | 39 | 40 | Module contents 41 | --------------- 42 | 43 | .. automodule:: sciplot.ui 44 | :members: 45 | :undoc-members: 46 | :show-inheritance: 47 | -------------------------------------------------------------------------------- /.travis/build_pyqt5.sh: -------------------------------------------------------------------------------- 1 | PYQT=$TRAVIS_BUILD_DIR/pyqt 2 | 3 | SIP_VERSION=4.19.6 4 | PYQT_VERSION=5.8.1 5 | QT_BASE=58 6 | USE_CMAKE=true 7 | 8 | sudo add-apt-repository -y ppa:beineri/opt-qt591-trusty 9 | sudo apt-get update 10 | sudo apt-get install -y qt59base 11 | source /opt/qt59/bin/qt59-env.sh 12 | 13 | mkdir -p $PYQT 14 | cd $PYQT 15 | 16 | wget -O sip.tar.gz http://sourceforge.net/projects/pyqt/files/sip/sip-$SIP_VERSION/sip-$SIP_VERSION.tar.gz 17 | mkdir -p sip 18 | tar xzf sip.tar.gz -C sip --strip-component=1 19 | 20 | wget -O PyQt.tar.gz http://sourceforge.net/projects/pyqt/files/PyQt5/PyQt-$PYQT_VERSION/PyQt5_gpl-$PYQT_VERSION.tar.gz 21 | mkdir -p PyQt 22 | tar xzf PyQt.tar.gz -C PyQt --strip-components=1 23 | 24 | cd $PYQT/sip 25 | #python configure.py -e $PYQT/include 26 | python configure.py 27 | make 28 | sudo make install 29 | 30 | cd $PYQT/PyQt 31 | python configure.py --confirm-license --no-designer-plugin -e QtCore -e QtGui -e QtWidgets 32 | make 33 | sudo make install -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | # https://github.com/astropy/astropy/blob/master/appveyor.yml 2 | 3 | branches: 4 | only: 5 | - master 6 | - dev 7 | - version 8 | # except: 9 | 10 | 11 | environment: 12 | 13 | global: 14 | PYTHON: "C:\\conda" 15 | MINICONDA_VERSION: "latest" 16 | PYTHON_ARCH: "64" 17 | CONDA_DEPENDENCIES: "pyqt matplotlib pytest" 18 | # PIP_DEPENDENCIES: "objgraph" 19 | 20 | matrix: 21 | - PYTHON_VERSION: "3.6" 22 | NUMPY_VERSION: "stable" 23 | 24 | matrix: 25 | fast_finish: true 26 | 27 | platform: 28 | -x64 29 | 30 | # os: Visual Studio 2015 Update 2 31 | 32 | install: 33 | - "git clone git://github.com/astropy/ci-helpers.git" 34 | - "powershell ci-helpers/appveyor/install-miniconda.ps1" 35 | - "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%" 36 | - "activate test" 37 | 38 | # Not a .NET project, we build Astropy in the install step instead 39 | build: false 40 | 41 | test_script: 42 | - "%CMD_IN_ENV% python -m pytest" 43 | -------------------------------------------------------------------------------- /sciplot/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | SciPlot-PyQt: Publication-ready scientific plotting for Python 4 | ============================================================== 5 | 6 | SciPlot-PyQt (aka SciPlot) is a user-interface/matplotlib wrapper built with 7 | PyQt5 that allows interactive plotting through an embedded matplotlib canvas. 8 | It enables fast and easy publication-ready plots and images: 9 | 10 | * Interactive plotting 11 | * Theme and style editing (TODO) 12 | * Figure saving and opening for later editing (TODO) 13 | 14 | Supported Plot Types 15 | --------------------- 16 | Line plots : plot 17 | 18 | Bar plots : bar, hist 19 | 20 | Polycollections : fill_between 21 | 22 | Images : imshow 23 | """ 24 | 25 | import sys as _sys 26 | import os as _os 27 | import pkg_resources as _pkg_resources 28 | 29 | from . import sciplotUI 30 | 31 | _sys.path.append(_os.path.abspath('../')) 32 | 33 | try: 34 | __version__ = _pkg_resources.require("sciplot-pyqt")[0].version 35 | except: 36 | pass 37 | 38 | # __version__ = '0.2.2' 39 | 40 | # __all__ = ['SciPlotUI'] 41 | 42 | # from .sciplotUI import SciPlotUI as main 43 | -------------------------------------------------------------------------------- /sciplot/data/special.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Somewhat specialized data containers. 4 | 5 | Created on Thu Jul 7 15:21:29 2016 6 | 7 | @author: chc 8 | """ 9 | 10 | from sciplot.data.polycollections import PolyCollectionStyle 11 | from sciplot.data.generic import Data as _Data 12 | 13 | class DataFillBetween(_Data, PolyCollectionStyle): 14 | def __init__(self): 15 | self._setupData() 16 | self._setupPolyCollectionStyle() 17 | del self.y 18 | self.y_low = None 19 | self.y_high = None 20 | 21 | @property 22 | def model_style(self): 23 | out = {} 24 | out.update(self.style_dict) 25 | out['label'] = self.label 26 | out['id'] = self.id 27 | out['meta'] = self.meta 28 | return out 29 | 30 | @model_style.setter 31 | def model_style(self, value): 32 | self.label = value['label'] 33 | self.meta = value['meta'] 34 | self.style_dict['facecolor'] = value['facecolor'] 35 | self.style_dict['alpha'] = value['alpha'] 36 | self.style_dict['edgecolor'] = value['edgecolor'] 37 | self.style_dict['linewidth'] = value['linewidth'] 38 | -------------------------------------------------------------------------------- /docs/source/sciplot.ui.models.rst: -------------------------------------------------------------------------------- 1 | sciplot.ui.models package 2 | ========================= 3 | 4 | Submodules 5 | ---------- 6 | 7 | sciplot.ui.models.abstract module 8 | --------------------------------- 9 | 10 | .. automodule:: sciplot.ui.models.abstract 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | sciplot.ui.models.bars module 16 | ----------------------------- 17 | 18 | .. automodule:: sciplot.ui.models.bars 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | 23 | sciplot.ui.models.fillbetween module 24 | ------------------------------------ 25 | 26 | .. automodule:: sciplot.ui.models.fillbetween 27 | :members: 28 | :undoc-members: 29 | :show-inheritance: 30 | 31 | sciplot.ui.models.images module 32 | ------------------------------- 33 | 34 | .. automodule:: sciplot.ui.models.images 35 | :members: 36 | :undoc-members: 37 | :show-inheritance: 38 | 39 | sciplot.ui.models.lines module 40 | ------------------------------ 41 | 42 | .. automodule:: sciplot.ui.models.lines 43 | :members: 44 | :undoc-members: 45 | :show-inheritance: 46 | 47 | 48 | Module contents 49 | --------------- 50 | 51 | .. automodule:: sciplot.ui.models 52 | :members: 53 | :undoc-members: 54 | :show-inheritance: 55 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | 4 | *.py[cod] 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Nose-related 10 | .noseids 11 | 12 | #VS Code Files 13 | .idea/ 14 | 15 | # Distribution / packaging 16 | .Python 17 | env/ 18 | #build/ 19 | develop-eggs/ 20 | dist/ 21 | downloads/ 22 | eggs/ 23 | .eggs/ 24 | lib/ 25 | lib64/ 26 | parts/ 27 | sdist/ 28 | var/ 29 | .vscode/ 30 | *.egg-info/ 31 | .installed.cfg 32 | *.egg 33 | .ipynb_checkpoints/ 34 | 35 | # PyInstaller 36 | # Usually these files are written by a python script from a template 37 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 38 | *.manifest 39 | *.spec 40 | 41 | # Installer logs 42 | pip-log.txt 43 | pip-delete-this-directory.txt 44 | 45 | # Unit test / coverage reports 46 | htmlcov/ 47 | .tox/ 48 | .coverage 49 | .coverage.* 50 | .cache 51 | nosetests.xml 52 | coverage.xml 53 | .pytest_cache* 54 | 55 | # Translations 56 | *.mo 57 | *.pot 58 | 59 | # Django stuff: 60 | *.log 61 | 62 | # Sphinx documentation 63 | # docs/_build/ 64 | 65 | # PyBuilder 66 | target/ 67 | 68 | # Scrap 69 | Scrap/ 70 | *Scrap* 71 | 72 | # Backups 73 | *~ 74 | 75 | # H5 Files 76 | *.h5 77 | *.hdf5 78 | 79 | # Fonts 80 | *.ttf 81 | 82 | # Pickle file 83 | *.pickle 84 | *.pypirc 85 | 86 | # Sphinx documentation 87 | docs/build 88 | # docs/_static/ 89 | -------------------------------------------------------------------------------- /docs/source/sciplot.data.rst: -------------------------------------------------------------------------------- 1 | sciplot.data package 2 | ==================== 3 | 4 | Submodules 5 | ---------- 6 | 7 | sciplot.data.bars module 8 | ------------------------ 9 | 10 | .. automodule:: sciplot.data.bars 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | sciplot.data.generic module 16 | --------------------------- 17 | 18 | .. automodule:: sciplot.data.generic 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | 23 | sciplot.data.images module 24 | -------------------------- 25 | 26 | .. automodule:: sciplot.data.images 27 | :members: 28 | :undoc-members: 29 | :show-inheritance: 30 | 31 | sciplot.data.lines module 32 | ------------------------- 33 | 34 | .. automodule:: sciplot.data.lines 35 | :members: 36 | :undoc-members: 37 | :show-inheritance: 38 | 39 | sciplot.data.polycollections module 40 | ----------------------------------- 41 | 42 | .. automodule:: sciplot.data.polycollections 43 | :members: 44 | :undoc-members: 45 | :show-inheritance: 46 | 47 | sciplot.data.special module 48 | --------------------------- 49 | 50 | .. automodule:: sciplot.data.special 51 | :members: 52 | :undoc-members: 53 | :show-inheritance: 54 | 55 | 56 | Module contents 57 | --------------- 58 | 59 | .. automodule:: sciplot.data 60 | :members: 61 | :undoc-members: 62 | :show-inheritance: 63 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | matrix: 3 | include: 4 | - os: linux 5 | dist: xenial 6 | services: 7 | - xvfb 8 | sudo: false 9 | python: '3.6' 10 | - os: linux 11 | dist: xenial 12 | services: 13 | - xvfb 14 | sudo: false 15 | python: 3.7 16 | cache: 17 | apt: true 18 | pip: true 19 | ccache: true 20 | directories: 21 | - $TRAVIS_BUILD_DIR/pyqt 22 | before_install: 23 | - if [[ "$TRAVIS_PYTHON_VERSION" != "3.4" && "$TRAVIS_PYTHON_VERSION" != "3.5" ]]; 24 | then pip install pyqt5; else source $TRAVIS_BUILD_DIR/.travis/build_pyqt5.sh; fi 25 | - pip install matplotlib 26 | # - pip install sphinx 27 | # - pip install numpydoc 28 | # - pip install sphinx_rtd_theme 29 | install: 30 | - cd $TRAVIS_BUILD_DIR 31 | # - if [[ "$TRAVIS_PYTHON_VERSION" != "3.4" && "$TRAVIS_PYTHON_VERSION" != "3.5" ]]; 32 | # then python setup.py egg_info; 33 | # python setup.py dist_info; 34 | # python setup.py build_sphinx; 35 | # python setup.py sdist; 36 | # fi 37 | - if [[ "$TRAVIS_PYTHON_VERSION" != "3.4" && "$TRAVIS_PYTHON_VERSION" != "3.5" ]]; 38 | then python setup.py egg_info; 39 | python setup.py dist_info; 40 | python setup.py sdist; 41 | fi 42 | - python setup.py install 43 | - pwd 44 | - ls 45 | - cd sciplot 46 | # - python setup.py sdist 47 | # - cd $TRAVIS_BUILD_DIR/docs/ 48 | # - sphinx-apidoc -o ./source ../sciplot 49 | # - make html 50 | # - pip uninstall -y sciplot-pyqt 51 | # - cd $TRAVIS_BUILD_DIR 52 | # - python setup.py install 53 | script: 54 | - pytest 55 | 56 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | This software was developed by employees of the National Institute of Standards 2 | and Technology (NIST), an agency of the Federal Government. Pursuant to 3 | [title 17 United States Code Section 105](http://www.copyright.gov/title17/92chap1.html#105), 4 | works of NIST employees are not subject to copyright protection in the United States and are 5 | considered to be in the public domain. Permission to freely use, copy, modify, 6 | and distribute this software and its documentation without fee is hereby granted, 7 | provided that this notice and disclaimer of warranty appears in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER 10 | EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY 11 | THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF 12 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM INFRINGEMENT, 13 | AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE SOFTWARE, OR ANY 14 | WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE. IN NO EVENT SHALL NIST BE LIABLE 15 | FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT, INDIRECT, SPECIAL OR 16 | CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM, OR IN ANY WAY CONNECTED 17 | WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY, CONTRACT, TORT, OR 18 | OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR PROPERTY OR 19 | OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT OF THE 20 | RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER. -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Thu Jul 21 01:11:04 2016 4 | 5 | @author: chc 6 | """ 7 | 8 | from setuptools import setup, find_packages 9 | 10 | with open('README.rst', encoding='utf-8') as f: 11 | long_description = f.read() 12 | 13 | setup(name='sciplot-pyqt', 14 | version = '0.2.3', 15 | description = 'A small matplotlib wrapper/UI for creating \ 16 | publication-ready plots, graphs, and images', 17 | long_description = long_description, 18 | url = 'http://github.com/CCampJr/SciPlot-PyQt', 19 | author = 'Charles H. Camp Jr.', 20 | author_email = 'charles.camp@nist.gov', 21 | license = 'Public Domain', 22 | packages = find_packages(), 23 | zip_safe = False, 24 | include_package_data = True, 25 | install_requires=['numpy','matplotlib'], 26 | setup_requires=['pytest-runner'], 27 | tests_require=['pytest'], 28 | classifiers=['Development Status :: 3 - Alpha', 29 | 'Intended Audience :: Developers', 30 | 'Intended Audience :: Science/Research', 31 | 'Operating System :: OS Independent', 32 | 'Environment :: X11 Applications :: Qt', 33 | 'Programming Language :: Python :: 3 :: Only', 34 | 'Programming Language :: Python :: 3.4', 35 | 'Programming Language :: Python :: 3.5', 36 | 'Programming Language :: Python :: 3.6', 37 | 'Topic :: Scientific/Engineering :: Visualization']) -------------------------------------------------------------------------------- /CHANGELOG.rst: -------------------------------------------------------------------------------- 1 | ========= 2 | Changelog 3 | ========= 4 | 5 | This document records all notable changes to 6 | `SciPlot-PyQt `_. 7 | 8 | This project adheres to `PEP 440 -- Version Identification 9 | and Dependency Specification `_. 10 | 11 | 0.2.3 (21-11-29) 12 | ----------------- 13 | 14 | - Bug fix 15 | 16 | 0.2.2 (19-05-20) 17 | ----------------- 18 | 19 | - Added control to figure DPI and figsize 20 | - Minor tweaks to UI layout 21 | - Address compatibility with Matplotlib 2.*/3.* 22 | 23 | 0.2.1 (17-12-07) 24 | ---------------- 25 | 26 | - CHANGELOG added 27 | - Continuous integration with `Travis CI `_ 28 | and `Appveyor `_. 29 | - Stability improvements 30 | - Improved support for Matplotlib 1.5.* and 2.* 31 | 32 | 0.1.4 (17-09-26) 33 | ---------------- 34 | 35 | - Added concurrent support for both Matplotlib 1.5.* and 2.* 36 | 37 | 38 | 0.1.3 (17-01-19) 39 | ---------------- 40 | 41 | - Export bars, lines, and fill-betweens to CSV files 42 | - Total re-write of plot containers-- now a Plot-ID and Plot-Type system 43 | - Lots of improvements that were not catalogued 44 | 45 | 0.1.2 (16-07-22) 46 | ---------------- 47 | 48 | - Lots of improvements that were not catalogued 49 | - Can now pass MPL kwargs through sciplot instantiation 50 | 51 | 52 | 0.1.1 (16-07-21) 53 | ---------------- 54 | 55 | - SciPlot can now be embedded in other applications as a widget 56 | - Added bar plots 57 | 58 | 59 | 0.1 (16-07-21) 60 | -------------- 61 | 62 | - Initial stable(-ish) version 63 | - Deployed to PyPi -------------------------------------------------------------------------------- /sciplot/ui/models/abstract.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | 4 | Abstract ModelViewDelegate for MPL objects 5 | 6 | Created on Thu Jul 7 10:01:23 2016 7 | 8 | @author: chc 9 | """ 10 | 11 | from PyQt5.QtWidgets import (QApplication as _QApplication, 12 | QMainWindow as _QMainWindow, 13 | QColorDialog as _QColorDialog, 14 | QDoubleSpinBox as _QDoubleSpinBox, 15 | QComboBox as _QComboBox, 16 | QLineEdit as _QLineEdit, 17 | QStyledItemDelegate as _QStyledItemDelegate, 18 | QTableView as _QTableView, 19 | QSizePolicy as _QSizePolicy) 20 | 21 | from PyQt5.QtCore import (QAbstractTableModel as _QAbstractTableModel, 22 | QVariant as _QVariant, 23 | QObject as _QObject, 24 | pyqtSignal as _pyqtSignal, 25 | QModelIndex as _QModelIndex, 26 | Qt as _Qt) 27 | 28 | 29 | class AbstractTableModelMpl(_QAbstractTableModel, _QObject): 30 | 31 | def rowCount(self, parent=_QModelIndex()): 32 | return len(self._model_data) 33 | 34 | def columnCount(self, parent=_QModelIndex()): 35 | return len(self.headers) 36 | 37 | def headerData(self, col, orientation, role): 38 | if orientation == _Qt.Horizontal and role == _Qt.DisplayRole: 39 | return self.headers[col] 40 | return _QVariant() 41 | 42 | def doubleClickCheck(self, index): 43 | raise NotImplementedError 44 | 45 | def deleteData(self, index): 46 | self.setData(index, True) 47 | 48 | def data(self, index, role=_Qt.DisplayRole): 49 | raise NotImplementedError 50 | 51 | def setData(self, index, value, role=_Qt.EditRole): 52 | raise NotImplementedError 53 | 54 | def flags(self, index): 55 | flag = super(_QAbstractTableModel, self).flags(index) 56 | return flag | _Qt.ItemIsEditable 57 | 58 | 59 | class AbstractEditDelegateMpl(_QStyledItemDelegate): 60 | def createEditor(self, parent, option, index): 61 | raise NotImplementedError 62 | 63 | def setEditorData(self, editor, index): 64 | raise NotImplementedError 65 | 66 | def setModelData(self, editor, model, index): 67 | raise NotImplementedError 68 | -------------------------------------------------------------------------------- /sciplot/data/generic.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Generic data containers 4 | 5 | Created on Thu Jul 7 10:31:43 2016 6 | 7 | @author: chc 8 | """ 9 | 10 | import numpy as _np 11 | 12 | class Data: 13 | """ 14 | Contains the underlying data to be plotted 15 | 16 | Attributes 17 | ---------- 18 | x : ndarray (1D) 19 | x-data (effectively, the horizontal axis data) 20 | 21 | y : ndarray (1D) 22 | y-data (effectively, the vertical axis data) 23 | 24 | label : str 25 | Label information, i.e., the name of the plot, graph, etc. 26 | 27 | style_dict : dict 28 | Style information (e.g., color, linewidth, etc) 29 | 30 | mplobj : object 31 | Object returned from the MPL plotting procedure. 32 | """ 33 | def __init__(self): 34 | self._setupData() 35 | 36 | def _setupData(self): 37 | self.x = None 38 | self.y = None 39 | self.id = None 40 | self.label = None 41 | self.mplobj = None 42 | #self.units = {'x_units': None, 'y_units': None} 43 | self.style_dict = {} 44 | self.meta = {} 45 | 46 | @property 47 | def model_style(self): 48 | out = {} 49 | out.update(self.style_dict) 50 | out['label'] = self.label 51 | out['id'] = self.id 52 | out['meta'] = self.meta 53 | return out 54 | 55 | class Data2D(Data): 56 | """ 57 | Contains the underlying data to be imaged 58 | """ 59 | def __init__(self): 60 | self._setupData() 61 | self.img = None 62 | 63 | @property 64 | def extent(self): 65 | """ 66 | Generate an extent list used for mpl.imshow 67 | [xmin, xmax, ymin, ymax] 68 | """ 69 | ext = [None, None, None, None] 70 | 71 | if self.x is not None: 72 | ext[0] = self.x.min() 73 | ext[1] = self.x.max() 74 | else: 75 | ext[0] = 0 76 | ext[1] = self.img.shape[1] 77 | 78 | if self.y is not None: 79 | ext[2] = self.y.min() 80 | ext[3] = self.y.max() 81 | else: 82 | ext[2] = 0 83 | ext[3] = self.img.shape[0] 84 | 85 | return ext 86 | 87 | class DataGlobal: 88 | """ 89 | Contains data that is pertinent across all plots 90 | """ 91 | def __init__(self): 92 | self.labels = {'x_label': None, 'y_label': None, 'title': None} 93 | -------------------------------------------------------------------------------- /sciplot/sciplotUI_addon.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | 4 | Demonstration of adding functionality to Sciplot 5 | 6 | Created on Mon Jul 11 23:46:01 2016 7 | 8 | @author: chc 9 | """ 10 | 11 | import numpy as _np 12 | 13 | # Append sys path 14 | import sys as _sys 15 | import os as _os 16 | if __name__ == '__main__': 17 | _sys.path.append(_os.path.abspath('../')) 18 | 19 | from PyQt5.QtWidgets import (QApplication as _QApplication, 20 | QTabWidget as _QTabWidget, 21 | QCalendarWidget as _QCalendarWidget) 22 | 23 | from sciplot.sciplotUI import SciPlotUI as _SciPlotUI 24 | 25 | class SciPlotAddon(_SciPlotUI): 26 | """ 27 | Trivial example of using SciPlot with addons (or building another \ 28 | application on top of SciPlot). 29 | """ 30 | def __init__(self, limit_to=None, parent=None): 31 | super(SciPlotAddon, self).__init__(limit_to=limit_to, parent=parent, show=True) 32 | # Setup SciPlot window 33 | # self.setup(limit_to=limit_to, parent=parent) 34 | 35 | # Add a calendar to the tabs and have it displayed initially 36 | cal1 = _QCalendarWidget() 37 | self.ui.modelTabWidget.insertTab(0, cal1, 'Add-On Tab') 38 | self.ui.modelTabWidget.setCurrentIndex(0) 39 | 40 | # Add a calendar to the toolBox and have it displayed initially 41 | cal2 = _QCalendarWidget() 42 | self.ui.toolBox.addItem(cal2, 'Add-On Toolbox Tab') 43 | self.ui.toolBox.setCurrentIndex(self.ui.toolBox.count()-1) 44 | 45 | # Adjust with of groupBox to accomodate new widget (10% larger than) 46 | width = cal2.sizeHint().width() 47 | self.ui.groupBox.setMinimumWidth(1.1*width) 48 | self.ui.groupBox.setMaximumWidth(1.1*width) 49 | self.ui.groupBox.updateGeometry() 50 | 51 | if __name__ == '__main__': 52 | 53 | app = _QApplication(_sys.argv) 54 | 55 | winPlotter = SciPlotAddon(limit_to=['bars']) 56 | 57 | winPlotter.show() 58 | 59 | x = _np.arange(100) 60 | y = x**2 61 | 62 | # winPlotter.plot(x, y, x_label='X', label='Plot') 63 | # winPlotter.plot(x, y**1.1, label='Plot 2') 64 | # winPlotter.fill_between(x, y-1000, y+1000, label='Fill Between') 65 | # 66 | # winPlotter.imshow(_np.random.randn(100,100), label='Imshow') 67 | # winPlotter.bar(x[::10],y[::10],label='Bar') 68 | # winPlotter.hist(y,label='Hist') 69 | 70 | winPlotter.bar(0,10, label='Bar: single-value') 71 | _sys.exit(app.exec_()) 72 | -------------------------------------------------------------------------------- /sciplot/data/images.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Fri Jul 8 13:52:57 2016 4 | 5 | @author: chc 6 | """ 7 | 8 | from sciplot.data.generic import Data2D as _Data2D 9 | import matplotlib as _mpl 10 | import numpy as _np 11 | 12 | 13 | class ImageStyle: 14 | """ 15 | Style information for each data plotted (images) 16 | """ 17 | def __init__(self): 18 | self._setupImageStyle() 19 | 20 | def _setupImageStyle(self): 21 | """ 22 | Style dictioonary. 23 | 24 | Maybe include cmap color in the future 25 | """ 26 | self.style_dict = {'cmap_name': None, 27 | 'alpha': None, 28 | 'clim': None} 29 | 30 | def retrieve_style_from_image(self, image): 31 | """ 32 | Take an MPL image object and retrieve appropriate attributes 33 | """ 34 | 35 | # cmap name 36 | cmap_name = image.get_cmap().name 37 | if cmap_name is None: 38 | cmap_name = 'Custom' 39 | self.style_dict['cmap_name'] = cmap_name 40 | 41 | # Alpha (transparency) 42 | alpha = image.get_alpha() 43 | if alpha is None: 44 | alpha = 1 45 | self.style_dict['alpha'] = alpha 46 | 47 | # clim 48 | self.style_dict['clim'] = image.get_clim() 49 | 50 | 51 | class DataImages(_Data2D, ImageStyle): 52 | def __init__(self): 53 | self.cbar = {'obj' : None, 'show' : False} 54 | self._setupData() 55 | self._setupImageStyle() 56 | 57 | @property 58 | def model_style(self): 59 | out = {} 60 | out.update(self.style_dict) 61 | 62 | # clim broken out into high and low in model 63 | clim = out.pop('clim') 64 | out['clim_low'] = _np.min(clim) 65 | out['clim_high'] = _np.max(clim) 66 | 67 | out['label'] = self.label 68 | out['id'] = self.id 69 | out['meta'] = self.meta 70 | out['colorbar'] = self.cbar['show'] 71 | return out 72 | 73 | @model_style.setter 74 | def model_style(self, value): 75 | self.label = value['label'] 76 | self.meta = value['meta'] 77 | self.style_dict['cmap_name'] = value['cmap_name'] 78 | self.style_dict['alpha'] = value['alpha'] 79 | 80 | # clim broken out into high and low in model 81 | self.style_dict['clim'] = list((value['clim_low'], value['clim_high'])) 82 | 83 | # colobar 84 | self.cbar['show'] = value['colorbar'] 85 | -------------------------------------------------------------------------------- /sciplot/data/lines.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Thu Jul 7 10:30:28 2016 4 | 5 | @author: chc 6 | """ 7 | 8 | from sciplot.data.generic import Data as _Data 9 | import matplotlib as _mpl 10 | 11 | 12 | class LineStyle: 13 | """ 14 | Style information for each data plotted (line) 15 | """ 16 | def __init__(self): 17 | self._setupLineStyle() 18 | 19 | def _setupLineStyle(self): 20 | self.style_dict = {'color': None, 21 | 'alpha': None, 22 | 'linewidth': None, 23 | 'linestyle': None, 24 | 'marker': None, 25 | 'markersize': None} 26 | 27 | def retrieve_style_from_line(self, line): 28 | """ 29 | Take an MPL line object and retrieve appropriate attributes 30 | """ 31 | 32 | # Line color 33 | color = line.get_color() 34 | if isinstance(color, str): 35 | # print('Color: {}'.format(color)) 36 | # color = _mpl.colors.ColorConverter.cache[color] 37 | color = _mpl.colors.ColorConverter().to_rgb(color) 38 | if isinstance(color, tuple): 39 | color = list(color) 40 | self.style_dict['color'] = color 41 | 42 | # Alpha (transparency) 43 | alpha = line.get_alpha() 44 | if alpha is None: 45 | alpha = 1 46 | self.style_dict['alpha'] = alpha 47 | 48 | # Linewidth 49 | self.style_dict['linewidth'] = line.get_linewidth() 50 | 51 | # Linestyle 52 | self.style_dict['linestyle'] = line.get_linestyle() 53 | 54 | # Marker 55 | self.style_dict['marker'] = line.get_marker() 56 | 57 | # Marker Size 58 | self.style_dict['markersize'] = line.get_markersize() 59 | 60 | 61 | class DataLine(_Data, LineStyle): 62 | def __init__(self): 63 | self._setupData() 64 | self._setupLineStyle() 65 | 66 | @property 67 | def model_style(self): 68 | out = {} 69 | out.update(self.style_dict) 70 | out['label'] = self.label 71 | out['id'] = self.id 72 | out['meta'] = self.meta 73 | return out 74 | 75 | @model_style.setter 76 | def model_style(self, value): 77 | self.label = value['label'] 78 | self.meta = value['meta'] 79 | self.style_dict['color'] = value['color'] 80 | self.style_dict['alpha'] = value['alpha'] 81 | self.style_dict['linewidth'] = value['linewidth'] 82 | self.style_dict['linestyle'] = value['linestyle'] 83 | self.style_dict['marker'] = value['marker'] 84 | self.style_dict['markersize'] = value['markersize'] 85 | 86 | class PlotsDataContainer: 87 | """ 88 | Contains all plot data 89 | """ 90 | def __init__(self): 91 | self.line_data_list = [] 92 | self.patch_data_list = [] 93 | -------------------------------------------------------------------------------- /sciplot/data/bars.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Thu Jul 7 10:30:28 2016 4 | 5 | @author: chc 6 | """ 7 | 8 | from sciplot.data.generic import Data as _Data 9 | import matplotlib as _mpl 10 | 11 | 12 | class BarStyle: 13 | """ 14 | Style information for each data plotted (bar) 15 | """ 16 | def __init__(self): 17 | self._setupBarStyle() 18 | 19 | def _setupBarStyle(self): 20 | self.style_dict = {'facecolor': None, 21 | 'alpha': None, 22 | 'edgecolor': None, 23 | 'linewidth': None, 24 | 'width_factor': None} 25 | 26 | def retrieve_style_from_bar(self, bar): 27 | """ 28 | Take an MPL bar object and retrieve appropriate attributes 29 | """ 30 | 31 | # facecolor 32 | facecolor = bar.get_facecolor()[:-1] 33 | if isinstance(facecolor, str): 34 | # facecolor = _mpl.colors.ColorConverter.cache[facecolor] 35 | facecolor = _mpl.colors.ColorConverter().to_rgb(facecolor) 36 | if isinstance(facecolor, tuple): 37 | facecolor = list(facecolor) 38 | self.style_dict['facecolor'] = facecolor 39 | 40 | # Alpha (transparency) 41 | alpha = bar.get_alpha() 42 | if alpha is None: 43 | alpha = 1 44 | self.style_dict['alpha'] = alpha 45 | 46 | # edgecolor 47 | edgecolor = bar.get_edgecolor()[:-1] 48 | if isinstance(edgecolor, str): 49 | # edgecolor = _mpl.colors.ColorConverter.cache[edgecolor] 50 | edgecolor = _mpl.colors.ColorConverter().to_rgb(edgecolor) 51 | if isinstance(edgecolor, tuple): 52 | edgecolor = list(edgecolor) 53 | self.style_dict['edgecolor'] = edgecolor 54 | 55 | # Linewidth 56 | self.style_dict['linewidth'] = bar.get_linewidth() 57 | 58 | 59 | class DataBar(_Data, BarStyle): 60 | def __init__(self): 61 | self._width = None 62 | self._left = None 63 | self._gap = None 64 | self._bottom = None 65 | self._setupData() 66 | self._setupBarStyle() 67 | 68 | @property 69 | def model_style(self): 70 | out = {} 71 | out.update(self.style_dict) 72 | out['label'] = self.label 73 | out['id'] = self.id 74 | out['meta'] = self.meta 75 | return out 76 | 77 | @model_style.setter 78 | def model_style(self, value): 79 | self.label = value['label'] 80 | self.meta = value['meta'] 81 | self.style_dict['facecolor'] = value['facecolor'] 82 | self.style_dict['alpha'] = value['alpha'] 83 | self.style_dict['edgecolor'] = value['edgecolor'] 84 | self.style_dict['linewidth'] = value['linewidth'] 85 | self.style_dict['width_factor'] = value['width_factor'] 86 | if self._gap is not None: 87 | self._width = self._gap*self.style_dict['width_factor'] 88 | else: 89 | self._width = self.style_dict['width_factor'] 90 | self._left = self.x - self._width/2 91 | -------------------------------------------------------------------------------- /sciplot/data/polycollections.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Thu Jul 7 15:06:04 2016 4 | 5 | @author: chc 6 | """ 7 | 8 | import matplotlib as _mpl 9 | 10 | 11 | class PolyCollectionStyle: 12 | """ 13 | Style information for each data plotted (polycollection) 14 | """ 15 | def __init__(self): 16 | self._setupPolyCollectionStyle() 17 | 18 | def _setupPolyCollectionStyle(self): 19 | self.style_dict = {'facecolor': None, 20 | 'alpha': None, 21 | 'edgecolor': None, 22 | 'linewidth': None} 23 | 24 | def retrieve_style_from_polycollection(self, pc): 25 | """ 26 | Take an MPL polycollection object and retrieve appropriate attributes 27 | """ 28 | 29 | # facecolor p-collections return arrays of RGBA 30 | # Not going to worry about different face colors yet 31 | color = pc.get_facecolors()[0][:-1] 32 | if isinstance(color, str): 33 | # color = _mpl.colors.ColorConverter.cache[color] 34 | color = _mpl.colors.ColorConverter().to_rgb(color) 35 | if isinstance(color, tuple): 36 | color = list(color) 37 | self.style_dict['facecolor'] = color 38 | 39 | # Alpha (transparency) 40 | alpha = pc.get_alpha() 41 | if alpha is None: 42 | alpha = 1 43 | self.style_dict['alpha'] = alpha 44 | 45 | # edgecolor p-collections return arrays of RGBA 46 | # Not going to worry about different edge colors yet 47 | if pc.get_edgecolors().size > 0: # MPL 2.0 starts with no edgecolors 48 | color = pc.get_edgecolors()[0][:-1] 49 | else: 50 | pass 51 | if isinstance(color, str): 52 | # color = _mpl.colors.ColorConverter.cache[color] 53 | color = _mpl.colors.ColorConverter().to_rgb(color) 54 | if isinstance(color, tuple): 55 | color = list(color) 56 | self.style_dict['edgecolor'] = color 57 | 58 | # Linewidth (p-collection return tuples of len 1) 59 | self.style_dict['linewidth'] = pc.get_linewidth()[0] 60 | 61 | 62 | # Not actually going to use polycollection raw data 63 | # So I'm not going to create this 64 | # 65 | #class DataPolyCollection(_Data, PolyCollectionStyle): 66 | # def __init__(self): 67 | # self._setupData() 68 | # self._setupLineStyle() 69 | # 70 | # @property 71 | # def model_style(self): 72 | # out = {} 73 | # out.update(self.style_dict) 74 | # out['label'] = self.label 75 | # return out 76 | # 77 | # @model_style.setter 78 | # def model_style(self, value): 79 | # self.label = value['label'] 80 | # self.style_dict['color'] = value['color'] 81 | # self.style_dict['alpha'] = value['alpha'] 82 | # self.style_dict['linewidth'] = value['linewidth'] 83 | # self.style_dict['linestyle'] = value['linestyle'] 84 | # self.style_dict['marker'] = value['marker'] 85 | # self.style_dict['markersize'] = value['markersize'] 86 | 87 | class PlotsDataContainer: 88 | """ 89 | Contains all plot data 90 | """ 91 | def __init__(self): 92 | self.line_data_list = [] 93 | self.patch_data_list = [] 94 | -------------------------------------------------------------------------------- /sciplot/ui/dialogs.py: -------------------------------------------------------------------------------- 1 | from PyQt5.QtWidgets import (QWidget, QDialogButtonBox, QApplication, 2 | QDialog, QHBoxLayout, QVBoxLayout, 3 | QLabel, QDoubleSpinBox, QSpinBox) 4 | import sys 5 | 6 | class DualEntry(QDialog): 7 | """ Just a simple Dialog for retrieving 2 values """ 8 | def __init__(self, entry1, entry2, input_type, text, min, max, parent=None): 9 | """ 10 | Note: Use the static method 11 | """ 12 | super().__init__(parent=parent) 13 | self.initUI(entry1, entry2, input_type, text, min, max) 14 | 15 | def initUI(self, entry1, entry2, input_type, text, min, max): 16 | self.setObjectName('Dialog') 17 | self.setStyleSheet("font: 10pt \"Arial\";") 18 | self.setWindowTitle('Input dialog') 19 | 20 | # Setting the parent makes it the default layout 21 | self.vlayout = QVBoxLayout(self) 22 | self.vlayout.setObjectName('vlayout') 23 | 24 | self.main_text = QLabel(self) 25 | self.main_text.setText(text) 26 | self.vlayout.insertWidget(0, self.main_text) 27 | self.hlayout = QHBoxLayout() 28 | self.hlayout.setObjectName('hlayout') 29 | self.vlayout.insertLayout(1,self.hlayout) 30 | 31 | if input_type is float: 32 | self.entry1 = QDoubleSpinBox(self) 33 | self.entry2 = QDoubleSpinBox(self) 34 | elif input_type is int: 35 | self.entry1 = QSpinBox(self) 36 | self.entry2 = QSpinBox(self) 37 | self.entry1.setMaximum(max) 38 | self.entry1.setMinimum(min) 39 | self.entry2.setMaximum(max) 40 | self.entry2.setMinimum(min) 41 | self.entry1.setValue(entry1) 42 | self.entry2.setValue(entry2) 43 | 44 | self.hlayout.insertWidget(0, self.entry1) 45 | self.hlayout.insertWidget(1, self.entry2) 46 | 47 | self.bb = QDialogButtonBox(self) 48 | self.bb.setStandardButtons(QDialogButtonBox.Cancel|QDialogButtonBox.Ok) 49 | self.bb.accepted.connect(self.accept) 50 | self.bb.rejected.connect(self.reject) 51 | self.vlayout.insertWidget(-1,self.bb) 52 | self.show() 53 | 54 | @staticmethod 55 | def getDualEntries(entry1, entry2, input_type=float, text='Enter Values:', min=-10000000000, 56 | max=10000000000, parent=None): 57 | """ 58 | Pops up a dialog box that retrieves 2 values, either float or int 59 | 60 | Parameters 61 | ---------- 62 | entry1: int or float 63 | Default value in the left box 64 | 65 | entry2: int or float 66 | Default value in the right box 67 | 68 | input_type : type 69 | Either float or int are currently supported 70 | 71 | text : str 72 | Text describing the values to set 73 | 74 | min : int or float 75 | Minimum value for entries 76 | 77 | max : int or float 78 | Maximum value for entries 79 | 80 | parent : object or None 81 | Parent of the Dialog (QDialog) box. 82 | 83 | Returns 84 | ------- 85 | (numeric, numeric), int 86 | Returns (left value, right value), Ok pressed. If Ok was pressed, value == 1, else 0. 87 | 88 | """ 89 | dialog = DualEntry(entry1, entry2, input_type, text, min, max, parent) 90 | result = dialog.exec_() 91 | if result: 92 | return (dialog.entry1.value(), dialog.entry2.value()), result 93 | else: 94 | return (), result 95 | 96 | if __name__ == '__main__': 97 | 98 | app = QApplication(sys.argv) 99 | ex = DualEntry.getDualEntries(0, 100, float, 'Tets', 0, 100) 100 | print(ex) 101 | # sys.exit(app.exec_()) -------------------------------------------------------------------------------- /sciplot/ui/widget_mpl.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Create generic MPL canvas, toolbar, figure, axis. 4 | 5 | Heavily borrowed from: 6 | matplotlib.org/examples/user_interfaces/embedding_in_qt5.html 7 | 8 | Created on Thu Jun 30 15:41:35 2016 9 | 10 | @author: chc 11 | """ 12 | 13 | import sys as _sys 14 | import os as _os 15 | import numpy as _np 16 | import matplotlib as _mpl 17 | import matplotlib.style as _mpl_sty 18 | 19 | from PyQt5 import QtWidgets as _QtWidgets 20 | 21 | from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as \ 22 | FigureCanvas 23 | from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as \ 24 | _NavigationToolbar 25 | 26 | from matplotlib.figure import Figure as _Figure 27 | 28 | # Make sure that we are using QT5 29 | _mpl.use('Qt5Agg') 30 | 31 | 32 | class MplCanvas(FigureCanvas): 33 | """Ultimately, this is a QWidget (as well as a FigureCanvasAgg, etc.).""" 34 | 35 | def __init__(self, subplot=111, parent=None, width=5, height=4, dpi=100, 36 | figfacecolor=3*[0.941], style=None, **kwargs): 37 | 38 | # super(MplCanvas, self).__init__(parent) 39 | if style is None: 40 | pass 41 | else: 42 | _mpl.style.use(style) 43 | self._mpl_v1 = _mpl.__version__.split('.')[0] == 1 44 | 45 | # Create figure and axes 46 | self.fig = _Figure(figsize=(width, height), dpi=dpi, 47 | facecolor=figfacecolor, **kwargs) 48 | # Initialize the canvas 49 | FigureCanvas.__init__(self, self.fig) 50 | self.setParent(parent) 51 | 52 | self.setupAx(subplot=subplot, **kwargs) 53 | 54 | # Not really used, but could be used to have some sort of initial plot 55 | self.compute_initial_figure() 56 | 57 | # Set canvas size policies and geometry 58 | FigureCanvas.setSizePolicy(self, _QtWidgets.QSizePolicy.Fixed, 59 | _QtWidgets.QSizePolicy.Fixed) 60 | FigureCanvas.updateGeometry(self) 61 | 62 | # Create the toolbar and connect to canvas (self) 63 | self.toolbar = _NavigationToolbar(self, None) 64 | 65 | def setupAx(self, subplot=111, **kwargs): 66 | if subplot > 339: 67 | raise ValueError('Subplot is limited to 3x3 or less') 68 | # Break out e.g. 111 to [1, 1, 1] 69 | subplot_list = [int(val) for val in list(str(subplot))] 70 | 71 | # Total number of subplots 72 | num_plots = subplot_list[0]*subplot_list[1] 73 | 74 | # Num of vertical subplots 75 | vnum = subplot_list[0] 76 | 77 | # Num of horiz subplots 78 | hnum = subplot_list[1] 79 | 80 | # [start, stop) count 81 | c_start = vnum*100 + hnum*10 + 1 82 | c_stop = vnum*100 + hnum*10 + num_plots + 1 83 | 84 | if num_plots <= 0: 85 | raise ValueError('subplot need be 1x1 to 3x3') 86 | elif num_plots == 1: 87 | self.ax = self.fig.add_subplot(111, **kwargs) 88 | elif num_plots > 1: 89 | self.ax = [] 90 | for num, splt_num in enumerate(_np.arange(c_start, c_stop)): 91 | self.ax.append(self.fig.add_subplot(splt_num, **kwargs)) 92 | if self._mpl_v1: 93 | self.ax[num].hold(False) 94 | self.fig.tight_layout() 95 | 96 | def compute_initial_figure(self): 97 | pass 98 | 99 | 100 | if __name__ == '__main__': 101 | 102 | from PyQt5 import QtCore as _QtCore 103 | 104 | class ApplicationWindow(_QtWidgets.QMainWindow): 105 | def __init__(self, subplot=111, style=None): 106 | _QtWidgets.QMainWindow.__init__(self) 107 | #super(ApplicationWindow, self).__init__(None) 108 | self.setAttribute(_QtCore.Qt.WA_DeleteOnClose) 109 | 110 | self.main_widget = _QtWidgets.QWidget(self) 111 | 112 | self.mpl_layout = _QtWidgets.QVBoxLayout(self.main_widget) 113 | self.mpl_widget = MplCanvas(parent=self.main_widget, 114 | subplot=subplot, width=5, height=4, 115 | dpi=100, style=style) 116 | self.mpl_layout.addWidget(self.mpl_widget.toolbar) 117 | self.mpl_layout.addWidget(self.mpl_widget) 118 | 119 | self.main_widget.setFocus() 120 | self.setCentralWidget(self.main_widget) 121 | 122 | self.setSizePolicy(_QtWidgets.QSizePolicy.Expanding, 123 | _QtWidgets.QSizePolicy.Expanding) 124 | 125 | qApp = _QtWidgets.QApplication(_sys.argv) 126 | 127 | 128 | aw2 = ApplicationWindow(style='seaborn-deep', subplot=211) 129 | aw2.mpl_widget.ax[0].set_title('0') 130 | aw2.mpl_widget.ax[1].set_title('1') 131 | aw2.show() 132 | 133 | qApp.exec_() 134 | -------------------------------------------------------------------------------- /sciplot/utils/mplstyle.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Thu Jun 30 10:23:03 2016 4 | 5 | @author: chc 6 | """ 7 | 8 | from cycler import cycler 9 | import copy as _copy 10 | import numpy as _np 11 | import matplotlib as _mpl 12 | import matplotlib.markers 13 | import matplotlib.lines 14 | 15 | class MplMarkers: 16 | MARKER_DICT = _mpl.markers.MarkerStyle.markers 17 | MARKER_DESC = [] 18 | MARKER_SYMBOL = [] 19 | for count in MARKER_DICT: 20 | MARKER_SYMBOL.append(count) 21 | MARKER_DESC.append(MARKER_DICT[count]) 22 | 23 | @classmethod 24 | def index(cls, str_in): 25 | idx = None 26 | try: 27 | idx = cls.MARKER_DESC.index(str_in) 28 | except: 29 | idx = None 30 | try: 31 | idx = cls.MARKER_SYMBOL.index(str_in) 32 | except: 33 | idx = None 34 | return idx 35 | 36 | class MplLines: 37 | LINESTYLE_DICT = _mpl.lines.lineStyles 38 | LINESTYLE_DESC = [] 39 | LINESTYLE_SYMBOL = [] 40 | for count in LINESTYLE_DICT: 41 | LINESTYLE_SYMBOL.append(count) 42 | LINESTYLE_DESC.append(LINESTYLE_DICT[count]) 43 | 44 | @classmethod 45 | def index(cls, str_in): 46 | idx = None 47 | try: 48 | idx = cls.LINESTYLE_DESC.index(str_in) 49 | except: 50 | idx = None 51 | try: 52 | idx = cls.LINESTYLE_SYMBOL.index(str_in) 53 | except: 54 | idx = None 55 | return idx 56 | 57 | class MplStyleSheets: 58 | """ 59 | 60 | """ 61 | 62 | _tableau20 = _np.array([[31, 119, 180], 63 | [174, 199, 232], 64 | [255, 127, 14], 65 | [255, 187, 120], 66 | [44, 160, 44], 67 | [152, 223, 138], 68 | [214, 39, 40], 69 | [255, 152, 150], 70 | [148, 103, 189], 71 | [197, 176, 213], 72 | [140, 86, 75], 73 | [196, 156, 148], 74 | [227, 119, 194], 75 | [247, 182, 210], 76 | [127, 127, 127], 77 | [199, 199, 199], 78 | [188, 189, 34], 79 | [219, 219, 141], 80 | [23, 190, 207], 81 | [158, 218, 229]], dtype=_np.float)/255 82 | 83 | _tableau10 = _tableau20[::2,:] 84 | 85 | _tableau10_med = _np.array([[114, 158, 206], 86 | [255, 158, 74], 87 | [103, 191, 92], 88 | [237, 102, 93], 89 | [173, 139, 201], 90 | [168, 120, 110], 91 | [237, 151, 202], 92 | [162, 162, 162], 93 | [205, 204, 93], 94 | [109, 204, 218]], dtype = _np.float)/255 95 | 96 | 97 | _base_crikit = {'font.family': ['sans-serif'], 98 | 'font.sans-serif': ['Arial', 99 | 'Bitstream Vera Sans', 100 | 'DejaVu Sans', 101 | 'Lucida Grande', 102 | 'Verdana', 103 | 'Geneva', 104 | 'Lucid', 105 | 'Avant Garde', 106 | 'sans-serif'], 107 | 'axes.prop_cycle': cycler('color', _tableau10), 108 | # 'axes.prop_cycle': plt.style.library['ggplot']['axes.prop_cycle'], 109 | 'image.cmap': 'viridis', 110 | 'image.interpolation': 'none'} 111 | 112 | _paper_halfwidth = {'axes.labelsize': 8.8, 113 | 'axes.titlesize': 9.6, 114 | 'figure.figsize': [3.3, 4.4], 115 | 'grid.linewidth': 0.8, 116 | 'legend.fontsize': 8.0, 117 | 'lines.linewidth': 1.4, 118 | 'lines.markeredgewidth': 0.0, 119 | 'lines.markersize': 5.6, 120 | 'patch.linewidth': 0.24, 121 | 'xtick.labelsize': 8.0, 122 | 'xtick.major.pad': 5.6, 123 | 'xtick.major.width': 0.8, 124 | 'xtick.minor.width': 0.4, 125 | 'ytick.labelsize': 8.0, 126 | 'ytick.major.pad': 5.6, 127 | 'ytick.major.width': 0.8, 128 | 'ytick.minor.width': 0.4} 129 | 130 | _paper_fullwidth = {'axes.labelsize': 8.8, 131 | 'axes.titlesize': 9.6, 132 | 'figure.figsize': [6.4, 4.4], 133 | 'grid.linewidth': 0.8, 134 | 'legend.fontsize': 8.8, 135 | 'lines.linewidth': 1.4, 136 | 'lines.markeredgewidth': 0.0, 137 | 'lines.markersize': 5.6, 138 | 'patch.linewidth': 0.24, 139 | 'xtick.labelsize': 8.0, 140 | 'xtick.major.pad': 5.6, 141 | 'xtick.major.width': 0.8, 142 | 'xtick.minor.width': 0.4, 143 | 'ytick.labelsize': 8.0, 144 | 'ytick.major.pad': 5.6, 145 | 'ytick.major.width': 0.8, 146 | 'ytick.minor.width': 0.4} 147 | 148 | _poster = {'axes.labelsize': 17.6, 149 | 'axes.titlesize': 19.2, 150 | 'figure.figsize': [12.8, 8.8], 151 | 'grid.linewidth': 1.6, 152 | 'legend.fontsize': 16.0, 153 | 'lines.linewidth': 2.8, 154 | 'lines.markeredgewidth': 0.0, 155 | 'lines.markersize': 11.2, 156 | 'patch.linewidth': 0.48, 157 | 'xtick.labelsize': 16.0, 158 | 'xtick.major.pad': 11.2, 159 | 'xtick.major.width': 1.6, 160 | 'xtick.minor.width': 0.8, 161 | 'ytick.labelsize': 16.0, 162 | 'ytick.major.pad': 11.2, 163 | 'ytick.major.width': 1.6, 164 | 'ytick.minor.width': 0.8} 165 | 166 | basic_halfwidth = _copy.deepcopy(_base_crikit) 167 | basic_halfwidth.update(_paper_halfwidth) 168 | 169 | basic_fullwidth = _copy.deepcopy(_base_crikit) 170 | basic_fullwidth.update(_paper_fullwidth) 171 | 172 | basic_poster = _copy.deepcopy(_base_crikit) 173 | basic_poster.update(_poster) 174 | 175 | 176 | if __name__ == '__main__': 177 | import matplotlib.pyplot as _plt 178 | import numpy as _np 179 | 180 | x = _np.arange(100) 181 | _plt.style.use('classic') 182 | style = MplStyleSheets.basic_fullwidth 183 | _plt.style.use(style) 184 | 185 | _plt.figure() 186 | 187 | _plt.plot((_np.random.rand(10,1)*x).T, label='test') 188 | _plt.legend() 189 | _plt.show() 190 | 191 | _plt.figure() 192 | _plt.imshow(_np.random.rand(100,100)) 193 | _plt.colorbar() 194 | _plt.show() 195 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | .. -*- mode: rst -*- 2 | 3 | .. image:: https://travis-ci.org/CCampJr/SciPlot-PyQt.svg?branch=master 4 | :alt: Travis CI Status 5 | :target: https://travis-ci.org/CCampJr/SciPlot-PyQt 6 | 7 | .. image:: https://ci.appveyor.com/api/projects/status/github/CCampJr/SciPlot-PyQt?branch=master&svg=true 8 | :alt: AppVeyor CI Status 9 | :target: https://ci.appveyor.com/project/CCampJr/sciplot-pyqt 10 | 11 | .. image:: https://img.shields.io/pypi/v/sciplot-pyqt.svg 12 | :alt: PyPI Project Page 13 | :target: https://pypi.org/project/sciplot-pyqt/ 14 | 15 | .. image:: https://img.shields.io/pypi/pyversions/sciplot-pyqt.svg 16 | :alt: PyPI - Python Version 17 | :target: https://pypi.org/project/sciplot-pyqt/ 18 | 19 | .. image:: https://img.shields.io/badge/PyQt-5-ff69b4.svg 20 | :alt: PyQt5 21 | :target: https://www.riverbankcomputing.com/software/pyqt/download5 22 | 23 | .. image:: https://img.shields.io/badge/License-NIST%20Public%20Domain-green.svg 24 | :alt: NIST Public Domain 25 | :target: https://github.com/CCampJr/SciPlot-PyQt/blob/master/LICENSE.md 26 | 27 | SciPlot-PyQt: Publication-ready scientific plotting for Python 28 | =============================================================== 29 | 30 | SciPlot-PyQt (aka SciPlot) is a user-interface/matplotlib wrapper built with 31 | PyQt5 that allows interactive plotting through an embedded matplotlib canvas. 32 | It enables fast and easy publication-ready plots and images: 33 | 34 | - Interactive plotting 35 | 36 | - Theme and style editing (TODO) 37 | 38 | - Figure saving and opening for later editing (TODO) 39 | 40 | Dependencies 41 | ------------ 42 | 43 | **Note**: These are the developmental system specs. Older versions of certain 44 | packages may work. 45 | 46 | - python >= 3.4 47 | 48 | - Tested with 3.6.7, 3.7.1 49 | 50 | - numpy 51 | 52 | - PyQT5 (5.5.* or 5.6.*) 53 | 54 | - Tested with 5.6, 5.8.1, 5.12 55 | 56 | - matplotlib (1.*, 2.*, 3.*) 57 | 58 | - Sphinx (1.5.2) (Only for building documentation) 59 | 60 | 61 | Known Issues 62 | ------------ 63 | 64 | 1. **IPython**: SciPlot has problems when imported and ran via sciplot.main() or %run from within 65 | IPython. It appears to work as expected when called through a normal Python interpreter. 66 | 67 | 2. **PyQt 5.7**: There is a bug in PyQt 5.7.* that will prevent SciPlot's tables from showing the 68 | individual plot entries (see https://www.riverbankcomputing.com/pipermail/pyqt/2017-January/038483.html). 69 | Apparently, this will be fixed in 5.7.2. 70 | 71 | - As WinPython 3.5.2.3Qt5 and 3.5.2.2Qt5 use PyQt 5.7.*, it is advised to use WinPython 3.5.2.1Qt5 or 72 | 3.4.4.5Qt5 until the matter is sorted out. 73 | 74 | - Alternatively, one can uninstall pyqt5.7.* and force an install of <= 5.6.*. 75 | 76 | 3. **MATPLOTLIB 2.0**: SciPlot version solder than 0.1.4 will crash with MPL 2.* as 77 | several changes have been made to the MPL API. 78 | 79 | - For v0.1.3, the dev-MPL2 branch should address those problems 80 | - v0.1.4 is a merge of v0.1.3 and the dev-MPL2 branch (with other updates) 81 | 82 | 83 | Installation 84 | ------------ 85 | 86 | **Note**: the installer only checks for the dependencies of matplotlib and 87 | numpy. PyQt5 is not checked for, though, it is also required. See Depencies 88 | above for more information and requirements. 89 | 90 | Using pip (hard install) 91 | ~~~~~~~~~~~~~~~~~~~~~~~~ 92 | 93 | .. code:: 94 | 95 | # Only Python 3.* installed 96 | pip install sciplot-pyqt 97 | 98 | # If you have both Python 2.* and 3.* you may need 99 | pip3 install sciplot-pyqt 100 | 101 | Using pip (soft install [can update with git]) 102 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 103 | 104 | .. code:: 105 | 106 | # Make new directory for sciplot-pyqt and enter it 107 | # Clone from github 108 | git clone https://github.com/CCampJr/SciPlot-PyQt.git 109 | 110 | # Only Python 3.* installed 111 | pip install -e . 112 | 113 | # If you have both Python 2.* and 3.* you may need instead 114 | pip3 install -e . 115 | 116 | # To update in the future 117 | git pull 118 | 119 | Using setuptools 120 | ~~~~~~~~~~~~~~~~ 121 | 122 | You will need to `download the repository `_ 123 | or clone the repository with git: 124 | 125 | .. code:: 126 | 127 | # Make new directory for sciplot-pyqt and enter it 128 | # Clone from github 129 | git clone https://github.com/CCampJr/SciPlot-PyQt.git 130 | 131 | Perform the install **without building the documentation**: 132 | 133 | .. code:: 134 | 135 | python setup.py install 136 | 137 | Perform the install **and build the documentation** (see dependencies above): 138 | 139 | .. code:: 140 | 141 | python setup.py build_sphinx 142 | python setup.py install 143 | 144 | Usage 145 | ----- 146 | 147 | .. code:: python 148 | 149 | import sciplot 150 | sp = sciplot.main() 151 | 152 | **Note** Sciplot-pyqt relies on an existing, active Qt5 QApplication instance 153 | to operate. Some versions of Matplotlib 1.5.* provide this, others do not. 154 | Matplotlib 2.* seems to always provide this functionality. 155 | 156 | **If** you perform the examples below and just a non-repsonsive white window 157 | appears, follow up the plotting commands with: 158 | 159 | .. code:: python 160 | 161 | sp.app.exec_() 162 | 163 | Sciplot tests for this and will provide you with a message to your terminal: 164 | 165 | .. code:: 166 | 167 | No QApplication instance (this is common with certain 168 | version of Matplotlib). Creating one. 169 | You will need to exec manually after you finish plotting. 170 | 171 | -----------Example--------------- 172 | import sciplot 173 | sp = sciplot.main() 174 | 175 | # Plot a line 176 | sp.plot((0,1),(0,1)) 177 | # Start the QApplication 178 | sp.app.exec_() 179 | 180 | Example 181 | ~~~~~~~ 182 | 183 | .. code:: python 184 | 185 | sp.plot((0,1),(2,3),label='Line', x_label='X', y_label='Y', ls='--') 186 | sp.fill_between((0,1),(1,2),(3,4),label='Fill Between', color='r', alpha=0.25) 187 | 188 | .. image:: ./Screenshot.png 189 | 190 | .. code:: python 191 | 192 | sp.hist(r, bins=100, label='Histogram', color=[0, .2, .3], 193 | x_label='Amplitude', y_label='Counts', alpha=0.5) 194 | 195 | .. image:: ./Screenshot2.png 196 | 197 | .. code:: python 198 | 199 | sp.imshow(r, clim=[25,75], cmap='viridis', label='Imshow', x_label='X (pix)', 200 | y_label='Y (pix)') 201 | 202 | .. image:: ./Screenshot3.png 203 | 204 | NONLICENSE 205 | ---------- 206 | This software was developed at the National Institute of Standards and Technology (NIST) by 207 | employees of the Federal Government in the course of their official duties. Pursuant to 208 | `Title 17 Section 105 of the United States Code `_, 209 | this software is not subject to copyright protection and is in the public domain. 210 | NIST assumes no responsibility whatsoever for use by other parties of its source code, 211 | and makes no guarantees, expressed or implied, about its quality, reliability, or any other characteristic. 212 | 213 | Specific software products identified in this open source project were used in order 214 | to perform technology transfer and collaboration. In no case does such identification imply 215 | recommendation or endorsement by the National Institute of Standards and Technology, nor 216 | does it imply that the products identified are necessarily the best available for the 217 | purpose. 218 | 219 | Contact 220 | ------- 221 | Charles H Camp Jr: `charles.camp@nist.gov `_ 222 | 223 | Contributors 224 | ------------- 225 | Charles H Camp Jr, Mona Lee, Xavier Audier 226 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | REM Command file for Sphinx documentation 4 | 5 | if "%SPHINXBUILD%" == "" ( 6 | set SPHINXBUILD=sphinx-build 7 | ) 8 | set BUILDDIR=build 9 | set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% source 10 | set I18NSPHINXOPTS=%SPHINXOPTS% source 11 | if NOT "%PAPER%" == "" ( 12 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% 13 | set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% 14 | ) 15 | 16 | if "%1" == "" goto help 17 | 18 | if "%1" == "help" ( 19 | :help 20 | echo.Please use `make ^` where ^ is one of 21 | echo. html to make standalone HTML files 22 | echo. dirhtml to make HTML files named index.html in directories 23 | echo. singlehtml to make a single large HTML file 24 | echo. pickle to make pickle files 25 | echo. json to make JSON files 26 | echo. htmlhelp to make HTML files and a HTML help project 27 | echo. qthelp to make HTML files and a qthelp project 28 | echo. devhelp to make HTML files and a Devhelp project 29 | echo. epub to make an epub 30 | echo. epub3 to make an epub3 31 | echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter 32 | echo. text to make text files 33 | echo. man to make manual pages 34 | echo. texinfo to make Texinfo files 35 | echo. gettext to make PO message catalogs 36 | echo. changes to make an overview over all changed/added/deprecated items 37 | echo. xml to make Docutils-native XML files 38 | echo. pseudoxml to make pseudoxml-XML files for display purposes 39 | echo. linkcheck to check all external links for integrity 40 | echo. doctest to run all doctests embedded in the documentation if enabled 41 | echo. coverage to run coverage check of the documentation if enabled 42 | goto end 43 | ) 44 | 45 | if "%1" == "clean" ( 46 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i 47 | del /q /s %BUILDDIR%\* 48 | goto end 49 | ) 50 | 51 | 52 | REM Check if sphinx-build is available and fallback to Python version if any 53 | %SPHINXBUILD% 1>NUL 2>NUL 54 | if errorlevel 9009 goto sphinx_python 55 | goto sphinx_ok 56 | 57 | :sphinx_python 58 | 59 | set SPHINXBUILD=python -m sphinx.__init__ 60 | %SPHINXBUILD% 2> nul 61 | if errorlevel 9009 ( 62 | echo. 63 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 64 | echo.installed, then set the SPHINXBUILD environment variable to point 65 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 66 | echo.may add the Sphinx directory to PATH. 67 | echo. 68 | echo.If you don't have Sphinx installed, grab it from 69 | echo.http://sphinx-doc.org/ 70 | exit /b 1 71 | ) 72 | 73 | :sphinx_ok 74 | 75 | 76 | if "%1" == "html" ( 77 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html 78 | if errorlevel 1 exit /b 1 79 | echo. 80 | echo.Build finished. The HTML pages are in %BUILDDIR%/html. 81 | goto end 82 | ) 83 | 84 | if "%1" == "dirhtml" ( 85 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml 86 | if errorlevel 1 exit /b 1 87 | echo. 88 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. 89 | goto end 90 | ) 91 | 92 | if "%1" == "singlehtml" ( 93 | %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml 94 | if errorlevel 1 exit /b 1 95 | echo. 96 | echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. 97 | goto end 98 | ) 99 | 100 | if "%1" == "pickle" ( 101 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle 102 | if errorlevel 1 exit /b 1 103 | echo. 104 | echo.Build finished; now you can process the pickle files. 105 | goto end 106 | ) 107 | 108 | if "%1" == "json" ( 109 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json 110 | if errorlevel 1 exit /b 1 111 | echo. 112 | echo.Build finished; now you can process the JSON files. 113 | goto end 114 | ) 115 | 116 | if "%1" == "htmlhelp" ( 117 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp 118 | if errorlevel 1 exit /b 1 119 | echo. 120 | echo.Build finished; now you can run HTML Help Workshop with the ^ 121 | .hhp project file in %BUILDDIR%/htmlhelp. 122 | goto end 123 | ) 124 | 125 | if "%1" == "qthelp" ( 126 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp 127 | if errorlevel 1 exit /b 1 128 | echo. 129 | echo.Build finished; now you can run "qcollectiongenerator" with the ^ 130 | .qhcp project file in %BUILDDIR%/qthelp, like this: 131 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\SciPlot-PyQt.qhcp 132 | echo.To view the help file: 133 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\SciPlot-PyQt.ghc 134 | goto end 135 | ) 136 | 137 | if "%1" == "devhelp" ( 138 | %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp 139 | if errorlevel 1 exit /b 1 140 | echo. 141 | echo.Build finished. 142 | goto end 143 | ) 144 | 145 | if "%1" == "epub" ( 146 | %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub 147 | if errorlevel 1 exit /b 1 148 | echo. 149 | echo.Build finished. The epub file is in %BUILDDIR%/epub. 150 | goto end 151 | ) 152 | 153 | if "%1" == "epub3" ( 154 | %SPHINXBUILD% -b epub3 %ALLSPHINXOPTS% %BUILDDIR%/epub3 155 | if errorlevel 1 exit /b 1 156 | echo. 157 | echo.Build finished. The epub3 file is in %BUILDDIR%/epub3. 158 | goto end 159 | ) 160 | 161 | if "%1" == "latex" ( 162 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 163 | if errorlevel 1 exit /b 1 164 | echo. 165 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. 166 | goto end 167 | ) 168 | 169 | if "%1" == "latexpdf" ( 170 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 171 | cd %BUILDDIR%/latex 172 | make all-pdf 173 | cd %~dp0 174 | echo. 175 | echo.Build finished; the PDF files are in %BUILDDIR%/latex. 176 | goto end 177 | ) 178 | 179 | if "%1" == "latexpdfja" ( 180 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 181 | cd %BUILDDIR%/latex 182 | make all-pdf-ja 183 | cd %~dp0 184 | echo. 185 | echo.Build finished; the PDF files are in %BUILDDIR%/latex. 186 | goto end 187 | ) 188 | 189 | if "%1" == "text" ( 190 | %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text 191 | if errorlevel 1 exit /b 1 192 | echo. 193 | echo.Build finished. The text files are in %BUILDDIR%/text. 194 | goto end 195 | ) 196 | 197 | if "%1" == "man" ( 198 | %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man 199 | if errorlevel 1 exit /b 1 200 | echo. 201 | echo.Build finished. The manual pages are in %BUILDDIR%/man. 202 | goto end 203 | ) 204 | 205 | if "%1" == "texinfo" ( 206 | %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo 207 | if errorlevel 1 exit /b 1 208 | echo. 209 | echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. 210 | goto end 211 | ) 212 | 213 | if "%1" == "gettext" ( 214 | %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale 215 | if errorlevel 1 exit /b 1 216 | echo. 217 | echo.Build finished. The message catalogs are in %BUILDDIR%/locale. 218 | goto end 219 | ) 220 | 221 | if "%1" == "changes" ( 222 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes 223 | if errorlevel 1 exit /b 1 224 | echo. 225 | echo.The overview file is in %BUILDDIR%/changes. 226 | goto end 227 | ) 228 | 229 | if "%1" == "linkcheck" ( 230 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck 231 | if errorlevel 1 exit /b 1 232 | echo. 233 | echo.Link check complete; look for any errors in the above output ^ 234 | or in %BUILDDIR%/linkcheck/output.txt. 235 | goto end 236 | ) 237 | 238 | if "%1" == "doctest" ( 239 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest 240 | if errorlevel 1 exit /b 1 241 | echo. 242 | echo.Testing of doctests in the sources finished, look at the ^ 243 | results in %BUILDDIR%/doctest/output.txt. 244 | goto end 245 | ) 246 | 247 | if "%1" == "coverage" ( 248 | %SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage 249 | if errorlevel 1 exit /b 1 250 | echo. 251 | echo.Testing of coverage in the sources finished, look at the ^ 252 | results in %BUILDDIR%/coverage/python.txt. 253 | goto end 254 | ) 255 | 256 | if "%1" == "xml" ( 257 | %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml 258 | if errorlevel 1 exit /b 1 259 | echo. 260 | echo.Build finished. The XML files are in %BUILDDIR%/xml. 261 | goto end 262 | ) 263 | 264 | if "%1" == "pseudoxml" ( 265 | %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml 266 | if errorlevel 1 exit /b 1 267 | echo. 268 | echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. 269 | goto end 270 | ) 271 | 272 | :end 273 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = build 9 | 10 | # User-friendly check for sphinx-build 11 | ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) 12 | $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don\'t have Sphinx installed, grab it from http://sphinx-doc.org/) 13 | endif 14 | 15 | # Internal variables. 16 | PAPEROPT_a4 = -D latex_paper_size=a4 17 | PAPEROPT_letter = -D latex_paper_size=letter 18 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source 19 | # the i18n builder cannot share the environment and doctrees with the others 20 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source 21 | 22 | .PHONY: help 23 | help: 24 | @echo "Please use \`make ' where is one of" 25 | @echo " html to make standalone HTML files" 26 | @echo " dirhtml to make HTML files named index.html in directories" 27 | @echo " singlehtml to make a single large HTML file" 28 | @echo " pickle to make pickle files" 29 | @echo " json to make JSON files" 30 | @echo " htmlhelp to make HTML files and a HTML help project" 31 | @echo " qthelp to make HTML files and a qthelp project" 32 | @echo " applehelp to make an Apple Help Book" 33 | @echo " devhelp to make HTML files and a Devhelp project" 34 | @echo " epub to make an epub" 35 | @echo " epub3 to make an epub3" 36 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 37 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 38 | @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" 39 | @echo " text to make text files" 40 | @echo " man to make manual pages" 41 | @echo " texinfo to make Texinfo files" 42 | @echo " info to make Texinfo files and run them through makeinfo" 43 | @echo " gettext to make PO message catalogs" 44 | @echo " changes to make an overview of all changed/added/deprecated items" 45 | @echo " xml to make Docutils-native XML files" 46 | @echo " pseudoxml to make pseudoxml-XML files for display purposes" 47 | @echo " linkcheck to check all external links for integrity" 48 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 49 | @echo " coverage to run coverage check of the documentation (if enabled)" 50 | 51 | .PHONY: clean 52 | clean: 53 | rm -rf $(BUILDDIR)/* 54 | 55 | .PHONY: html 56 | html: 57 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 58 | @echo 59 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 60 | 61 | .PHONY: dirhtml 62 | dirhtml: 63 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 64 | @echo 65 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 66 | 67 | .PHONY: singlehtml 68 | singlehtml: 69 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 70 | @echo 71 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 72 | 73 | .PHONY: pickle 74 | pickle: 75 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 76 | @echo 77 | @echo "Build finished; now you can process the pickle files." 78 | 79 | .PHONY: json 80 | json: 81 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 82 | @echo 83 | @echo "Build finished; now you can process the JSON files." 84 | 85 | .PHONY: htmlhelp 86 | htmlhelp: 87 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 88 | @echo 89 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 90 | ".hhp project file in $(BUILDDIR)/htmlhelp." 91 | 92 | .PHONY: qthelp 93 | qthelp: 94 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 95 | @echo 96 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 97 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 98 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/SciPlot-PyQt.qhcp" 99 | @echo "To view the help file:" 100 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/SciPlot-PyQt.qhc" 101 | 102 | .PHONY: applehelp 103 | applehelp: 104 | $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp 105 | @echo 106 | @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." 107 | @echo "N.B. You won't be able to view it unless you put it in" \ 108 | "~/Library/Documentation/Help or install it in your application" \ 109 | "bundle." 110 | 111 | .PHONY: devhelp 112 | devhelp: 113 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 114 | @echo 115 | @echo "Build finished." 116 | @echo "To view the help file:" 117 | @echo "# mkdir -p $$HOME/.local/share/devhelp/SciPlot-PyQt" 118 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/SciPlot-PyQt" 119 | @echo "# devhelp" 120 | 121 | .PHONY: epub 122 | epub: 123 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 124 | @echo 125 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 126 | 127 | .PHONY: epub3 128 | epub3: 129 | $(SPHINXBUILD) -b epub3 $(ALLSPHINXOPTS) $(BUILDDIR)/epub3 130 | @echo 131 | @echo "Build finished. The epub3 file is in $(BUILDDIR)/epub3." 132 | 133 | .PHONY: latex 134 | latex: 135 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 136 | @echo 137 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 138 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 139 | "(use \`make latexpdf' here to do that automatically)." 140 | 141 | .PHONY: latexpdf 142 | latexpdf: 143 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 144 | @echo "Running LaTeX files through pdflatex..." 145 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 146 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 147 | 148 | .PHONY: latexpdfja 149 | latexpdfja: 150 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 151 | @echo "Running LaTeX files through platex and dvipdfmx..." 152 | $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja 153 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 154 | 155 | .PHONY: text 156 | text: 157 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 158 | @echo 159 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 160 | 161 | .PHONY: man 162 | man: 163 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 164 | @echo 165 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 166 | 167 | .PHONY: texinfo 168 | texinfo: 169 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 170 | @echo 171 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 172 | @echo "Run \`make' in that directory to run these through makeinfo" \ 173 | "(use \`make info' here to do that automatically)." 174 | 175 | .PHONY: info 176 | info: 177 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 178 | @echo "Running Texinfo files through makeinfo..." 179 | make -C $(BUILDDIR)/texinfo info 180 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 181 | 182 | .PHONY: gettext 183 | gettext: 184 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 185 | @echo 186 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 187 | 188 | .PHONY: changes 189 | changes: 190 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 191 | @echo 192 | @echo "The overview file is in $(BUILDDIR)/changes." 193 | 194 | .PHONY: linkcheck 195 | linkcheck: 196 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 197 | @echo 198 | @echo "Link check complete; look for any errors in the above output " \ 199 | "or in $(BUILDDIR)/linkcheck/output.txt." 200 | 201 | .PHONY: doctest 202 | doctest: 203 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 204 | @echo "Testing of doctests in the sources finished, look at the " \ 205 | "results in $(BUILDDIR)/doctest/output.txt." 206 | 207 | .PHONY: coverage 208 | coverage: 209 | $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage 210 | @echo "Testing of coverage in the sources finished, look at the " \ 211 | "results in $(BUILDDIR)/coverage/python.txt." 212 | 213 | .PHONY: xml 214 | xml: 215 | $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml 216 | @echo 217 | @echo "Build finished. The XML files are in $(BUILDDIR)/xml." 218 | 219 | .PHONY: pseudoxml 220 | pseudoxml: 221 | $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml 222 | @echo 223 | @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." 224 | -------------------------------------------------------------------------------- /sciplot/ui/models/images.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Fri Jul 8 14:11:40 2016 4 | 5 | @author: chc 6 | """ 7 | 8 | import matplotlib as _mpl 9 | 10 | from PyQt5.QtWidgets import (QApplication as _QApplication, 11 | QMainWindow as _QMainWindow, 12 | QColorDialog as _QColorDialog, 13 | QCheckBox as _QCheckBox, 14 | QDoubleSpinBox as _QDoubleSpinBox, 15 | QComboBox as _QComboBox, 16 | QLineEdit as _QLineEdit, 17 | QStyledItemDelegate as _QStyledItemDelegate, 18 | QTableView as _QTableView, 19 | QSizePolicy as _QSizePolicy) 20 | 21 | from PyQt5.QtCore import (QAbstractTableModel as _QAbstractTableModel, 22 | QVariant as _QVariant, 23 | QObject as _QObject, 24 | pyqtSignal as _pyqtSignal, 25 | QModelIndex as _QModelIndex, 26 | Qt as _Qt) 27 | 28 | from PyQt5.QtGui import (QPixmap as _QPixmap, 29 | QIcon as _QIcon, 30 | QColor as _QColor) 31 | 32 | from sciplot.utils.mplstyle import MplMarkers, MplLines 33 | 34 | from sciplot.ui.models.abstract import (AbstractTableModelMpl as 35 | _AbstractTableModelMpl, 36 | AbstractEditDelegateMpl as 37 | _AbstractEditDelegateMpl) 38 | 39 | class TableModelImages(_AbstractTableModelMpl): 40 | _HEADERS = ['Cmap', 41 | 'Alpha', 42 | 'Clim Low', 43 | 'Clim High', 44 | 'Label', 45 | 'Colorbar', 46 | 'Delete'] 47 | 48 | _COL_CMAP = _HEADERS.index('Cmap') 49 | _COL_ALPHA = _HEADERS.index('Alpha') 50 | _COL_CLIM_LOW = _HEADERS.index('Clim Low') 51 | _COL_CLIM_HIGH = _HEADERS.index('Clim High') 52 | _COL_CBAR = _HEADERS.index('Colorbar') 53 | _COL_LABEL = _HEADERS.index('Label') 54 | _COL_DELETE = _HEADERS.index('Delete') 55 | 56 | dataDeleted = _pyqtSignal(int, float) 57 | 58 | def __init__(self, parent=None): 59 | 60 | super(_QAbstractTableModel, self).__init__(parent) 61 | self.headers = TableModelImages._HEADERS 62 | self._model_data = [] 63 | 64 | def rowCount(self, parent=_QModelIndex()): 65 | return len(self._model_data) 66 | 67 | def columnCount(self, parent=_QModelIndex()): 68 | return len(self.headers) 69 | 70 | def headerData(self, col, orientation, role): 71 | if orientation == _Qt.Horizontal and role == _Qt.DisplayRole: 72 | return self.headers[col] 73 | return _QVariant() 74 | 75 | def doubleClickCheck(self, index): 76 | col = index.column() 77 | # if col == TableModelImages._COL_CMAP: # CMAP 78 | # self.changeColor(index) 79 | if col == TableModelImages._COL_DELETE: # Delete? 80 | self.deleteData(index) 81 | 82 | def deleteData(self, index): 83 | self.setData(index, True) 84 | 85 | def data(self, index, role=_Qt.DisplayRole): 86 | if not index.isValid() or not 0 <= index.row() < self.rowCount(): 87 | return _QVariant() 88 | 89 | row = index.row() 90 | col = index.column() 91 | 92 | if role == _Qt.DisplayRole: 93 | if col == TableModelImages._COL_CMAP: 94 | return str(self._model_data[row]['cmap_name']) 95 | elif col == TableModelImages._COL_ALPHA: 96 | return str(self._model_data[row]['alpha']) 97 | elif col == TableModelImages._COL_CLIM_LOW: 98 | return str(self._model_data[row]['clim_low']) 99 | elif col == TableModelImages._COL_CLIM_HIGH: 100 | return str(self._model_data[row]['clim_high']) 101 | elif col == TableModelImages._COL_CBAR: 102 | # print('1') 103 | return str(self._model_data[row]['colorbar']) 104 | elif col == TableModelImages._COL_LABEL: 105 | return str(self._model_data[row]['label']) 106 | elif col == TableModelImages._COL_DELETE: 107 | return '' 108 | elif role == _Qt.DecorationRole: 109 | if col == TableModelImages._COL_DELETE: 110 | color = [1, 0, 0] 111 | color_256 = [color[0]*255, color[1]*255, color[2]*255] 112 | qcolor = _QColor(color_256[0], color_256[1], color_256[2]) 113 | pm = _QPixmap(20, 20) 114 | pm.fill(qcolor) 115 | icon = _QIcon(pm) 116 | return icon 117 | else: 118 | return _QVariant() 119 | 120 | def setData(self, index, value, role=_Qt.EditRole): 121 | if role == _Qt.EditRole: 122 | row = index.row() 123 | col = index.column() 124 | 125 | if col == TableModelImages._COL_CMAP: 126 | self._model_data[row]['cmap_name'] = value 127 | elif col == TableModelImages._COL_ALPHA: 128 | self._model_data[row]['alpha'] = float(value) 129 | elif col == TableModelImages._COL_CLIM_LOW: 130 | self._model_data[row]['clim_low'] = float(value) 131 | elif col == TableModelImages._COL_CLIM_HIGH: 132 | self._model_data[row]['clim_high'] = float(value) 133 | elif col == TableModelImages._COL_CBAR: 134 | # print('2') 135 | self._model_data[row]['colorbar'] = bool(value) 136 | elif col == TableModelImages._COL_LABEL: 137 | self._model_data[row]['label'] = value 138 | elif col == TableModelImages._COL_DELETE: 139 | if value: 140 | out = self._model_data.pop(row) 141 | self.layoutChanged.emit() 142 | self.dataDeleted.emit(row, out['id']) 143 | 144 | self.dataChanged.emit(index, index) 145 | 146 | def flags(self, index): 147 | flag = super(_QAbstractTableModel, self).flags(index) 148 | return flag | _Qt.ItemIsEditable 149 | 150 | 151 | class EditDelegateImages(_AbstractEditDelegateMpl): 152 | def createEditor(self, parent, option, index): 153 | col = index.column() 154 | 155 | if col == TableModelImages._COL_ALPHA: 156 | spinBoxSize = _QDoubleSpinBox(parent) 157 | spinBoxSize.setMinimum(0) 158 | spinBoxSize.setMaximum(1) 159 | spinBoxSize.setSingleStep(.1) 160 | return spinBoxSize 161 | # clim_low, clim_high 162 | elif (col == TableModelImages._COL_CLIM_LOW or 163 | col == TableModelImages._COL_CLIM_HIGH): 164 | spinBoxSize = _QDoubleSpinBox(parent) 165 | spinBoxSize.setMinimum(-1e10) 166 | spinBoxSize.setMaximum(1e10) 167 | spinBoxSize.setSingleStep(.5) 168 | return spinBoxSize 169 | elif col == TableModelImages._COL_CBAR: # colorbar 170 | # print('3') 171 | comboBoxTrueFalse = _QComboBox(parent) 172 | comboBoxTrueFalse.addItems(['True','False']) 173 | return comboBoxTrueFalse 174 | elif col == TableModelImages._COL_CMAP: # cmaps 175 | comboBoxCmapNames = _QComboBox(parent) 176 | list_cmaps = list(_mpl.cm.cmap_d.keys()) 177 | list_cmaps.sort() 178 | for cmap_name in list_cmaps: 179 | comboBoxCmapNames.addItem(cmap_name) 180 | return comboBoxCmapNames 181 | elif col == TableModelImages._COL_LABEL: # Label 182 | lineEditLabel = _QLineEdit(parent) 183 | return lineEditLabel 184 | elif col == TableModelImages._COL_DELETE: # Delete? 185 | pass 186 | else: 187 | return _QVariant() 188 | 189 | def setEditorData(self, editor, index): 190 | col = index.column() 191 | item = index.data(_Qt.DisplayRole) 192 | if (col == TableModelImages._COL_ALPHA or 193 | col == TableModelImages._COL_CLIM_LOW or 194 | col == TableModelImages._COL_CLIM_HIGH): 195 | item_float = float(item) 196 | editor.setValue(item_float) 197 | elif col == TableModelImages._COL_LABEL: # Label 198 | editor.setText(item) 199 | elif (col == TableModelImages._COL_CBAR or 200 | col == TableModelImages._COL_CMAP): # colorbar or cmap 201 | loc = editor.findText(item) 202 | if loc < 0: 203 | loc = 0 204 | editor.setCurrentIndex(loc) 205 | else: 206 | pass 207 | 208 | def setModelData(self, editor, model, index): 209 | col = index.column() 210 | 211 | # Alpha or clim's 212 | if (col == TableModelImages._COL_ALPHA or 213 | col == TableModelImages._COL_CLIM_LOW or 214 | col == TableModelImages._COL_CLIM_HIGH): 215 | value = editor.value() 216 | model.setData(index, value) 217 | elif col == TableModelImages._COL_CMAP: # cmap 218 | list_cmaps = list(_mpl.cm.cmap_d.keys()) 219 | list_cmaps.sort() 220 | idx = editor.currentIndex() 221 | cmap_name = list_cmaps[idx] 222 | model.setData(index, cmap_name) 223 | elif col == TableModelImages._COL_CBAR: # Colorbar 224 | # print('5') 225 | cbar = editor.currentText() == 'True' 226 | model.setData(index, cbar) 227 | elif col == TableModelImages._COL_LABEL: # Label 228 | label = editor.text() 229 | model.setData(index, label) 230 | -------------------------------------------------------------------------------- /sciplot/ui/qt5-creator/Old/ui_Plotter_dialog.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Dialog 4 | 5 | 6 | 7 | 0 8 | 0 9 | 1208 10 | 892 11 | 12 | 13 | 14 | Dialog 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | Qt::Vertical 23 | 24 | 25 | 26 | 20 27 | 40 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 0 39 | 0 40 | 41 | 42 | 43 | 44 | 200 45 | 16777215 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 0 57 | 0 58 | 59 | 60 | 61 | 62 | 16777215 63 | 16777215 64 | 65 | 66 | 67 | 0 68 | 69 | 70 | 71 | 72 | 0 73 | 0 74 | 176 75 | 358 76 | 77 | 78 | 79 | General Parameters 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | Title 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | X Label 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | Y Label 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | Aspect Ratio 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | auto 126 | 127 | 128 | 129 | 130 | equal 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | Qt::Vertical 141 | 142 | 143 | 144 | 20 145 | 40 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 0 156 | 0 157 | 149 158 | 248 159 | 160 | 161 | 162 | Axis Parameters 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | Axis Visibility 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | true 181 | 182 | 183 | 184 | 185 | 186 | 187 | X Limits 188 | 189 | 190 | 191 | 192 | 193 | 194 | 10 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | Y Limits 208 | 209 | 210 | 211 | 212 | 213 | 214 | 10 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | Scaling 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | auto 236 | 237 | 238 | 239 | 240 | equal 241 | 242 | 243 | 244 | 245 | tight 246 | 247 | 248 | 249 | 250 | image 251 | 252 | 253 | 254 | 255 | square 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | Qt::Vertical 264 | 265 | 266 | 267 | 20 268 | 40 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | Qt::Vertical 283 | 284 | 285 | 286 | 20 287 | 40 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | -------------------------------------------------------------------------------- /sciplot/ui/models/fillbetween.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | This entire model is for fill-between type of plots, which actually uses 4 | polycollections. In the future, this may become a polycollections model, 5 | but for now K.I.S.S. 6 | 7 | Created on Thu Jul 7 15:25:08 2016 8 | 9 | @author: chc 10 | """ 11 | 12 | from PyQt5.QtWidgets import (QApplication as _QApplication, 13 | QMainWindow as _QMainWindow, 14 | QColorDialog as _QColorDialog, 15 | QDoubleSpinBox as _QDoubleSpinBox, 16 | QComboBox as _QComboBox, 17 | QLineEdit as _QLineEdit, 18 | QStyledItemDelegate as _QStyledItemDelegate, 19 | QTableView as _QTableView, 20 | QSizePolicy as _QSizePolicy) 21 | 22 | from PyQt5.QtCore import (QAbstractTableModel as _QAbstractTableModel, 23 | QVariant as _QVariant, 24 | QObject as _QObject, 25 | pyqtSignal as _pyqtSignal, 26 | QModelIndex as _QModelIndex, 27 | Qt as _Qt) 28 | 29 | from PyQt5.QtGui import (QPixmap as _QPixmap, 30 | QIcon as _QIcon, 31 | QColor as _QColor) 32 | 33 | from sciplot.utils.mplstyle import MplMarkers, MplLines 34 | from sciplot.utils.general import round_list 35 | 36 | from sciplot.ui.models.abstract import (AbstractTableModelMpl as 37 | _AbstractTableModelMpl, 38 | AbstractEditDelegateMpl as 39 | _AbstractEditDelegateMpl) 40 | 41 | class TableModelFillBetween(_AbstractTableModelMpl): 42 | _HEADERS = ['Facecolor', 43 | 'Alpha', 44 | 'Edgecolor', 45 | 'LineWidth', 46 | 'Label', 47 | 'Delete'] 48 | 49 | _COL_FACECOLOR = _HEADERS.index('Facecolor') 50 | _COL_ALPHA = _HEADERS.index('Alpha') 51 | _COL_EDGECOLOR = _HEADERS.index('Edgecolor') 52 | _COL_LINEWIDTH = _HEADERS.index('LineWidth') 53 | _COL_LABEL = _HEADERS.index('Label') 54 | _COL_DELETE = _HEADERS.index('Delete') 55 | 56 | dataDeleted = _pyqtSignal(int, float) 57 | 58 | def __init__(self, parent=None): 59 | 60 | super(_QAbstractTableModel, self).__init__(parent) 61 | self.headers = TableModelFillBetween._HEADERS 62 | self._model_data = [] 63 | 64 | def rowCount(self, parent=_QModelIndex()): 65 | """ 66 | Return row count of table view 67 | """ 68 | return len(self._model_data) 69 | 70 | def columnCount(self, parent=_QModelIndex()): 71 | """ 72 | Return col count of table view 73 | """ 74 | return len(self.headers) 75 | 76 | def headerData(self, col, orientation, role): 77 | """ 78 | Basic horizontal header with no special role 79 | """ 80 | if orientation == _Qt.Horizontal and role == _Qt.DisplayRole: 81 | return self.headers[col] 82 | return _QVariant() 83 | 84 | def doubleClickCheck(self, index): 85 | """ 86 | Double-clicking certain columns has special effects. In this case, 87 | the color change columns and the delete column 88 | """ 89 | col = index.column() 90 | if (col == TableModelFillBetween._COL_FACECOLOR or 91 | col == TableModelFillBetween._COL_EDGECOLOR): 92 | # Face- or EdgeColor 93 | self.changeColor(index) 94 | elif col == TableModelFillBetween._COL_DELETE: # Delete? 95 | self.deleteData(index) 96 | 97 | def deleteData(self, index): 98 | self.setData(index, True) 99 | 100 | def changeColor(self, index): 101 | row = index.row() 102 | col = index.column() 103 | 104 | if col == TableModelFillBetween._COL_FACECOLOR: 105 | color = self._model_data[row]['facecolor'] 106 | else: 107 | color = self._model_data[row]['edgecolor'] 108 | 109 | # from [0,1] -> [0,255] color scale 110 | color_256 = [color[0]*255, color[1]*255, color[2]*255] 111 | qcolor = _QColor(color_256[0], color_256[1], color_256[2]) 112 | 113 | result = _QColorDialog.getColor(qcolor) 114 | if _QColor.isValid(result): 115 | self.setData(index, result.getRgb()) 116 | else: 117 | return None 118 | 119 | def data(self, index, role=_Qt.DisplayRole): 120 | if not index.isValid() or not 0 <= index.row() < self.rowCount(): 121 | return _QVariant() 122 | 123 | row = index.row() 124 | col = index.column() 125 | 126 | if role == _Qt.DisplayRole: 127 | if col == TableModelFillBetween._COL_FACECOLOR: 128 | return str(round_list(self._model_data[row]['facecolor'])) 129 | elif col == TableModelFillBetween._COL_ALPHA: 130 | return str(self._model_data[row]['alpha']) 131 | elif col == TableModelFillBetween._COL_EDGECOLOR: 132 | return str(round_list(self._model_data[row]['edgecolor'])) 133 | elif col == TableModelFillBetween._COL_LINEWIDTH: 134 | return str(self._model_data[row]['linewidth']) 135 | elif col == TableModelFillBetween._COL_LABEL: 136 | return str(self._model_data[row]['label']) 137 | elif col == TableModelFillBetween._COL_DELETE: 138 | return '' 139 | elif role == _Qt.DecorationRole: 140 | if (col == TableModelFillBetween._COL_FACECOLOR or 141 | col == TableModelFillBetween._COL_EDGECOLOR): 142 | if col == TableModelFillBetween._COL_FACECOLOR: 143 | color = self._model_data[row]['facecolor'] 144 | elif col == TableModelFillBetween._COL_EDGECOLOR: 145 | color = self._model_data[row]['edgecolor'] 146 | 147 | color_256 = [color[0]*255, color[1]*255, color[2]*255] 148 | qcolor = _QColor(color_256[0], color_256[1], color_256[2]) 149 | pm = _QPixmap(20, 20) 150 | pm.fill(qcolor) 151 | icon = _QIcon(pm) 152 | return icon 153 | elif col == TableModelFillBetween._COL_DELETE: 154 | color = [1, 0, 0] 155 | color_256 = [color[0]*255, color[1]*255, color[2]*255] 156 | qcolor = _QColor(color_256[0], color_256[1], color_256[2]) 157 | pm = _QPixmap(20, 20) 158 | pm.fill(qcolor) 159 | icon = _QIcon(pm) 160 | return icon 161 | else: 162 | return _QVariant() 163 | else: 164 | return _QVariant() 165 | 166 | def setData(self, index, value, role=_Qt.EditRole): 167 | if role == _Qt.EditRole: 168 | row = index.row() 169 | col = index.column() 170 | 171 | if col == TableModelFillBetween._COL_FACECOLOR: 172 | color_255 = value[0:-1] 173 | color = [round(color_255[0]/255, 2), 174 | round(color_255[1]/255, 2), 175 | round(color_255[2]/255, 2)] 176 | self._model_data[row]['facecolor'] = color 177 | elif col == TableModelFillBetween._COL_EDGECOLOR: 178 | color_255 = value[0:-1] 179 | color = [round(color_255[0]/255, 2), 180 | round(color_255[1]/255, 2), 181 | round(color_255[2]/255, 2)] 182 | self._model_data[row]['edgecolor'] = color 183 | elif col == TableModelFillBetween._COL_ALPHA: 184 | self._model_data[row]['alpha'] = float(value) 185 | elif col == TableModelFillBetween._COL_LINEWIDTH: 186 | self._model_data[row]['linewidth'] = float(value) 187 | elif col == TableModelFillBetween._COL_LABEL: 188 | self._model_data[row]['label'] = value 189 | elif col == TableModelFillBetween._COL_DELETE: 190 | if value: 191 | out = self._model_data.pop(row) 192 | self.layoutChanged.emit() 193 | self.dataDeleted.emit(row, out['id']) 194 | 195 | self.dataChanged.emit(index, index) 196 | 197 | def flags(self, index): 198 | flag = super(_QAbstractTableModel, self).flags(index) 199 | return flag | _Qt.ItemIsEditable 200 | 201 | 202 | class EditDelegateFillBetween(_AbstractEditDelegateMpl): 203 | def createEditor(self, parent, option, index): 204 | col = index.column() 205 | if (col == TableModelFillBetween._COL_FACECOLOR or 206 | col == TableModelFillBetween._COL_EDGECOLOR): 207 | # Color handled by doubleClicked SIGNAL 208 | pass 209 | # LineWidth or Marker size or Alpha 210 | elif (col == TableModelFillBetween._COL_LINEWIDTH or 211 | col == TableModelFillBetween._COL_ALPHA): 212 | spinBoxSize = _QDoubleSpinBox(parent) 213 | spinBoxSize.setMinimum(0) 214 | spinBoxSize.setMaximum(20) 215 | spinBoxSize.setSingleStep(.5) 216 | return spinBoxSize 217 | elif col == TableModelFillBetween._COL_LABEL: # Label 218 | lineEditLabel = _QLineEdit(parent) 219 | return lineEditLabel 220 | elif col == TableModelFillBetween._COL_DELETE: # Delete? 221 | pass 222 | else: 223 | return _QVariant() 224 | 225 | def setEditorData(self, editor, index): 226 | col = index.column() 227 | item = index.data(_Qt.DisplayRole) 228 | if (col == TableModelFillBetween._COL_FACECOLOR or 229 | col == TableModelFillBetween._COL_EDGECOLOR): 230 | # Color handled by doubleClicked SIGNAL 231 | pass 232 | # LineWidth or MarkerSize or Alpha 233 | elif (col == TableModelFillBetween._COL_LINEWIDTH or 234 | col == TableModelFillBetween._COL_ALPHA): 235 | item_float = float(item) 236 | editor.setValue(item_float) 237 | elif col == TableModelFillBetween._COL_LABEL: # Label 238 | editor.setText(item) 239 | else: 240 | pass 241 | 242 | def setModelData(self, editor, model, index): 243 | col = index.column() 244 | if (col == TableModelFillBetween._COL_FACECOLOR or 245 | col == TableModelFillBetween._COL_EDGECOLOR): 246 | # Color handled by doubleClicked SIGNAL 247 | pass 248 | # LineWidth or MarkerSize or Alpha 249 | elif (col == TableModelFillBetween._COL_LINEWIDTH or 250 | col == TableModelFillBetween._COL_ALPHA): 251 | value = editor.value() 252 | model.setData(index, value) 253 | elif col == TableModelFillBetween._COL_LABEL: # Label 254 | label = editor.text() 255 | model.setData(index, label) 256 | -------------------------------------------------------------------------------- /sciplot/ui/models/bars.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | 4 | ModelViewDelegate for an MPL Line object 5 | 6 | Created on Thu Jul 7 10:01:23 2016 7 | 8 | @author: chc 9 | """ 10 | 11 | from PyQt5.QtWidgets import (QApplication as _QApplication, 12 | QMainWindow as _QMainWindow, 13 | QColorDialog as _QColorDialog, 14 | QDoubleSpinBox as _QDoubleSpinBox, 15 | QComboBox as _QComboBox, 16 | QLineEdit as _QLineEdit, 17 | QStyledItemDelegate as _QStyledItemDelegate, 18 | QTableView as _QTableView, 19 | QSizePolicy as _QSizePolicy) 20 | 21 | from PyQt5.QtCore import (QAbstractTableModel as _QAbstractTableModel, 22 | QVariant as _QVariant, 23 | QObject as _QObject, 24 | pyqtSignal as _pyqtSignal, 25 | QModelIndex as _QModelIndex, 26 | Qt as _Qt) 27 | 28 | from PyQt5.QtGui import (QPixmap as _QPixmap, 29 | QIcon as _QIcon, 30 | QColor as _QColor) 31 | 32 | 33 | from sciplot.ui.models.abstract import (AbstractTableModelMpl as 34 | _AbstractTableModelMpl, 35 | AbstractEditDelegateMpl as 36 | _AbstractEditDelegateMpl) 37 | from sciplot.utils.general import round_list 38 | 39 | class TableModelBars(_AbstractTableModelMpl): 40 | _HEADERS = ['Facecolor', 41 | 'Alpha', 42 | 'Edgecolor', 43 | 'Line Width', 44 | 'Width Factor', 45 | 'Label', 46 | 'Delete'] 47 | 48 | _COL_FACECOLOR = _HEADERS.index('Facecolor') 49 | _COL_ALPHA = _HEADERS.index('Alpha') 50 | _COL_EDGECOLOR = _HEADERS.index('Edgecolor') 51 | _COL_LINEWIDTH = _HEADERS.index('Line Width') 52 | _COL_WIDTH_FACTOR = _HEADERS.index('Width Factor') 53 | _COL_LABEL = _HEADERS.index('Label') 54 | _COL_DELETE = _HEADERS.index('Delete') 55 | 56 | # Emit row, plot ID 57 | dataDeleted = _pyqtSignal(int, float) 58 | 59 | def __init__(self, parent=None): 60 | 61 | super(_QAbstractTableModel, self).__init__(parent) 62 | self.headers = TableModelBars._HEADERS 63 | self._model_data = [] 64 | 65 | def rowCount(self, parent=_QModelIndex()): 66 | return len(self._model_data) 67 | 68 | def columnCount(self, parent=_QModelIndex()): 69 | return len(self.headers) 70 | 71 | def headerData(self, col, orientation, role): 72 | if orientation == _Qt.Horizontal and role == _Qt.DisplayRole: 73 | return self.headers[col] 74 | return _QVariant() 75 | 76 | def doubleClickCheck(self, index): 77 | col = index.column() 78 | if (col == TableModelBars._COL_FACECOLOR or 79 | col == TableModelBars._COL_EDGECOLOR): # Colors 80 | self.changeColor(index) 81 | elif col == TableModelBars._COL_DELETE: # Delete? 82 | self.deleteData(index) 83 | 84 | def deleteData(self, index): 85 | self.setData(index, True) 86 | 87 | def changeColor(self, index): 88 | row = index.row() 89 | col = index.column() 90 | 91 | if col == TableModelBars._COL_FACECOLOR: 92 | color = self._model_data[row]['facecolor'] 93 | elif col == TableModelBars._COL_EDGECOLOR: 94 | color = self._model_data[row]['edgecolor'] 95 | color_256 = [color[0]*255, color[1]*255, color[2]*255] 96 | qcolor = _QColor(color_256[0], color_256[1], color_256[2]) 97 | 98 | result = _QColorDialog.getColor(qcolor) 99 | if _QColor.isValid(result): 100 | self.setData(index, result.getRgb()) 101 | else: 102 | return None 103 | 104 | def data(self, index, role=_Qt.DisplayRole): 105 | if not index.isValid() or not 0 <= index.row() < self.rowCount(): 106 | return _QVariant() 107 | 108 | row = index.row() 109 | col = index.column() 110 | 111 | if role == _Qt.DisplayRole: 112 | if col == TableModelBars._COL_FACECOLOR: 113 | return str(round_list(self._model_data[row]['facecolor'])) 114 | elif col == TableModelBars._COL_ALPHA: 115 | return str(self._model_data[row]['alpha']) 116 | elif col == TableModelBars._COL_EDGECOLOR: 117 | return str(round_list(self._model_data[row]['edgecolor'])) 118 | elif col == TableModelBars._COL_LINEWIDTH: 119 | return str(self._model_data[row]['linewidth']) 120 | elif col == TableModelBars._COL_WIDTH_FACTOR: 121 | return str(self._model_data[row]['width_factor']) 122 | elif col == TableModelBars._COL_LABEL: 123 | return str(self._model_data[row]['label']) 124 | elif col == TableModelBars._COL_DELETE: 125 | return '' 126 | elif role == _Qt.DecorationRole: 127 | if col == TableModelBars._COL_FACECOLOR: 128 | color = self._model_data[row]['facecolor'] 129 | elif col == TableModelBars._COL_EDGECOLOR: 130 | color = self._model_data[row]['edgecolor'] 131 | if (col == TableModelBars._COL_FACECOLOR or 132 | col == TableModelBars._COL_EDGECOLOR): 133 | color_256 = [color[0]*255, color[1]*255, color[2]*255] 134 | qcolor = _QColor(color_256[0], color_256[1], color_256[2]) 135 | pm = _QPixmap(20, 20) 136 | pm.fill(qcolor) 137 | icon = _QIcon(pm) 138 | return icon 139 | elif col == TableModelBars._COL_DELETE: 140 | color = [1, 0, 0] 141 | color_256 = [color[0]*255, color[1]*255, color[2]*255] 142 | qcolor = _QColor(color_256[0], color_256[1], color_256[2]) 143 | pm = _QPixmap(20, 20) 144 | pm.fill(qcolor) 145 | icon = _QIcon(pm) 146 | return icon 147 | else: 148 | pass 149 | else: 150 | return _QVariant() 151 | 152 | def setData(self, index, value, role=_Qt.EditRole): 153 | if role == _Qt.EditRole: 154 | row = index.row() 155 | col = index.column() 156 | 157 | if (col == TableModelBars._COL_FACECOLOR or 158 | col == TableModelBars._COL_EDGECOLOR): 159 | color_255 = value[0:-1] 160 | color = [round(color_255[0]/255, 2), 161 | round(color_255[1]/255, 2), 162 | round(color_255[2]/255, 2)] 163 | if col == TableModelBars._COL_FACECOLOR: 164 | self._model_data[row]['facecolor'] = color 165 | if col == TableModelBars._COL_EDGECOLOR: 166 | self._model_data[row]['edgecolor'] = color 167 | elif col == TableModelBars._COL_ALPHA: 168 | self._model_data[row]['alpha'] = float(value) 169 | elif col == TableModelBars._COL_LINEWIDTH: 170 | self._model_data[row]['linewidth'] = float(value) 171 | elif col == TableModelBars._COL_WIDTH_FACTOR: 172 | self._model_data[row]['width_factor'] = float(value) 173 | elif col == TableModelBars._COL_LABEL: 174 | self._model_data[row]['label'] = value 175 | elif col == TableModelBars._COL_DELETE: 176 | if value: 177 | out = self._model_data.pop(row) 178 | self.layoutChanged.emit() 179 | self.dataDeleted.emit(row, out['id']) 180 | 181 | self.dataChanged.emit(index, index) 182 | 183 | def flags(self, index): 184 | flag = super(_QAbstractTableModel, self).flags(index) 185 | return flag | _Qt.ItemIsEditable 186 | 187 | 188 | class EditDelegateBars(_AbstractEditDelegateMpl): 189 | def createEditor(self, parent, option, index): 190 | col = index.column() 191 | if col == TableModelBars._COL_FACECOLOR: 192 | # Color handled by doubleClicked SIGNAL 193 | pass 194 | elif col == TableModelBars._COL_EDGECOLOR: 195 | # Color handled by doubleClicked SIGNAL 196 | pass 197 | # Alpha 198 | elif col == TableModelBars._COL_ALPHA: 199 | spinBoxSize = _QDoubleSpinBox(parent) 200 | spinBoxSize.setMinimum(0) 201 | spinBoxSize.setMaximum(1) 202 | spinBoxSize.setSingleStep(.1) 203 | return spinBoxSize 204 | # Width Fraction 205 | elif col == TableModelBars._COL_WIDTH_FACTOR: 206 | spinBoxSize = _QDoubleSpinBox(parent) 207 | spinBoxSize.setMinimum(0) 208 | spinBoxSize.setMaximum(1000000000) 209 | spinBoxSize.setSingleStep(.1) 210 | return spinBoxSize 211 | 212 | # LineWidth 213 | elif col == TableModelBars._COL_LINEWIDTH: 214 | spinBoxSize = _QDoubleSpinBox(parent) 215 | spinBoxSize.setMinimum(0) 216 | spinBoxSize.setMaximum(20) 217 | spinBoxSize.setSingleStep(.5) 218 | return spinBoxSize 219 | elif col == TableModelBars._COL_LABEL: # Label 220 | lineEditLabel = _QLineEdit(parent) 221 | return lineEditLabel 222 | elif col == TableModelBars._COL_DELETE: # Delete? 223 | pass 224 | else: 225 | return _QVariant() 226 | 227 | def setEditorData(self, editor, index): 228 | col = index.column() 229 | item = index.data(_Qt.DisplayRole) 230 | if (col == TableModelBars._COL_FACECOLOR or 231 | col == TableModelBars._COL_EDGECOLOR): 232 | # Color handled by doubleClick SIGNAL 233 | pass 234 | # LineWidth or MarkerSize or Alpha 235 | elif (col == TableModelBars._COL_LINEWIDTH or 236 | col == TableModelBars._COL_ALPHA or 237 | col == TableModelBars._COL_WIDTH_FACTOR): 238 | item_float = float(item) 239 | editor.setValue(item_float) 240 | elif col == TableModelBars._COL_LABEL: # Label 241 | editor.setText(item) 242 | else: 243 | pass 244 | 245 | def setModelData(self, editor, model, index): 246 | col = index.column() 247 | if col == TableModelBars._COL_FACECOLOR: 248 | # Color handled by doubleClick SIGNAL 249 | pass 250 | elif col == TableModelBars._COL_EDGECOLOR: 251 | # Color handled by doubleClick SIGNAL 252 | pass 253 | # LineWidth or MarkerSize or Alpha 254 | elif (col == TableModelBars._COL_LINEWIDTH or 255 | col == TableModelBars._COL_ALPHA or 256 | col == TableModelBars._COL_WIDTH_FACTOR): 257 | value = editor.value() 258 | model.setData(index, value) 259 | elif col == TableModelBars._COL_LABEL: # Label 260 | label = editor.text() 261 | model.setData(index, label) 262 | -------------------------------------------------------------------------------- /docs/source/conf.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # SciPlot-PyQt documentation build configuration file, created by 5 | # sphinx-quickstart on Wed Jul 20 23:57:50 2016. 6 | # 7 | # This file is execfile()d with the current directory set to its 8 | # containing dir. 9 | # 10 | # Note that not all possible configuration values are present in this 11 | # autogenerated file. 12 | # 13 | # All configuration values have a default; values that are commented out 14 | # serve to show the default. 15 | 16 | import sys 17 | import os 18 | import pkg_resources # part of setuptools 19 | 20 | sys.path.insert(0, os.path.abspath('../..')) 21 | # If extensions (or modules to document with autodoc) are in another directory, 22 | # add these directories to sys.path here. If the directory is relative to the 23 | # documentation root, use os.path.abspath to make it absolute, like shown here. 24 | #sys.path.insert(0, os.path.abspath('.')) 25 | 26 | # -- General configuration ------------------------------------------------ 27 | 28 | # If your documentation needs a minimal Sphinx version, state it here. 29 | #needs_sphinx = '1.0' 30 | 31 | # Add any Sphinx extension module names here, as strings. They can be 32 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 33 | # ones. 34 | extensions = [ 35 | 'sphinx.ext.autodoc', 36 | 'sphinx.ext.doctest', 37 | 'sphinx.ext.intersphinx', 38 | 'sphinx.ext.todo', 39 | 'sphinx.ext.coverage', 40 | 'sphinx.ext.mathjax', 41 | 'sphinx.ext.viewcode', 42 | 'sphinx.ext.githubpages', 43 | 'numpydoc' 44 | ] 45 | numpydoc_show_class_members = False 46 | autodoc_default_flags = ['private-members'] 47 | # Add any paths that contain templates here, relative to this directory. 48 | templates_path = ['_templates'] 49 | 50 | # The suffix(es) of source filenames. 51 | # You can specify multiple suffix as a list of string: 52 | # source_suffix = ['.rst', '.md'] 53 | source_suffix = '.rst' 54 | 55 | # The encoding of source files. 56 | #source_encoding = 'utf-8-sig' 57 | 58 | # The master toctree document. 59 | master_doc = 'index' 60 | 61 | # General information about the project. 62 | project = 'SciPlot-PyQt' 63 | # copyright = '2016, 2017, Charles H Camp Jr' 64 | author = 'Charles H. Camp Jr.' 65 | 66 | # The version info for the project you're documenting, acts as replacement for 67 | # |version| and |release|, also used in various other places throughout the 68 | # built documents. 69 | # 70 | # The short X.Y version. 71 | version = pkg_resources.require("sciplot-pyqt")[0].version 72 | print('Retrieved version number: {}'.format(version)) 73 | 74 | # The full version, including alpha/beta/rc tags. 75 | release = version 76 | 77 | # The language for content autogenerated by Sphinx. Refer to documentation 78 | # for a list of supported languages. 79 | # 80 | # This is also used if you do content translation via gettext catalogs. 81 | # Usually you set "language" from the command line for these cases. 82 | language = None 83 | 84 | # There are two options for replacing |today|: either, you set today to some 85 | # non-false value, then it is used: 86 | #today = '' 87 | # Else, today_fmt is used as the format for a strftime call. 88 | #today_fmt = '%B %d, %Y' 89 | 90 | # List of patterns, relative to source directory, that match files and 91 | # directories to ignore when looking for source files. 92 | # This patterns also effect to html_static_path and html_extra_path 93 | exclude_patterns = [] 94 | 95 | # The reST default role (used for this markup: `text`) to use for all 96 | # documents. 97 | #default_role = None 98 | 99 | # If true, '()' will be appended to :func: etc. cross-reference text. 100 | #add_function_parentheses = True 101 | 102 | # If true, the current module name will be prepended to all description 103 | # unit titles (such as .. function::). 104 | #add_module_names = True 105 | 106 | # If true, sectionauthor and moduleauthor directives will be shown in the 107 | # output. They are ignored by default. 108 | #show_authors = False 109 | 110 | # The name of the Pygments (syntax highlighting) style to use. 111 | pygments_style = 'sphinx' 112 | 113 | # A list of ignored prefixes for module index sorting. 114 | #modindex_common_prefix = [] 115 | 116 | # If true, keep warnings as "system message" paragraphs in the built documents. 117 | #keep_warnings = False 118 | 119 | # If true, `todo` and `todoList` produce output, else they produce nothing. 120 | todo_include_todos = True 121 | 122 | 123 | # -- Options for HTML output ---------------------------------------------- 124 | 125 | # The theme to use for HTML and HTML Help pages. See the documentation for 126 | # a list of builtin themes. 127 | html_theme = 'sphinx_rtd_theme' 128 | 129 | # Theme options are theme-specific and customize the look and feel of a theme 130 | # further. For a list of options available for each theme, see the 131 | # documentation. 132 | #html_theme_options = {} 133 | 134 | # Add any paths that contain custom themes here, relative to this directory. 135 | #html_theme_path = [] 136 | 137 | # The name for this set of Sphinx documents. 138 | # " v documentation" by default. 139 | #html_title = 'SciPlot-PyQt v16.07' 140 | 141 | # A shorter title for the navigation bar. Default is the same as html_title. 142 | #html_short_title = None 143 | 144 | # The name of an image file (relative to this directory) to place at the top 145 | # of the sidebar. 146 | #html_logo = None 147 | 148 | # The name of an image file (relative to this directory) to use as a favicon of 149 | # the docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 150 | # pixels large. 151 | #html_favicon = None 152 | 153 | # Add any paths that contain custom static files (such as style sheets) here, 154 | # relative to this directory. They are copied after the builtin static files, 155 | # so a file named "default.css" will overwrite the builtin "default.css". 156 | html_static_path = ['_static'] 157 | 158 | # Add any extra paths that contain custom files (such as robots.txt or 159 | # .htaccess) here, relative to this directory. These files are copied 160 | # directly to the root of the documentation. 161 | #html_extra_path = [] 162 | 163 | # If not None, a 'Last updated on:' timestamp is inserted at every page 164 | # bottom, using the given strftime format. 165 | # The empty string is equivalent to '%b %d, %Y'. 166 | #html_last_updated_fmt = None 167 | 168 | # If true, SmartyPants will be used to convert quotes and dashes to 169 | # typographically correct entities. 170 | #html_use_smartypants = True 171 | 172 | # Custom sidebar templates, maps document names to template names. 173 | #html_sidebars = {} 174 | 175 | # Additional templates that should be rendered to pages, maps page names to 176 | # template names. 177 | #html_additional_pages = {} 178 | 179 | # If false, no module index is generated. 180 | #html_domain_indices = True 181 | 182 | # If false, no index is generated. 183 | #html_use_index = True 184 | 185 | # If true, the index is split into individual pages for each letter. 186 | #html_split_index = False 187 | 188 | # If true, links to the reST sources are added to the pages. 189 | #html_show_sourcelink = True 190 | 191 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 192 | #html_show_sphinx = True 193 | 194 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 195 | #html_show_copyright = True 196 | 197 | # If true, an OpenSearch description file will be output, and all pages will 198 | # contain a tag referring to it. The value of this option must be the 199 | # base URL from which the finished HTML is served. 200 | #html_use_opensearch = '' 201 | 202 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 203 | #html_file_suffix = None 204 | 205 | # Language to be used for generating the HTML full-text search index. 206 | # Sphinx supports the following languages: 207 | # 'da', 'de', 'en', 'es', 'fi', 'fr', 'h', 'it', 'ja' 208 | # 'nl', 'no', 'pt', 'ro', 'r', 'sv', 'tr', 'zh' 209 | #html_search_language = 'en' 210 | 211 | # A dictionary with options for the search language support, empty by default. 212 | # 'ja' uses this config value. 213 | # 'zh' user can custom change `jieba` dictionary path. 214 | #html_search_options = {'type': 'default'} 215 | 216 | # The name of a javascript file (relative to the configuration directory) that 217 | # implements a search results scorer. If empty, the default will be used. 218 | #html_search_scorer = 'scorer.js' 219 | 220 | # Output file base name for HTML help builder. 221 | htmlhelp_basename = 'SciPlot-PyQtdoc' 222 | 223 | # -- Options for LaTeX output --------------------------------------------- 224 | 225 | latex_elements = { 226 | # The paper size ('letterpaper' or 'a4paper'). 227 | #'papersize': 'letterpaper', 228 | 229 | # The font size ('10pt', '11pt' or '12pt'). 230 | #'pointsize': '10pt', 231 | 232 | # Additional stuff for the LaTeX preamble. 233 | #'preamble': '', 234 | 235 | # Latex figure (float) alignment 236 | #'figure_align': 'htbp', 237 | } 238 | 239 | # Grouping the document tree into LaTeX files. List of tuples 240 | # (source start file, target name, title, 241 | # author, documentclass [howto, manual, or own class]). 242 | latex_documents = [ 243 | (master_doc, 'SciPlot-PyQt.tex', 'SciPlot-PyQt Documentation', 244 | 'Charles H. Camp Jr.', 'manual'), 245 | ] 246 | 247 | # The name of an image file (relative to this directory) to place at the top of 248 | # the title page. 249 | #latex_logo = None 250 | 251 | # For "manual" documents, if this is true, then toplevel headings are parts, 252 | # not chapters. 253 | #latex_use_parts = False 254 | 255 | # If true, show page references after internal links. 256 | #latex_show_pagerefs = False 257 | 258 | # If true, show URL addresses after external links. 259 | #latex_show_urls = False 260 | 261 | # Documents to append as an appendix to all manuals. 262 | #latex_appendices = [] 263 | 264 | # If false, no module index is generated. 265 | #latex_domain_indices = True 266 | 267 | 268 | # -- Options for manual page output --------------------------------------- 269 | 270 | # One entry per manual page. List of tuples 271 | # (source start file, name, description, authors, manual section). 272 | man_pages = [ 273 | (master_doc, 'sciplot-pyqt', 'SciPlot-PyQt Documentation', 274 | [author], 1) 275 | ] 276 | 277 | # If true, show URL addresses after external links. 278 | #man_show_urls = False 279 | 280 | 281 | # -- Options for Texinfo output ------------------------------------------- 282 | 283 | # Grouping the document tree into Texinfo files. List of tuples 284 | # (source start file, target name, title, author, 285 | # dir menu entry, description, category) 286 | texinfo_documents = [ 287 | (master_doc, 'SciPlot-PyQt', 'SciPlot-PyQt Documentation', 288 | author, 'SciPlot-PyQt', 'One line description of project.', 289 | 'Miscellaneous'), 290 | ] 291 | 292 | # Documents to append as an appendix to all manuals. 293 | #texinfo_appendices = [] 294 | 295 | # If false, no module index is generated. 296 | #texinfo_domain_indices = True 297 | 298 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 299 | #texinfo_show_urls = 'footnote' 300 | 301 | # If true, do not generate a @detailmenu in the "Top" node's menu. 302 | #texinfo_no_detailmenu = False 303 | 304 | 305 | # Example configuration for intersphinx: refer to the Python standard library. 306 | intersphinx_mapping = {'python' : ('https://docs.python.org/3.6/', None), 'pyqt' : ('http://pyqt.sourceforge.net/Docs/PyQt5/', None)} 307 | 308 | -------------------------------------------------------------------------------- /sciplot/ui/models/lines.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | 4 | ModelViewDelegate for an MPL Line object 5 | 6 | Created on Thu Jul 7 10:01:23 2016 7 | 8 | @author: chc 9 | """ 10 | 11 | from PyQt5.QtWidgets import (QApplication as _QApplication, 12 | QMainWindow as _QMainWindow, 13 | QColorDialog as _QColorDialog, 14 | QDoubleSpinBox as _QDoubleSpinBox, 15 | QComboBox as _QComboBox, 16 | QLineEdit as _QLineEdit, 17 | QStyledItemDelegate as _QStyledItemDelegate, 18 | QTableView as _QTableView, 19 | QSizePolicy as _QSizePolicy, 20 | QHBoxLayout as _QHBoxLayout) 21 | 22 | from PyQt5.QtCore import (QAbstractTableModel as _QAbstractTableModel, 23 | QVariant as _QVariant, 24 | QObject as _QObject, 25 | pyqtSignal as _pyqtSignal, 26 | QModelIndex as _QModelIndex, 27 | Qt as _Qt) 28 | 29 | from PyQt5.QtGui import (QPixmap as _QPixmap, 30 | QIcon as _QIcon, 31 | QColor as _QColor) 32 | 33 | from sciplot.utils.mplstyle import MplMarkers, MplLines 34 | from sciplot.utils.general import round_list 35 | 36 | from sciplot.ui.models.abstract import (AbstractTableModelMpl as 37 | _AbstractTableModelMpl, 38 | AbstractEditDelegateMpl as 39 | _AbstractEditDelegateMpl) 40 | 41 | class TableModelLines(_AbstractTableModelMpl): 42 | """ 43 | 44 | Signals 45 | ------- 46 | dataSeleted : int, int 47 | Row, id of plot 48 | """ 49 | _HEADERS = ['Color', 50 | 'Alpha', 51 | 'LineWidth', 52 | 'LineStyle', 53 | 'Marker', 54 | 'Marker Size', 55 | 'Label', 56 | 'Delete'] 57 | 58 | _COL_COLOR = _HEADERS.index('Color') 59 | _COL_ALPHA = _HEADERS.index('Alpha') 60 | _COL_LINEWIDTH = _HEADERS.index('LineWidth') 61 | _COL_LINESTYLE = _HEADERS.index('LineStyle') 62 | _COL_MARKER = _HEADERS.index('Marker') 63 | _COL_MARKERSIZE = _HEADERS.index('Marker Size') 64 | _COL_LABEL = _HEADERS.index('Label') 65 | _COL_DELETE = _HEADERS.index('Delete') 66 | # _COL_ID = _HEADERS.index('ID') 67 | 68 | dataDeleted = _pyqtSignal(int, float) 69 | 70 | def __init__(self, parent=None): 71 | 72 | super(_QAbstractTableModel, self).__init__(parent) 73 | self.headers = TableModelLines._HEADERS 74 | self._model_data = [] 75 | 76 | def rowCount(self, parent=_QModelIndex()): 77 | return len(self._model_data) 78 | 79 | def columnCount(self, parent=_QModelIndex()): 80 | return len(self.headers) 81 | 82 | def headerData(self, col, orientation, role): 83 | if orientation == _Qt.Horizontal and role == _Qt.DisplayRole: 84 | return self.headers[col] 85 | return _QVariant() 86 | 87 | def doubleClickCheck(self, index): 88 | col = index.column() 89 | if col == TableModelLines._COL_COLOR: # Color 90 | self.changeColor(index) 91 | elif col == TableModelLines._COL_DELETE: # Delete? 92 | self.deleteData(index) 93 | 94 | def deleteData(self, index): 95 | self.setData(index, True) 96 | 97 | def changeColor(self, index): 98 | row = index.row() 99 | color = self._model_data[row]['color'] 100 | color_256 = [color[0]*255, color[1]*255, color[2]*255] 101 | qcolor = _QColor(color_256[0], color_256[1], color_256[2]) 102 | 103 | result = _QColorDialog.getColor(qcolor) 104 | if _QColor.isValid(result): 105 | self.setData(index, result.getRgb()) 106 | else: 107 | return None 108 | 109 | def data(self, index, role=_Qt.DisplayRole): 110 | if not index.isValid() or not 0 <= index.row() < self.rowCount(): 111 | return _QVariant() 112 | 113 | row = index.row() 114 | col = index.column() 115 | 116 | if role == _Qt.DisplayRole: 117 | if col == TableModelLines._COL_COLOR: 118 | return str(round_list(self._model_data[row]['color'])) 119 | elif col == TableModelLines._COL_ALPHA: 120 | return str(self._model_data[row]['alpha']) 121 | elif col == TableModelLines._COL_LINEWIDTH: 122 | return str(self._model_data[row]['linewidth']) 123 | elif col == TableModelLines._COL_LINESTYLE: 124 | ls = self._model_data[row]['linestyle'] 125 | return str(MplLines.LINESTYLE_DICT[ls]) 126 | elif col == TableModelLines._COL_MARKER: 127 | mk = self._model_data[row]['marker'] 128 | return str(MplMarkers.MARKER_DICT[mk]) 129 | elif col == TableModelLines._COL_MARKERSIZE: 130 | return str(self._model_data[row]['markersize']) 131 | elif col == TableModelLines._COL_LABEL: 132 | return str(self._model_data[row]['label']) 133 | elif col == TableModelLines._COL_DELETE: 134 | return '' 135 | 136 | elif role == _Qt.DecorationRole: 137 | if col == TableModelLines._COL_COLOR: 138 | color = self._model_data[row]['color'] 139 | color_256 = [color[0]*255, color[1]*255, color[2]*255] 140 | qcolor = _QColor(color_256[0], color_256[1], color_256[2]) 141 | pm = _QPixmap(20, 20) 142 | pm.fill(qcolor) 143 | icon = _QIcon(pm) 144 | return icon 145 | elif col == TableModelLines._COL_DELETE: 146 | color = [1, 0, 0] 147 | color_256 = [color[0]*255, color[1]*255, color[2]*255] 148 | qcolor = _QColor(color_256[0], color_256[1], color_256[2]) 149 | pm = _QPixmap(20, 20) 150 | pm.fill(qcolor) 151 | icon = _QIcon(pm) 152 | return icon 153 | else: 154 | pass 155 | else: 156 | return _QVariant() 157 | 158 | def setData(self, index, value, role=_Qt.EditRole): 159 | if role == _Qt.EditRole: 160 | row = index.row() 161 | col = index.column() 162 | 163 | if col == TableModelLines._COL_COLOR: 164 | color_255 = value[0:-1] 165 | color = [round(color_255[0]/255, 2), 166 | round(color_255[1]/255, 2), 167 | round(color_255[2]/255, 2)] 168 | self._model_data[row]['color'] = color 169 | # self.colorChanged.emit(row) 170 | elif col == TableModelLines._COL_ALPHA: 171 | self._model_data[row]['alpha'] = float(value) 172 | elif col == TableModelLines._COL_LINEWIDTH: 173 | self._model_data[row]['linewidth'] = float(value) 174 | elif col == TableModelLines._COL_LINESTYLE: 175 | self._model_data[row]['linestyle'] = value 176 | elif col == TableModelLines._COL_MARKER: 177 | self._model_data[row]['marker'] = value 178 | elif col == TableModelLines._COL_MARKERSIZE: 179 | self._model_data[row]['markersize'] = float(value) 180 | elif col == TableModelLines._COL_LABEL: 181 | self._model_data[row]['label'] = value 182 | elif col == TableModelLines._COL_DELETE: 183 | if value: 184 | out = self._model_data.pop(row) 185 | self.layoutChanged.emit() 186 | self.dataDeleted.emit(row, out['id']) 187 | 188 | self.dataChanged.emit(index, index) 189 | 190 | def flags(self, index): 191 | flag = super(_QAbstractTableModel, self).flags(index) 192 | return flag | _Qt.ItemIsEditable 193 | 194 | 195 | class EditDelegateLines(_AbstractEditDelegateMpl): 196 | def createEditor(self, parent, option, index): 197 | col = index.column() 198 | if col == TableModelLines._COL_COLOR: 199 | # Color handled by doubleClicked SIGNAL 200 | pass 201 | # LineWidth or Marker size or Alpha 202 | elif (col == TableModelLines._COL_LINEWIDTH or 203 | col == TableModelLines._COL_ALPHA or 204 | col == TableModelLines._COL_MARKERSIZE): 205 | spinBoxSize = _QDoubleSpinBox(parent) 206 | spinBoxSize.setMinimum(0) 207 | spinBoxSize.setMaximum(20) 208 | spinBoxSize.setSingleStep(.5) 209 | return spinBoxSize 210 | elif col == TableModelLines._COL_LINESTYLE: # LineStyle 211 | comboBoxLineStyle = _QComboBox(parent) 212 | for line_style in MplLines.LINESTYLE_DESC: 213 | comboBoxLineStyle.addItem(line_style) 214 | return comboBoxLineStyle 215 | elif col == TableModelLines._COL_MARKER: # Marker 216 | comboBoxMarker = _QComboBox(parent) 217 | for marker_style in MplMarkers.MARKER_DESC: 218 | comboBoxMarker.addItem(marker_style) 219 | return comboBoxMarker 220 | elif col == TableModelLines._COL_LABEL: # Label 221 | lineEditLabel = _QLineEdit(parent) 222 | return lineEditLabel 223 | elif col == TableModelLines._COL_DELETE: # Delete? 224 | pass 225 | else: 226 | return _QVariant() 227 | 228 | def setEditorData(self, editor, index): 229 | col = index.column() 230 | item = index.data(_Qt.DisplayRole) 231 | if col == TableModelLines._COL_COLOR: 232 | # Color handled by doubleClick SIGNAL 233 | pass 234 | # LineWidth or MarkerSize or Alpha 235 | elif (col == TableModelLines._COL_LINEWIDTH or 236 | col == TableModelLines._COL_ALPHA or 237 | col == TableModelLines._COL_MARKERSIZE): 238 | item_float = float(item) 239 | editor.setValue(item_float) 240 | elif col == TableModelLines._COL_LINESTYLE: # LineStyle 241 | style_index = MplLines.index(item) 242 | editor.setCurrentIndex(style_index) 243 | elif col == TableModelLines._COL_MARKER: # Marker 244 | style_index = MplMarkers.index(item) 245 | editor.setCurrentIndex(style_index) 246 | elif col == TableModelLines._COL_LABEL: # Label 247 | editor.setText(item) 248 | else: 249 | pass 250 | 251 | def setModelData(self, editor, model, index): 252 | col = index.column() 253 | if col == TableModelLines._COL_COLOR: 254 | # Color handled by doubleClick SIGNAL 255 | pass 256 | # LineWidth or MarkerSize or Alpha 257 | elif (col == TableModelLines._COL_LINEWIDTH or 258 | col == TableModelLines._COL_ALPHA or 259 | col == TableModelLines._COL_MARKERSIZE): 260 | value = editor.value() 261 | model.setData(index, value) 262 | elif col == TableModelLines._COL_LINESTYLE: # LineStyle 263 | style_index = editor.currentIndex() 264 | style = MplLines.LINESTYLE_SYMBOL[style_index] 265 | model.setData(index, style) 266 | elif col == TableModelLines._COL_MARKER: # Marker 267 | style_index = editor.currentIndex() 268 | style = MplMarkers.MARKER_SYMBOL[style_index] 269 | model.setData(index, style) 270 | elif col == TableModelLines._COL_LABEL: # Label 271 | label = editor.text() 272 | model.setData(index, label) 273 | -------------------------------------------------------------------------------- /sciplot/ui/qt5-creator/Old/ui_Plotter_main.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | MainWindow 4 | 5 | 6 | 7 | 0 8 | 0 9 | 1208 10 | 892 11 | 12 | 13 | 14 | SciPlot PyQt 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | Qt::Vertical 24 | 25 | 26 | 27 | 20 28 | 40 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 0 40 | 0 41 | 42 | 43 | 44 | 45 | 200 46 | 16777215 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 0 58 | 0 59 | 60 | 61 | 62 | 63 | 16777215 64 | 16777215 65 | 66 | 67 | 68 | 0 69 | 70 | 71 | 72 | 73 | 0 74 | 0 75 | 176 76 | 332 77 | 78 | 79 | 80 | General Parameters 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | Title 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | X Label 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | Y Label 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | Aspect Ratio 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | auto 127 | 128 | 129 | 130 | 131 | equal 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | Qt::Vertical 142 | 143 | 144 | 145 | 20 146 | 40 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 0 157 | 0 158 | 176 159 | 332 160 | 161 | 162 | 163 | Axis Parameters 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | Axis Visibility 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | true 182 | 183 | 184 | 185 | 186 | 187 | 188 | X Limits 189 | 190 | 191 | 192 | 193 | 194 | 195 | 10 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | Y Limits 209 | 210 | 211 | 212 | 213 | 214 | 215 | 10 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | Scaling 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | auto 237 | 238 | 239 | 240 | 241 | equal 242 | 243 | 244 | 245 | 246 | tight 247 | 248 | 249 | 250 | 251 | image 252 | 253 | 254 | 255 | 256 | square 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | Qt::Vertical 265 | 266 | 267 | 268 | 20 269 | 40 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | Qt::Vertical 284 | 285 | 286 | 287 | 20 288 | 40 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 0 302 | 0 303 | 1208 304 | 26 305 | 306 | 307 | 308 | 309 | Format 310 | 311 | 312 | 313 | false 314 | 315 | 316 | Figure Width Format 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | false 333 | 334 | 335 | Color Scheme 336 | 337 | 338 | 339 | 340 | false 341 | 342 | 343 | Single-Column 344 | 345 | 346 | 347 | 348 | false 349 | 350 | 351 | Double-Column 352 | 353 | 354 | 355 | 356 | false 357 | 358 | 359 | Journal Styles 360 | 361 | 362 | 363 | 364 | false 365 | 366 | 367 | Macro Schema 368 | 369 | 370 | 371 | 372 | 373 | 374 | -------------------------------------------------------------------------------- /sciplot/ui/qt_Plotter.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file '.\ui_Plotter.ui' 4 | # 5 | # Created by: PyQt5 UI code generator 5.12 6 | # 7 | # WARNING! All changes made in this file will be lost! 8 | 9 | from PyQt5 import QtCore, QtGui, QtWidgets 10 | 11 | 12 | class Ui_MainWindow(object): 13 | def setupUi(self, MainWindow): 14 | MainWindow.setObjectName("MainWindow") 15 | MainWindow.resize(1208, 911) 16 | MainWindow.setStyleSheet("background-color: rgb(255, 255, 255);") 17 | self.centralwidget = QtWidgets.QWidget(MainWindow) 18 | self.centralwidget.setAutoFillBackground(False) 19 | self.centralwidget.setObjectName("centralwidget") 20 | self.horizontalLayout_4 = QtWidgets.QHBoxLayout(self.centralwidget) 21 | self.horizontalLayout_4.setObjectName("horizontalLayout_4") 22 | self.verticalLayout = QtWidgets.QVBoxLayout() 23 | self.verticalLayout.setSizeConstraint(QtWidgets.QLayout.SetNoConstraint) 24 | self.verticalLayout.setObjectName("verticalLayout") 25 | spacerItem = QtWidgets.QSpacerItem(1000, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) 26 | self.verticalLayout.addItem(spacerItem) 27 | spacerItem1 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) 28 | self.verticalLayout.addItem(spacerItem1) 29 | self.horizontalLayout_4.addLayout(self.verticalLayout) 30 | self.groupBox = QtWidgets.QGroupBox(self.centralwidget) 31 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Preferred) 32 | sizePolicy.setHorizontalStretch(0) 33 | sizePolicy.setVerticalStretch(0) 34 | sizePolicy.setHeightForWidth(self.groupBox.sizePolicy().hasHeightForWidth()) 35 | self.groupBox.setSizePolicy(sizePolicy) 36 | self.groupBox.setMaximumSize(QtCore.QSize(600, 16777215)) 37 | self.groupBox.setBaseSize(QtCore.QSize(300, 0)) 38 | self.groupBox.setTitle("") 39 | self.groupBox.setFlat(False) 40 | self.groupBox.setObjectName("groupBox") 41 | self.verticalLayout_5 = QtWidgets.QVBoxLayout(self.groupBox) 42 | self.verticalLayout_5.setSizeConstraint(QtWidgets.QLayout.SetNoConstraint) 43 | self.verticalLayout_5.setObjectName("verticalLayout_5") 44 | self.toolBox = QtWidgets.QToolBox(self.groupBox) 45 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Preferred) 46 | sizePolicy.setHorizontalStretch(0) 47 | sizePolicy.setVerticalStretch(1) 48 | sizePolicy.setHeightForWidth(self.toolBox.sizePolicy().hasHeightForWidth()) 49 | self.toolBox.setSizePolicy(sizePolicy) 50 | self.toolBox.setMinimumSize(QtCore.QSize(200, 0)) 51 | self.toolBox.setMaximumSize(QtCore.QSize(300, 16777215)) 52 | self.toolBox.setBaseSize(QtCore.QSize(100, 0)) 53 | self.toolBox.setFrameShape(QtWidgets.QFrame.NoFrame) 54 | self.toolBox.setObjectName("toolBox") 55 | self.FigureParameters = QtWidgets.QWidget() 56 | self.FigureParameters.setGeometry(QtCore.QRect(0, 0, 200, 610)) 57 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Expanding) 58 | sizePolicy.setHorizontalStretch(0) 59 | sizePolicy.setVerticalStretch(0) 60 | sizePolicy.setHeightForWidth(self.FigureParameters.sizePolicy().hasHeightForWidth()) 61 | self.FigureParameters.setSizePolicy(sizePolicy) 62 | self.FigureParameters.setObjectName("FigureParameters") 63 | self.gridLayout_2 = QtWidgets.QGridLayout(self.FigureParameters) 64 | self.gridLayout_2.setObjectName("gridLayout_2") 65 | self.label_10 = QtWidgets.QLabel(self.FigureParameters) 66 | self.label_10.setObjectName("label_10") 67 | self.gridLayout_2.addWidget(self.label_10, 2, 0, 1, 1) 68 | self.spinBoxFigureDPI = QtWidgets.QSpinBox(self.FigureParameters) 69 | self.spinBoxFigureDPI.setMinimum(10) 70 | self.spinBoxFigureDPI.setMaximum(100000000) 71 | self.spinBoxFigureDPI.setSingleStep(100) 72 | self.spinBoxFigureDPI.setProperty("value", 100) 73 | self.spinBoxFigureDPI.setObjectName("spinBoxFigureDPI") 74 | self.gridLayout_2.addWidget(self.spinBoxFigureDPI, 1, 0, 1, 1) 75 | spacerItem2 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) 76 | self.gridLayout_2.addItem(spacerItem2, 8, 0, 1, 1) 77 | self.label_9 = QtWidgets.QLabel(self.FigureParameters) 78 | self.label_9.setObjectName("label_9") 79 | self.gridLayout_2.addWidget(self.label_9, 0, 0, 1, 1) 80 | self.spinBoxFigureSavedDPI = QtWidgets.QSpinBox(self.FigureParameters) 81 | self.spinBoxFigureSavedDPI.setMinimum(10) 82 | self.spinBoxFigureSavedDPI.setMaximum(100000000) 83 | self.spinBoxFigureSavedDPI.setSingleStep(100) 84 | self.spinBoxFigureSavedDPI.setProperty("value", 100) 85 | self.spinBoxFigureSavedDPI.setObjectName("spinBoxFigureSavedDPI") 86 | self.gridLayout_2.addWidget(self.spinBoxFigureSavedDPI, 3, 0, 1, 1) 87 | self.frame = QtWidgets.QFrame(self.FigureParameters) 88 | self.frame.setFrameShape(QtWidgets.QFrame.Box) 89 | self.frame.setFrameShadow(QtWidgets.QFrame.Plain) 90 | self.frame.setObjectName("frame") 91 | self.verticalLayout_4 = QtWidgets.QVBoxLayout(self.frame) 92 | self.verticalLayout_4.setContentsMargins(1, 1, 1, 1) 93 | self.verticalLayout_4.setSpacing(2) 94 | self.verticalLayout_4.setObjectName("verticalLayout_4") 95 | self.label_11 = QtWidgets.QLabel(self.frame) 96 | self.label_11.setObjectName("label_11") 97 | self.verticalLayout_4.addWidget(self.label_11) 98 | self.label_12 = QtWidgets.QLabel(self.frame) 99 | self.label_12.setObjectName("label_12") 100 | self.verticalLayout_4.addWidget(self.label_12) 101 | self.horizontalLayout_3 = QtWidgets.QHBoxLayout() 102 | self.horizontalLayout_3.setContentsMargins(5, 5, 5, 5) 103 | self.horizontalLayout_3.setObjectName("horizontalLayout_3") 104 | self.spinBoxFigSizeWidth = QtWidgets.QDoubleSpinBox(self.frame) 105 | self.spinBoxFigSizeWidth.setMaximum(100.0) 106 | self.spinBoxFigSizeWidth.setObjectName("spinBoxFigSizeWidth") 107 | self.horizontalLayout_3.addWidget(self.spinBoxFigSizeWidth) 108 | self.spinBoxFigSizeHeight = QtWidgets.QDoubleSpinBox(self.frame) 109 | self.spinBoxFigSizeHeight.setMaximum(100.0) 110 | self.spinBoxFigSizeHeight.setObjectName("spinBoxFigSizeHeight") 111 | self.horizontalLayout_3.addWidget(self.spinBoxFigSizeHeight) 112 | self.verticalLayout_4.addLayout(self.horizontalLayout_3) 113 | self.gridLayout_2.addWidget(self.frame, 4, 0, 1, 1) 114 | self.pushButtonApplyFigParams = QtWidgets.QPushButton(self.FigureParameters) 115 | self.pushButtonApplyFigParams.setObjectName("pushButtonApplyFigParams") 116 | self.gridLayout_2.addWidget(self.pushButtonApplyFigParams, 5, 0, 1, 1) 117 | spacerItem3 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) 118 | self.gridLayout_2.addItem(spacerItem3, 6, 0, 1, 1) 119 | self.toolBox.addItem(self.FigureParameters, "") 120 | self.AxisParameters = QtWidgets.QWidget() 121 | self.AxisParameters.setGeometry(QtCore.QRect(0, 0, 200, 610)) 122 | self.AxisParameters.setObjectName("AxisParameters") 123 | self.gridLayout_3 = QtWidgets.QGridLayout(self.AxisParameters) 124 | self.gridLayout_3.setObjectName("gridLayout_3") 125 | self.verticalLayout_3 = QtWidgets.QVBoxLayout() 126 | self.verticalLayout_3.setObjectName("verticalLayout_3") 127 | self.label_5 = QtWidgets.QLabel(self.AxisParameters) 128 | self.label_5.setObjectName("label_5") 129 | self.verticalLayout_3.addWidget(self.label_5) 130 | self.checkBoxAxisVisible = QtWidgets.QCheckBox(self.AxisParameters) 131 | self.checkBoxAxisVisible.setMinimumSize(QtCore.QSize(0, 20)) 132 | self.checkBoxAxisVisible.setText("") 133 | self.checkBoxAxisVisible.setChecked(True) 134 | self.checkBoxAxisVisible.setObjectName("checkBoxAxisVisible") 135 | self.verticalLayout_3.addWidget(self.checkBoxAxisVisible) 136 | self.label_6 = QtWidgets.QLabel(self.AxisParameters) 137 | self.label_6.setObjectName("label_6") 138 | self.verticalLayout_3.addWidget(self.label_6) 139 | self.horizontalLayout = QtWidgets.QHBoxLayout() 140 | self.horizontalLayout.setContentsMargins(-1, -1, -1, 10) 141 | self.horizontalLayout.setObjectName("horizontalLayout") 142 | self.lineEditXLimMin = QtWidgets.QLineEdit(self.AxisParameters) 143 | self.lineEditXLimMin.setObjectName("lineEditXLimMin") 144 | self.horizontalLayout.addWidget(self.lineEditXLimMin) 145 | self.lineEditXLimMax = QtWidgets.QLineEdit(self.AxisParameters) 146 | self.lineEditXLimMax.setObjectName("lineEditXLimMax") 147 | self.horizontalLayout.addWidget(self.lineEditXLimMax) 148 | self.verticalLayout_3.addLayout(self.horizontalLayout) 149 | self.label_7 = QtWidgets.QLabel(self.AxisParameters) 150 | self.label_7.setObjectName("label_7") 151 | self.verticalLayout_3.addWidget(self.label_7) 152 | self.horizontalLayout_2 = QtWidgets.QHBoxLayout() 153 | self.horizontalLayout_2.setContentsMargins(-1, -1, -1, 10) 154 | self.horizontalLayout_2.setObjectName("horizontalLayout_2") 155 | self.lineEditYLimMin = QtWidgets.QLineEdit(self.AxisParameters) 156 | self.lineEditYLimMin.setObjectName("lineEditYLimMin") 157 | self.horizontalLayout_2.addWidget(self.lineEditYLimMin) 158 | self.lineEditYLimMax = QtWidgets.QLineEdit(self.AxisParameters) 159 | self.lineEditYLimMax.setObjectName("lineEditYLimMax") 160 | self.horizontalLayout_2.addWidget(self.lineEditYLimMax) 161 | self.verticalLayout_3.addLayout(self.horizontalLayout_2) 162 | self.label_8 = QtWidgets.QLabel(self.AxisParameters) 163 | self.label_8.setObjectName("label_8") 164 | self.verticalLayout_3.addWidget(self.label_8) 165 | self.comboBoxAxisScaling = QtWidgets.QComboBox(self.AxisParameters) 166 | self.comboBoxAxisScaling.setObjectName("comboBoxAxisScaling") 167 | self.comboBoxAxisScaling.addItem("") 168 | self.comboBoxAxisScaling.addItem("") 169 | self.comboBoxAxisScaling.addItem("") 170 | self.comboBoxAxisScaling.addItem("") 171 | self.comboBoxAxisScaling.addItem("") 172 | self.verticalLayout_3.addWidget(self.comboBoxAxisScaling) 173 | spacerItem4 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) 174 | self.verticalLayout_3.addItem(spacerItem4) 175 | self.gridLayout_3.addLayout(self.verticalLayout_3, 1, 0, 1, 1) 176 | self.verticalLayout_2 = QtWidgets.QVBoxLayout() 177 | self.verticalLayout_2.setContentsMargins(10, 10, 10, 10) 178 | self.verticalLayout_2.setObjectName("verticalLayout_2") 179 | self.label = QtWidgets.QLabel(self.AxisParameters) 180 | self.label.setObjectName("label") 181 | self.verticalLayout_2.addWidget(self.label) 182 | self.lineEditTitle = QtWidgets.QLineEdit(self.AxisParameters) 183 | self.lineEditTitle.setObjectName("lineEditTitle") 184 | self.verticalLayout_2.addWidget(self.lineEditTitle) 185 | self.label_2 = QtWidgets.QLabel(self.AxisParameters) 186 | self.label_2.setObjectName("label_2") 187 | self.verticalLayout_2.addWidget(self.label_2) 188 | self.lineEditXLabel = QtWidgets.QLineEdit(self.AxisParameters) 189 | self.lineEditXLabel.setObjectName("lineEditXLabel") 190 | self.verticalLayout_2.addWidget(self.lineEditXLabel) 191 | self.label_3 = QtWidgets.QLabel(self.AxisParameters) 192 | self.label_3.setObjectName("label_3") 193 | self.verticalLayout_2.addWidget(self.label_3) 194 | self.lineEditYLabel = QtWidgets.QLineEdit(self.AxisParameters) 195 | self.lineEditYLabel.setObjectName("lineEditYLabel") 196 | self.verticalLayout_2.addWidget(self.lineEditYLabel) 197 | self.label_4 = QtWidgets.QLabel(self.AxisParameters) 198 | self.label_4.setObjectName("label_4") 199 | self.verticalLayout_2.addWidget(self.label_4) 200 | self.comboBoxAspect = QtWidgets.QComboBox(self.AxisParameters) 201 | self.comboBoxAspect.setObjectName("comboBoxAspect") 202 | self.comboBoxAspect.addItem("") 203 | self.comboBoxAspect.addItem("") 204 | self.verticalLayout_2.addWidget(self.comboBoxAspect) 205 | self.gridLayout_3.addLayout(self.verticalLayout_2, 0, 0, 1, 1) 206 | self.toolBox.addItem(self.AxisParameters, "") 207 | self.verticalLayout_5.addWidget(self.toolBox, 0, QtCore.Qt.AlignLeft) 208 | spacerItem5 = QtWidgets.QSpacerItem(20, 100, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Maximum) 209 | self.verticalLayout_5.addItem(spacerItem5) 210 | self.pushButtonDefaultView = QtWidgets.QPushButton(self.groupBox) 211 | self.pushButtonDefaultView.setMaximumSize(QtCore.QSize(200, 16777215)) 212 | self.pushButtonDefaultView.setBaseSize(QtCore.QSize(200, 25)) 213 | font = QtGui.QFont() 214 | font.setFamily("Arial") 215 | font.setPointSize(10) 216 | font.setBold(True) 217 | font.setWeight(75) 218 | self.pushButtonDefaultView.setFont(font) 219 | self.pushButtonDefaultView.setObjectName("pushButtonDefaultView") 220 | self.verticalLayout_5.addWidget(self.pushButtonDefaultView) 221 | self.pushButtonClearAll = QtWidgets.QPushButton(self.groupBox) 222 | self.pushButtonClearAll.setMaximumSize(QtCore.QSize(200, 16777215)) 223 | self.pushButtonClearAll.setBaseSize(QtCore.QSize(200, 0)) 224 | font = QtGui.QFont() 225 | font.setFamily("Arial") 226 | font.setPointSize(10) 227 | font.setBold(True) 228 | font.setWeight(75) 229 | self.pushButtonClearAll.setFont(font) 230 | self.pushButtonClearAll.setObjectName("pushButtonClearAll") 231 | self.verticalLayout_5.addWidget(self.pushButtonClearAll) 232 | self.verticalLayout_5.setStretch(0, 10) 233 | self.verticalLayout_5.setStretch(1, 10) 234 | self.verticalLayout_5.setStretch(2, 10) 235 | self.verticalLayout_5.setStretch(3, 10) 236 | self.horizontalLayout_4.addWidget(self.groupBox) 237 | self.horizontalLayout_4.setStretch(1, 10) 238 | MainWindow.setCentralWidget(self.centralwidget) 239 | self.menubar = QtWidgets.QMenuBar(MainWindow) 240 | self.menubar.setGeometry(QtCore.QRect(0, 0, 1208, 22)) 241 | self.menubar.setObjectName("menubar") 242 | self.menuFormat = QtWidgets.QMenu(self.menubar) 243 | self.menuFormat.setObjectName("menuFormat") 244 | self.menuFigure_Width_Format = QtWidgets.QMenu(self.menuFormat) 245 | self.menuFigure_Width_Format.setEnabled(False) 246 | self.menuFigure_Width_Format.setObjectName("menuFigure_Width_Format") 247 | self.menuFigure_Format_Settings = QtWidgets.QMenu(self.menuFormat) 248 | self.menuFigure_Format_Settings.setObjectName("menuFigure_Format_Settings") 249 | self.menuFile = QtWidgets.QMenu(self.menubar) 250 | self.menuFile.setObjectName("menuFile") 251 | MainWindow.setMenuBar(self.menubar) 252 | self.statusbar = QtWidgets.QStatusBar(MainWindow) 253 | self.statusbar.setObjectName("statusbar") 254 | MainWindow.setStatusBar(self.statusbar) 255 | self.actionStyle = QtWidgets.QAction(MainWindow) 256 | self.actionStyle.setEnabled(False) 257 | self.actionStyle.setObjectName("actionStyle") 258 | self.actionSingle_Column = QtWidgets.QAction(MainWindow) 259 | self.actionSingle_Column.setEnabled(False) 260 | self.actionSingle_Column.setObjectName("actionSingle_Column") 261 | self.actionDouble_Column = QtWidgets.QAction(MainWindow) 262 | self.actionDouble_Column.setEnabled(False) 263 | self.actionDouble_Column.setObjectName("actionDouble_Column") 264 | self.actionJournal_Styles = QtWidgets.QAction(MainWindow) 265 | self.actionJournal_Styles.setEnabled(False) 266 | self.actionJournal_Styles.setObjectName("actionJournal_Styles") 267 | self.actionMacro_Schema = QtWidgets.QAction(MainWindow) 268 | self.actionMacro_Schema.setEnabled(False) 269 | self.actionMacro_Schema.setObjectName("actionMacro_Schema") 270 | self.actionExport_Lines_to_CSV = QtWidgets.QAction(MainWindow) 271 | self.actionExport_Lines_to_CSV.setVisible(False) 272 | self.actionExport_Lines_to_CSV.setObjectName("actionExport_Lines_to_CSV") 273 | self.actionExport_Bars_to_CSV = QtWidgets.QAction(MainWindow) 274 | self.actionExport_Bars_to_CSV.setVisible(False) 275 | self.actionExport_Bars_to_CSV.setObjectName("actionExport_Bars_to_CSV") 276 | self.actionExport_Fill_Between_to_CSV = QtWidgets.QAction(MainWindow) 277 | self.actionExport_Fill_Between_to_CSV.setVisible(False) 278 | self.actionExport_Fill_Between_to_CSV.setObjectName("actionExport_Fill_Between_to_CSV") 279 | self.actionExport_Image_to_CSV = QtWidgets.QAction(MainWindow) 280 | self.actionExport_Image_to_CSV.setVisible(False) 281 | self.actionExport_Image_to_CSV.setObjectName("actionExport_Image_to_CSV") 282 | self.actionFigureDPI = QtWidgets.QAction(MainWindow) 283 | self.actionFigureDPI.setObjectName("actionFigureDPI") 284 | self.actionFigureSavedDPI = QtWidgets.QAction(MainWindow) 285 | self.actionFigureSavedDPI.setObjectName("actionFigureSavedDPI") 286 | self.actionFigure_Size_Display = QtWidgets.QAction(MainWindow) 287 | self.actionFigure_Size_Display.setObjectName("actionFigure_Size_Display") 288 | self.actionFigure_Size_Saved = QtWidgets.QAction(MainWindow) 289 | self.actionFigure_Size_Saved.setObjectName("actionFigure_Size_Saved") 290 | self.menuFigure_Width_Format.addAction(self.actionSingle_Column) 291 | self.menuFigure_Width_Format.addAction(self.actionDouble_Column) 292 | self.menuFigure_Format_Settings.addAction(self.actionFigureDPI) 293 | self.menuFigure_Format_Settings.addAction(self.actionFigureSavedDPI) 294 | self.menuFigure_Format_Settings.addAction(self.actionFigure_Size_Display) 295 | self.menuFigure_Format_Settings.addAction(self.actionFigure_Size_Saved) 296 | self.menuFormat.addAction(self.actionStyle) 297 | self.menuFormat.addAction(self.menuFigure_Width_Format.menuAction()) 298 | self.menuFormat.addSeparator() 299 | self.menuFormat.addAction(self.menuFigure_Format_Settings.menuAction()) 300 | self.menuFormat.addSeparator() 301 | self.menuFormat.addAction(self.actionJournal_Styles) 302 | self.menuFormat.addAction(self.actionMacro_Schema) 303 | self.menuFile.addAction(self.actionExport_Lines_to_CSV) 304 | self.menuFile.addAction(self.actionExport_Bars_to_CSV) 305 | self.menuFile.addAction(self.actionExport_Fill_Between_to_CSV) 306 | self.menuFile.addAction(self.actionExport_Image_to_CSV) 307 | self.menubar.addAction(self.menuFile.menuAction()) 308 | self.menubar.addAction(self.menuFormat.menuAction()) 309 | 310 | self.retranslateUi(MainWindow) 311 | self.toolBox.setCurrentIndex(1) 312 | QtCore.QMetaObject.connectSlotsByName(MainWindow) 313 | 314 | def retranslateUi(self, MainWindow): 315 | _translate = QtCore.QCoreApplication.translate 316 | MainWindow.setWindowTitle(_translate("MainWindow", "SciPlot PyQt")) 317 | self.label_10.setText(_translate("MainWindow", "DPI: Saved")) 318 | self.label_9.setText(_translate("MainWindow", "DPI: Display")) 319 | self.label_11.setText(_translate("MainWindow", "Figure Size (inches)")) 320 | self.label_12.setText(_translate("MainWindow", "Width x Height")) 321 | self.pushButtonApplyFigParams.setText(_translate("MainWindow", "Apply New Parameters")) 322 | self.toolBox.setItemText(self.toolBox.indexOf(self.FigureParameters), _translate("MainWindow", "Figure Parameters")) 323 | self.label_5.setText(_translate("MainWindow", "Axis Visibility")) 324 | self.label_6.setText(_translate("MainWindow", "X Limits")) 325 | self.label_7.setText(_translate("MainWindow", "Y Limits")) 326 | self.label_8.setText(_translate("MainWindow", "Scaling")) 327 | self.comboBoxAxisScaling.setItemText(0, _translate("MainWindow", "auto")) 328 | self.comboBoxAxisScaling.setItemText(1, _translate("MainWindow", "equal")) 329 | self.comboBoxAxisScaling.setItemText(2, _translate("MainWindow", "tight")) 330 | self.comboBoxAxisScaling.setItemText(3, _translate("MainWindow", "image")) 331 | self.comboBoxAxisScaling.setItemText(4, _translate("MainWindow", "square")) 332 | self.label.setText(_translate("MainWindow", "Title")) 333 | self.label_2.setText(_translate("MainWindow", "X Label")) 334 | self.label_3.setText(_translate("MainWindow", "Y Label")) 335 | self.label_4.setText(_translate("MainWindow", "Aspect Ratio")) 336 | self.comboBoxAspect.setItemText(0, _translate("MainWindow", "auto")) 337 | self.comboBoxAspect.setItemText(1, _translate("MainWindow", "equal")) 338 | self.toolBox.setItemText(self.toolBox.indexOf(self.AxisParameters), _translate("MainWindow", "Axis Parameters")) 339 | self.pushButtonDefaultView.setText(_translate("MainWindow", "Set Default View")) 340 | self.pushButtonClearAll.setText(_translate("MainWindow", "Clear All")) 341 | self.menuFormat.setTitle(_translate("MainWindow", "Format")) 342 | self.menuFigure_Width_Format.setTitle(_translate("MainWindow", "Figure Width Format")) 343 | self.menuFigure_Format_Settings.setTitle(_translate("MainWindow", "Figure Format Settings")) 344 | self.menuFile.setTitle(_translate("MainWindow", "File")) 345 | self.actionStyle.setText(_translate("MainWindow", "Color Scheme")) 346 | self.actionSingle_Column.setText(_translate("MainWindow", "Single-Column")) 347 | self.actionDouble_Column.setText(_translate("MainWindow", "Double-Column")) 348 | self.actionJournal_Styles.setText(_translate("MainWindow", "Journal Styles")) 349 | self.actionMacro_Schema.setText(_translate("MainWindow", "Macro Schema")) 350 | self.actionExport_Lines_to_CSV.setText(_translate("MainWindow", "Export Lines to CSV")) 351 | self.actionExport_Bars_to_CSV.setText(_translate("MainWindow", "Export Bars to CSV")) 352 | self.actionExport_Fill_Between_to_CSV.setText(_translate("MainWindow", "Export Fill Between to CSV")) 353 | self.actionExport_Image_to_CSV.setText(_translate("MainWindow", "Export Image to CSV")) 354 | self.actionFigureDPI.setText(_translate("MainWindow", "DPI: Display")) 355 | self.actionFigureSavedDPI.setText(_translate("MainWindow", "DPI: Saved")) 356 | self.actionFigure_Size_Display.setText(_translate("MainWindow", "Figure Size: Display")) 357 | self.actionFigure_Size_Saved.setText(_translate("MainWindow", "Figure Size: Saved")) 358 | 359 | 360 | -------------------------------------------------------------------------------- /sciplot/ui/qt5-creator/ui_Plotter.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | MainWindow 4 | 5 | 6 | 7 | 0 8 | 0 9 | 1208 10 | 911 11 | 12 | 13 | 14 | SciPlot PyQt 15 | 16 | 17 | background-color: rgb(255, 255, 255); 18 | 19 | 20 | 21 | false 22 | 23 | 24 | 25 | 26 | 27 | QLayout::SetNoConstraint 28 | 29 | 30 | 31 | 32 | Qt::Horizontal 33 | 34 | 35 | QSizePolicy::Expanding 36 | 37 | 38 | 39 | 1000 40 | 20 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | Qt::Vertical 49 | 50 | 51 | 52 | 20 53 | 40 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 0 65 | 0 66 | 67 | 68 | 69 | 70 | 600 71 | 16777215 72 | 73 | 74 | 75 | 76 | 300 77 | 0 78 | 79 | 80 | 81 | 82 | 83 | 84 | false 85 | 86 | 87 | 88 | QLayout::SetNoConstraint 89 | 90 | 91 | 92 | 93 | 94 | 0 95 | 1 96 | 97 | 98 | 99 | 100 | 200 101 | 0 102 | 103 | 104 | 105 | 106 | 300 107 | 16777215 108 | 109 | 110 | 111 | 112 | 100 113 | 0 114 | 115 | 116 | 117 | QFrame::NoFrame 118 | 119 | 120 | 1 121 | 122 | 123 | 124 | 125 | 0 126 | 0 127 | 200 128 | 610 129 | 130 | 131 | 132 | 133 | 0 134 | 0 135 | 136 | 137 | 138 | Figure Parameters 139 | 140 | 141 | 142 | 143 | 144 | DPI: Saved 145 | 146 | 147 | 148 | 149 | 150 | 151 | 10 152 | 153 | 154 | 100000000 155 | 156 | 157 | 100 158 | 159 | 160 | 100 161 | 162 | 163 | 164 | 165 | 166 | 167 | Qt::Vertical 168 | 169 | 170 | QSizePolicy::Expanding 171 | 172 | 173 | 174 | 20 175 | 40 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | DPI: Display 184 | 185 | 186 | 187 | 188 | 189 | 190 | 10 191 | 192 | 193 | 100000000 194 | 195 | 196 | 100 197 | 198 | 199 | 100 200 | 201 | 202 | 203 | 204 | 205 | 206 | QFrame::Box 207 | 208 | 209 | QFrame::Plain 210 | 211 | 212 | 213 | 2 214 | 215 | 216 | 1 217 | 218 | 219 | 1 220 | 221 | 222 | 1 223 | 224 | 225 | 1 226 | 227 | 228 | 229 | 230 | Figure Size (inches) 231 | 232 | 233 | 234 | 235 | 236 | 237 | Width x Height 238 | 239 | 240 | 241 | 242 | 243 | 244 | 5 245 | 246 | 247 | 5 248 | 249 | 250 | 5 251 | 252 | 253 | 5 254 | 255 | 256 | 257 | 258 | 100.000000000000000 259 | 260 | 261 | 262 | 263 | 264 | 265 | 100.000000000000000 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | Apply New Parameters 278 | 279 | 280 | 281 | 282 | 283 | 284 | Qt::Horizontal 285 | 286 | 287 | 288 | 40 289 | 20 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 0 300 | 0 301 | 200 302 | 610 303 | 304 | 305 | 306 | Axis Parameters 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | Axis Visibility 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 0 323 | 20 324 | 325 | 326 | 327 | 328 | 329 | 330 | true 331 | 332 | 333 | 334 | 335 | 336 | 337 | X Limits 338 | 339 | 340 | 341 | 342 | 343 | 344 | 10 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | Y Limits 358 | 359 | 360 | 361 | 362 | 363 | 364 | 10 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | Scaling 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | auto 386 | 387 | 388 | 389 | 390 | equal 391 | 392 | 393 | 394 | 395 | tight 396 | 397 | 398 | 399 | 400 | image 401 | 402 | 403 | 404 | 405 | square 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | Qt::Vertical 414 | 415 | 416 | 417 | 20 418 | 40 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 10 429 | 430 | 431 | 10 432 | 433 | 434 | 10 435 | 436 | 437 | 10 438 | 439 | 440 | 441 | 442 | Title 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | X Label 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | Y Label 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | Aspect Ratio 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | auto 481 | 482 | 483 | 484 | 485 | equal 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | Qt::Vertical 500 | 501 | 502 | QSizePolicy::Maximum 503 | 504 | 505 | 506 | 20 507 | 100 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 200 517 | 16777215 518 | 519 | 520 | 521 | 522 | 200 523 | 25 524 | 525 | 526 | 527 | 528 | Arial 529 | 10 530 | 75 531 | true 532 | 533 | 534 | 535 | Set Default View 536 | 537 | 538 | 539 | 540 | 541 | 542 | 543 | 200 544 | 16777215 545 | 546 | 547 | 548 | 549 | 200 550 | 0 551 | 552 | 553 | 554 | 555 | Arial 556 | 10 557 | 75 558 | true 559 | 560 | 561 | 562 | Clear All 563 | 564 | 565 | 566 | 567 | 568 | 569 | 570 | 571 | 572 | 573 | 574 | 0 575 | 0 576 | 1208 577 | 22 578 | 579 | 580 | 581 | 582 | Format 583 | 584 | 585 | 586 | false 587 | 588 | 589 | Figure Width Format 590 | 591 | 592 | 593 | 594 | 595 | 596 | Figure Format Settings 597 | 598 | 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | File 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 | false 627 | 628 | 629 | Color Scheme 630 | 631 | 632 | 633 | 634 | false 635 | 636 | 637 | Single-Column 638 | 639 | 640 | 641 | 642 | false 643 | 644 | 645 | Double-Column 646 | 647 | 648 | 649 | 650 | false 651 | 652 | 653 | Journal Styles 654 | 655 | 656 | 657 | 658 | false 659 | 660 | 661 | Macro Schema 662 | 663 | 664 | 665 | 666 | Export Lines to CSV 667 | 668 | 669 | false 670 | 671 | 672 | 673 | 674 | Export Bars to CSV 675 | 676 | 677 | false 678 | 679 | 680 | 681 | 682 | Export Fill Between to CSV 683 | 684 | 685 | false 686 | 687 | 688 | 689 | 690 | Export Image to CSV 691 | 692 | 693 | false 694 | 695 | 696 | 697 | 698 | DPI: Display 699 | 700 | 701 | 702 | 703 | DPI: Saved 704 | 705 | 706 | 707 | 708 | Figure Size: Display 709 | 710 | 711 | 712 | 713 | Figure Size: Saved 714 | 715 | 716 | 717 | 718 | 719 | 720 | --------------------------------------------------------------------------------