Bindings for graphics lib QCustomPlot2 for PyQt5
Revision | 325b08ee4cee5aa39369633d07c25648078d8526 (tree) |
---|---|
Zeit | 2019-09-07 13:36:00 |
Autor | Sergey Salnikov <salsergey@gmai...> |
Commiter | Sergey Salnikov |
Improved support for different platforms and Python versions
* Refactored setup.py to support both Python 2 and Python 3.
* Added support for building from another directories.
* Improved automatic determination of qmake.exe if it is in PATH.
* Prepared for PyPI.
@@ -0,0 +1,3 @@ | ||
1 | +graft examples | |
2 | +graft QCustomPlot | |
3 | +graft sip |
@@ -3,6 +3,7 @@ | ||
3 | 3 | import os |
4 | 4 | import platform |
5 | 5 | import subprocess |
6 | +import sys | |
6 | 7 | |
7 | 8 | import sipconfig |
8 | 9 |
@@ -18,6 +19,8 @@ from sipdistutils import build_ext | ||
18 | 19 | from PyQt5.QtCore import PYQT_CONFIGURATION |
19 | 20 | from PyQt5.QtCore import QLibraryInfo |
20 | 21 | |
22 | +CPU_COUNT = os.cpu_count() if 'cpu_count' in dir(os) else 1 # number of parallel compilations | |
23 | + | |
21 | 24 | # monkey-patch for parallel compilation, see |
22 | 25 | # https://stackoverflow.com/questions/11013851/speeding-up-build-process-with-distutils |
23 | 26 | def parallelCCompile(self, sources, output_dir=None, macros=None, include_dirs=None, debug=0, extra_preargs=None, extra_postargs=None, depends=None): |
@@ -25,14 +28,13 @@ def parallelCCompile(self, sources, output_dir=None, macros=None, include_dirs=N | ||
25 | 28 | macros, objects, extra_postargs, pp_opts, build = self._setup_compile(output_dir, macros, include_dirs, sources, depends, extra_postargs) |
26 | 29 | cc_args = self._get_cc_args(pp_opts, debug, extra_preargs) |
27 | 30 | # parallel code |
28 | - N = os.cpu_count() # number of parallel compilations | |
29 | 31 | import multiprocessing.pool |
30 | 32 | def _single_compile(obj): |
31 | 33 | try: src, ext = build[obj] |
32 | 34 | except KeyError: return |
33 | 35 | self._compile(obj, src, ext, cc_args, extra_postargs, pp_opts) |
34 | 36 | # convert to list, imap is evaluated on-demand |
35 | - list(multiprocessing.pool.ThreadPool(N).imap(_single_compile,objects)) | |
37 | + list(multiprocessing.pool.ThreadPool(CPU_COUNT).imap(_single_compile,objects)) | |
36 | 38 | return objects |
37 | 39 | |
38 | 40 |
@@ -42,21 +44,31 @@ LINUX_HOST = (platform.system() == 'Linux') | ||
42 | 44 | # This is with Unix pathsep even on windows |
43 | 45 | QT_BINARIES = QLibraryInfo.location(QLibraryInfo.BinariesPath) |
44 | 46 | if WINDOWS_HOST: |
45 | - # Default to MSVC nmake | |
47 | + # Default to Qt's jom instead of MSVC nmake | |
46 | 48 | DEFAULT_MAKE = 'jom.exe' |
47 | - DEFAULT_QMAKE = "{}/{}".format(QT_BINARIES, "qmake.exe") | |
49 | + # Try to find qmake.exe in PATH | |
50 | + pipe = subprocess.Popen('where qmake.exe', stdout=subprocess.PIPE) | |
51 | + (stdout, stderr) = pipe.communicate() | |
52 | + DEFAULT_QMAKE = str(stdout.strip(), 'utf8') if sys.version_info.major == 3 else str(stdout.strip()) | |
53 | + if DEFAULT_QMAKE == '': | |
54 | + print('Unable to find qmake.exe. Please set the path manually using --qmake option.') | |
48 | 55 | else: |
49 | 56 | DEFAULT_MAKE = 'make' |
50 | - DEFAULT_QMAKE = "{}/{}".format(QT_BINARIES, "qmake") | |
57 | + DEFAULT_QMAKE = '{}/{}'.format(QT_BINARIES, 'qmake') | |
51 | 58 | |
52 | 59 | DEFAULT_QT_INCLUDE = QLibraryInfo.location(QLibraryInfo.HeadersPath) |
53 | 60 | ROOT = abspath(dirname(__file__)) |
61 | +BUILD_DIR = os.getcwd() | |
54 | 62 | BUILD_STATIC_DIR = join(ROOT, 'lib-static') |
55 | 63 | |
56 | 64 | # Monkey-patch, see above |
57 | 65 | CCompiler.compile=parallelCCompile |
58 | 66 | |
59 | 67 | |
68 | +with open(join(ROOT, 'README.md'), 'r') as fh: | |
69 | + long_description = fh.read() | |
70 | + | |
71 | + | |
60 | 72 | class MyBuilderExt(build_ext): |
61 | 73 | user_options = build_ext.user_options[:] |
62 | 74 | user_options += [ |
@@ -88,15 +100,15 @@ class MyBuilderExt(build_ext): | ||
88 | 100 | print('Setting make to \'%s\'' % DEFAULT_MAKE) |
89 | 101 | self.make = DEFAULT_MAKE |
90 | 102 | if self.qt_include_dir is None: |
91 | - pipe = subprocess.Popen([self.qmake, "-query", "QT_INSTALL_HEADERS"], stdout=subprocess.PIPE) | |
103 | + pipe = subprocess.Popen([self.qmake, '-query', 'QT_INSTALL_HEADERS'], stdout=subprocess.PIPE) | |
92 | 104 | (stdout, stderr) = pipe.communicate() |
93 | - self.qt_include_dir = str(stdout.strip(), 'utf8') | |
105 | + self.qt_include_dir = str(stdout.strip(), 'utf8') if sys.version_info.major == 3 else str(stdout.strip()) | |
94 | 106 | print('Setting Qt include dir to \'%s\'' % self.qt_include_dir) |
95 | 107 | |
96 | 108 | if self.qt_library_dir is None: |
97 | - pipe = subprocess.Popen([self.qmake, "-query", "QT_INSTALL_LIBS"], stdout=subprocess.PIPE) | |
109 | + pipe = subprocess.Popen([self.qmake, '-query', 'QT_INSTALL_LIBS'], stdout=subprocess.PIPE) | |
98 | 110 | (stdout, stderr) = pipe.communicate() |
99 | - self.qt_library_dir = str(stdout.strip(), 'utf8') | |
111 | + self.qt_library_dir = str(stdout.strip(), 'utf8') if sys.version_info.major == 3 else str(stdout.strip()) | |
100 | 112 | print('Setting Qt library dir to \'%s\'' % self.qt_library_dir) |
101 | 113 | |
102 | 114 | if not exists(self.qmake): |
@@ -104,24 +116,27 @@ class MyBuilderExt(build_ext): | ||
104 | 116 | |
105 | 117 | def __build_qcustomplot_library(self): |
106 | 118 | if WINDOWS_HOST: |
107 | - qcustomplot_static = join(self.build_temp, 'release', 'qcustomplot.lib') | |
119 | + qcustomplot_static = join(BUILD_DIR, self.build_temp, 'release', 'qcustomplot.lib') | |
108 | 120 | else: |
109 | - qcustomplot_static = join(self.build_temp, 'libqcustomplot.a') | |
121 | + qcustomplot_static = join(BUILD_DIR, self.build_temp, 'libqcustomplot.a') | |
110 | 122 | if exists(qcustomplot_static): |
111 | 123 | return |
112 | 124 | |
113 | - os.makedirs(self.build_temp, exist_ok=True) | |
125 | + if sys.version_info.major == 3: | |
126 | + os.makedirs(self.build_temp, exist_ok=True) | |
127 | + else: | |
128 | + os.makedirs(self.build_temp) | |
114 | 129 | os.chdir(self.build_temp) |
115 | 130 | print('Make static qcustomplot library...') |
116 | 131 | self.spawn([self.qmake, join(ROOT, 'QCustomPlot/src/qcp-staticlib.pro')]) |
117 | 132 | # AFAIK only nmake does not support -j option |
118 | - has_multiprocess = not(WINDOWS_HOST and "nmake"in self.make) | |
133 | + has_multiprocess = not(WINDOWS_HOST and 'nmake' in self.make) | |
119 | 134 | make_cmdline = [self.make] |
120 | 135 | if has_multiprocess: |
121 | - make_cmdline.extend(['-j', str(os.cpu_count())]) | |
122 | - self.spawn([self.make, '-j', str(os.cpu_count())]) | |
136 | + make_cmdline.extend(['-j', str(CPU_COUNT)]) | |
137 | + self.spawn([self.make, '-j', str(CPU_COUNT)]) | |
123 | 138 | |
124 | - os.chdir(ROOT) | |
139 | + os.chdir(BUILD_DIR) | |
125 | 140 | self.static_lib = qcustomplot_static |
126 | 141 | # Possibly it's hack |
127 | 142 | qcustomplot_ext = self.extensions[0] |
@@ -141,6 +156,7 @@ class MyBuilderExt(build_ext): | ||
141 | 156 | for subdir in ['.', 'QtCore', 'QtGui', 'QtWidgets', 'QtPrintSupport'] |
142 | 157 | ] |
143 | 158 | qcustomplot_ext.library_dirs += [ |
159 | + BUILD_DIR, | |
144 | 160 | self.build_temp, |
145 | 161 | self.qt_library_dir |
146 | 162 | ] |
@@ -167,21 +183,41 @@ setup( | ||
167 | 183 | name='QCustomPlot2', |
168 | 184 | version='2.0.1', |
169 | 185 | description='QCustomPlot is a Qt widget for plotting and data visualization', |
170 | - author='Dmitry Voronin, Giuseppe Corbelli, Christopher Gilbert', | |
171 | - author_email='carriingfate92@yandex.ru', | |
172 | - url='https://github.com/cjgdev/QCustomPlot2-PyQt5', | |
173 | - platforms=['Linux'], | |
186 | + long_description=long_description, | |
187 | + long_description_content_type='text/markdown', | |
188 | + author='Dmitry Voronin, Giuseppe Corbelli, Christopher Gilbert, Sergey Salnikov', | |
189 | + author_email='salsergey@gmail.com', | |
190 | + url='https://osdn.net/users/salsergey/pf/QCustomPlot2-PyQt5', | |
191 | + platforms=['Linux', 'Windows'], | |
174 | 192 | license='MIT', |
175 | - ext_modules=[ | |
176 | - Extension( | |
177 | - 'QCustomPlot2', | |
178 | - ['sip/all.sip'], | |
179 | - include_dirs=['.','./sip'] | |
180 | - ), | |
193 | + classifiers=[ | |
194 | + 'Development Status :: 5 - Production/Stable', | |
195 | + 'Environment :: X11 Applications :: Qt', | |
196 | + 'Intended Audience :: Developers', | |
197 | + 'License :: OSI Approved :: MIT License', | |
198 | + 'Operating System :: POSIX :: Linux', | |
199 | + 'Operating System :: Microsoft :: Windows', | |
200 | + 'Programming Language :: Python :: 2', | |
201 | + 'Programming Language :: Python :: 2.7', | |
202 | + 'Programming Language :: Python :: 3', | |
203 | + 'Programming Language :: Python :: 3.3', | |
204 | + 'Programming Language :: Python :: 3.4', | |
205 | + 'Programming Language :: Python :: 3.5', | |
206 | + 'Programming Language :: Python :: 3.6', | |
207 | + 'Programming Language :: Python :: 3.7', | |
208 | + 'Topic :: Software Development :: User Interfaces' | |
181 | 209 | ], |
182 | 210 | requires=[ |
183 | 211 | 'sipconfig', |
184 | 212 | 'PyQt5' |
185 | 213 | ], |
214 | + install_requires=['PyQt5'], | |
215 | + ext_modules=[ | |
216 | + Extension( | |
217 | + 'QCustomPlot2', | |
218 | + [join(ROOT, 'sip/all.sip')], | |
219 | + include_dirs=[ROOT, join(ROOT, 'sip')] | |
220 | + ), | |
221 | + ], | |
186 | 222 | cmdclass={'build_ext': MyBuilderExt} |
187 | 223 | ) |