Skip to content

Commit dc46285

Browse files
committed
[pyqgis-console] Add targets and python script for generating console's QScintilla .pap file, for auto-completion
- Run 'make;make qsci-pap-src; make install' to update the default .pap file in source tree - Run 'make;make qsci-pap-master[; make install]' for local-only .pap that overrides the default .pap (when testing new bindings) - Update console to override default .pap if master.pap is available - Include new qgis.core.NULL attribute defined in <src>/python/__init__.py for QPyNullVariant comparisons - Update 11-month-old default .pap
1 parent 85f15b5 commit dc46285

File tree

7 files changed

+153
-6
lines changed

7 files changed

+153
-6
lines changed

CMakeLists.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ IF (WITH_BINDINGS)
8686
SET (WITH_STAGED_PLUGINS TRUE CACHE BOOL "Stage-install core Python plugins to run from build directory? (utilities and console are always staged)")
8787
SET (WITH_PY_COMPILE FALSE CACHE BOOL "Determines whether Python modules in staged or installed locations are byte-compiled")
8888
# concatenate QScintilla2 API files
89-
SET (WITH_QSCIAPI TRUE CACHE BOOL "Determines whether the QScintilla2 API files will be updated and concatenated")
89+
SET (WITH_QSCIAPI TRUE CACHE BOOL "Whether to generate PyQGIS QScintilla2 API file. Run 'make qsci-pap-master or qsci-pap-src' in between QGIS build and install, to generate .pap file for console auto-completion.")
9090
# path to custom Python framework on Mac
9191
IF (APPLE)
9292
SET (PYTHON_CUSTOM_FRAMEWORK "" CACHE PATH "Path to custom Python.framework on Mac. (should not have to specify other Python options)")

cmake/QsciAPI.cmake

+6
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77

88
SET(QGIS_PYTHON_API_FILE "${CMAKE_BINARY_DIR}/python/qsci_apis/PyQGIS.api")
99

10+
# create empty destination api file
11+
FILE(WRITE "${QGIS_PYTHON_API_FILE}" "")
12+
1013
IF(EXISTS "${CMAKE_BINARY_DIR}/python/qgis.gui.api")
1114
FILE(READ "${CMAKE_BINARY_DIR}/python/qgis.gui.api" FILE_CONTENT)
1215
STRING(REGEX MATCHALL "gui\\.QgisInterface([^\n]+)" MATCHED_CONTENT "${FILE_CONTENT}")
@@ -16,6 +19,9 @@ IF(EXISTS "${CMAKE_BINARY_DIR}/python/qgis.gui.api")
1619
ENDFOREACH(matchedLine)
1720
ENDIF(EXISTS "${CMAKE_BINARY_DIR}/python/qgis.gui.api")
1821

22+
# add qgis.core.NULL attribute defined in <src>/python/__init__.py for QPyNullVariant
23+
FILE(APPEND "${QGIS_PYTHON_API_FILE}" "qgis.core.NULL?7\n")
24+
1925
FOREACH(apiFile qgis.core.api qgis.gui.api qgis.analysis.api qgis.networkanalysis.api)
2026
SET(api "${CMAKE_BINARY_DIR}/python/${apiFile}")
2127
IF(EXISTS "${api}")

python/CMakeLists.txt

+38-4
Original file line numberDiff line numberDiff line change
@@ -127,17 +127,51 @@ SET(QGIS_PYTHON_DIR ${PYTHON_SITE_PACKAGES_DIR}/qgis)
127127
IF(WITH_QSCIAPI)
128128
# wait until after python module builds for api files to be available
129129
SET(QGIS_PYTHON_API_FILE "${CMAKE_BINARY_DIR}/python/qsci_apis/PyQGIS.api")
130-
# create empty destination api file
131-
FILE(WRITE "${QGIS_PYTHON_API_FILE}" "")
132130

133-
# run update/concatenate command after last python module is built (currently python_module_qgis_gui)
134-
ADD_CUSTOM_COMMAND(TARGET python_module_qgis_gui
131+
ADD_CUSTOM_TARGET(qsci-api ALL
132+
DEPENDS python_module_qgis_gui python_module_qgis_core python_module_qgis_analysis python_module_qgis_networkanalysis)
133+
134+
# run update/concatenate command
135+
ADD_CUSTOM_COMMAND(TARGET qsci-api
135136
POST_BUILD
136137
COMMAND "${CMAKE_COMMAND}" -P "${CMAKE_SOURCE_DIR}/cmake/QsciAPI.cmake"
137138
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
138139
COMMENT "Generating pyqgis api file" VERBATIM)
139140

140141
INSTALL(FILES ${QGIS_PYTHON_API_FILE} DESTINATION "${QGIS_DATA_DIR}/python/qsci_apis")
142+
143+
# create targets for generating console auto-completion *.pap binary file
144+
# these take too long to build (> 1 minute) for targets to have ALL property
145+
SET(APIS_SRC_DIR "${CMAKE_SOURCE_DIR}/python/qsci_apis")
146+
SET(APIS_BIN_DIR "${CMAKE_BINARY_DIR}/python/qsci_apis")
147+
148+
# generate a local-only .pap file based upon current master branch,
149+
# which will override (but not replace) the default .pap from source
150+
# run before 'make install' to have the local-only .pap file included in the install;
151+
# console will use it when running from build directory, regardless of whether it is installed
152+
ADD_CUSTOM_TARGET(qsci-pap-master
153+
DEPENDS qsci-api ${QGIS_PYTHON_API_FILE})
154+
155+
SET(PAP_NAME "pyqgis-master.pap")
156+
ADD_CUSTOM_COMMAND(TARGET qsci-pap-master
157+
POST_BUILD
158+
COMMAND ${PYTHON_EXECUTABLE} "${APIS_SRC_DIR}/generate_console_pap.py" "${PAP_NAME}" "${APIS_SRC_DIR}" "${APIS_BIN_DIR}"
159+
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
160+
COMMENT "Generating local-only ${PAP_NAME} for console auto-completion (MAY TAKE > 1 MINUTE!)" VERBATIM)
161+
162+
INSTALL(FILES "${APIS_BIN_DIR}/pyqgis-master.pap" DESTINATION "${QGIS_DATA_DIR}/python/qsci_apis")
163+
164+
# generate a .pap file to be immediately installed in QGIS source tree (the default .pap)
165+
ADD_CUSTOM_TARGET(qsci-pap-src
166+
DEPENDS qsci-api ${QGIS_PYTHON_API_FILE})
167+
168+
SET(PAP_NAME "pyqgis.pap")
169+
ADD_CUSTOM_COMMAND(TARGET qsci-pap-src
170+
POST_BUILD
171+
COMMAND ${PYTHON_EXECUTABLE} "${APIS_SRC_DIR}/generate_console_pap.py" "${PAP_NAME}" "${APIS_SRC_DIR}" "${APIS_SRC_DIR}"
172+
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
173+
COMMENT "Generating ${PAP_NAME} for console auto-completion (MAY TAKE > 1 MINUTE!)" VERBATIM)
174+
141175
ENDIF(WITH_QSCIAPI)
142176

143177
# Plugin utilities files to copy to staging or install

python/console/console_sci.py

+12-1
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,18 @@ def setLexers(self):
192192
chekBoxAPI = self.settings.value("pythonConsole/preloadAPI", True, type=bool)
193193
chekBoxPreparedAPI = self.settings.value("pythonConsole/usePreparedAPIFile", False, type=bool)
194194
if chekBoxAPI:
195-
self.api.loadPrepared(QgsApplication.pkgDataPath() + "/python/qsci_apis/pyqgis_master.pap")
195+
apisdir = os.path.join(QgsApplication.pkgDataPath(), "python", "qsci_apis")
196+
pap = os.path.join(apisdir, "pyqgis.pap")
197+
mpap = os.path.join(apisdir, "pyqgis-master.pap")
198+
if os.path.exists(mpap): # override installed with master .pap build
199+
pap = mpap
200+
if QgsApplication.isRunningFromBuildDir():
201+
bdir = os.path.dirname(QgsApplication.buildOutputPath())
202+
bpap = os.path.join(bdir, "python", "qsci_apis", "pyqgis-master.pap")
203+
if os.path.exists(bpap):
204+
# if not generated .pap exists, else fall back to preprepared one
205+
pap = bpap
206+
self.api.loadPrepared(pap)
196207
elif chekBoxPreparedAPI:
197208
self.api.loadPrepared(self.settings.value("pythonConsole/preparedAPIFile"))
198209
else:
+96
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
# -*- coding:utf-8 -*-
2+
"""
3+
/***************************************************************************
4+
Module to generate prepared APIs for calltips and auto-completion.
5+
-------------------
6+
begin : 2013-08-29
7+
copyright : (C) 2013 Larry Shaffer
8+
email : larrys (at) dakotacarto (dot) com
9+
***************************************************************************/
10+
11+
/***************************************************************************
12+
* *
13+
* This program is free software; you can redistribute it and/or modify *
14+
* it under the terms of the GNU General Public License as published by *
15+
* the Free Software Foundation; either version 2 of the License, or *
16+
* (at your option) any later version. *
17+
* *
18+
***************************************************************************/
19+
Portions of this file contain code from Eric4 APIsManager module.
20+
"""
21+
22+
import sys
23+
import os
24+
25+
from PyQt4.Qsci import QsciLexerPython, QsciAPIs
26+
from PyQt4.QtGui import QApplication
27+
from PyQt4.QtCore import QObject
28+
29+
30+
class PrepareAPIs(QObject):
31+
def __init__(self, api_lexer, api_files, pap_file):
32+
QObject.__init__(self)
33+
self._api = None
34+
self._api_files = api_files
35+
self._api_lexer = api_lexer
36+
self._pap_file = pap_file
37+
38+
def _clearLexer(self):
39+
self.qlexer = None
40+
41+
def _stopPreparation(self):
42+
if self._api is not None:
43+
self._api.cancelPreparation()
44+
self._api = None
45+
sys.exit(1)
46+
47+
def _preparationFinished(self):
48+
self._clearLexer()
49+
try:
50+
if os.path.exists(self._pap_file):
51+
os.remove(self._pap_file)
52+
prepd = self._api.savePrepared(unicode(self._pap_file))
53+
self._api = None
54+
sys.exit(0 if prepd else 1)
55+
except Exception, err:
56+
self._api = None
57+
sys.exit(1)
58+
59+
def prepareAPI(self):
60+
try:
61+
self._api = QsciAPIs(self._api_lexer)
62+
self._api.apiPreparationFinished.connect(self._preparationFinished)
63+
for api_file in self._api_files:
64+
self._api.load(unicode(api_file))
65+
self._api.prepare()
66+
except Exception, err:
67+
self._api = None
68+
sys.exit(1)
69+
70+
71+
if __name__ == '__main__':
72+
if len(sys.argv) != 4:
73+
print 'Usage: python <script> <pap_name_w-ext> ' \
74+
'<APIs_dir_path> <output_dir>'
75+
sys.exit(1)
76+
pap_name = sys.argv[1]
77+
api_src_dir = sys.argv[2]
78+
output_dir = sys.argv[3]
79+
80+
api_files = [
81+
os.path.join(output_dir, 'PyQGIS.api'),
82+
os.path.join(api_src_dir, 'Python-2.7.api'),
83+
os.path.join(api_src_dir, 'PyQt4-4.7.4.api'),
84+
os.path.join(api_src_dir, 'OSGeo_GDAL-OGR-1.9.1.api')
85+
]
86+
pap_file= os.path.join(output_dir, pap_name)
87+
88+
# print api_files.__repr__()
89+
# print pap_file.__repr__()
90+
91+
app = QApplication(sys.argv, False) # just start a non-gui console app
92+
api_lexer = QsciLexerPython()
93+
prepap = PrepareAPIs(api_lexer, api_files, pap_file)
94+
prepap.prepareAPI()
95+
96+
sys.exit(app.exec_())

python/qsci_apis/pyqgis.pap

750 KB
Binary file not shown.

python/qsci_apis/pyqgis_master.pap

-919 KB
Binary file not shown.

0 commit comments

Comments
 (0)