2

I was expecting autocomplete suggestions of Python keywords (i.e. for, range, lambda) and hoping for autocomplete suggestions with standard library modules and functions (i.e. sys, os.path, etc.) when setting up an api with the QsciLexerPython lexer, but in the code example below, compressed and updated from this website, the only auto-completion that happens are the four strings added to the list.

Am I doing something wrong, and if so how do I get python keywords to register in the autocomplete? (My main question here of course)

If rather this is the expected behavior, what role does the lexer play in the autocomplete and why are the keywords from the lexer not being assigned to the autocomplete lists in this example?

Lastly, is it possible to have python standard library modules and sub-modules in the autocomplete, and what does that require?

"""Base code originally from: http://kib2.free.fr/tutos/PyQt4/QScintilla2.html"""

import sys
from PyQt5 import QtWidgets, Qsci

app = QtWidgets.QApplication(sys.argv)
editor = Qsci.QsciScintilla()
lexer = Qsci.QsciLexerPython()
editor.setLexer(lexer)

## setup autocompletion
api = Qsci.QsciAPIs(lexer)
api.add("aLongString")
api.add("aLongerString")
api.add("aDifferentString")
api.add("sOmethingElse")
api.prepare()
editor.setAutoCompletionThreshold(1)
editor.setAutoCompletionSource(Qsci.QsciScintilla.AcsAPIs)

editor.show()
editor.setText(open(sys.argv[0]).read())
sys.exit(app.exec_())
Vince W.
  • 3,561
  • 3
  • 31
  • 59
  • 2
    There is no need to add the keywords directly. There are python api files provided by the standard qscintilla installation that can be loaded at runtime. You can also load api files for pyqt and for qscintilla itself. – ekhumoro Oct 11 '17 at 21:43
  • @ekhumoro Looking in the package I see those api files, but I am uncertain about what is the function call to load them. Could you provide an example? – Vince W. Oct 11 '17 at 21:52
  • Thanks for the comment, I went looking more closely at the class documentation here and found what I was looking for. See answer below. http://pyqt.sourceforge.net/Docs/QScintilla2/classQsciAPIs.html – Vince W. Oct 11 '17 at 22:06
  • Sorry - I was going to add an answer but you beat me to it :) – ekhumoro Oct 11 '17 at 22:12

2 Answers2

3

Thanks to ekhumoro for guiding me to the answer. The QsciAPIs class has a load method, and PyQt comes with a set of api files. Below is the code that does proper autocompletion in the manner I was looking for:

"""Base code originally from: http://kib2.free.fr/tutos/PyQt4/QScintilla2.html"""

import sys
import os
import PyQt5
from PyQt5 import QtWidgets, Qsci

app = QtWidgets.QApplication(sys.argv)
editor = Qsci.QsciScintilla()
lexer = Qsci.QsciLexerPython(editor)
editor.setLexer(lexer)

## setup autocompletion
api = Qsci.QsciAPIs(lexer)

# import the desired api file
pyqt_path = os.path.dirname(PyQt5.__file__)
api.load(os.path.join(pyqt_path, "Qt/qsci/api/python/Python-3.6.api"))

api.prepare()
editor.setAutoCompletionThreshold(1)
editor.setAutoCompletionSource(Qsci.QsciScintilla.AcsAll)

editor.show()
editor.setText(open(sys.argv[0]).read())
sys.exit(app.exec_())
Vince W.
  • 3,561
  • 3
  • 31
  • 59
  • 2
    Note that you can get a list of all installed api files from `api.installedAPIFiles()`. If you want the language keywords for python, you can add them individually from `lexer.keywords(1).split()`. – ekhumoro Oct 11 '17 at 22:11
  • Hi @ekhumoro , could you give more explanation about what the `api.load(..)` function actually does? From what I understand, it clones the keywords from the given api-file. Will `api.installedAPIFiles()` just return those filenames that have been cloned before? I would like to add your explanations to my website https://qscintilla.com/autocompletion/ – K.Mulier Nov 08 '17 at 19:48
  • @ekhumoro , if you agree, I will make proper reference to your StackOverflow account when adding your explanations to the website ;-) – K.Mulier Nov 08 '17 at 19:49
  • @K.Mulier. The raw api files are installed in a standard system location (usually in qt's shared-data directory). The `api.load()` method reads a raw api file, and stores it in an internal list. The `api.prepare()` method does the actual work of parsing the lines of raw data and storing the information in a format that can be accessed efiiciently. The prepared data can be saved using `api.savePrepared()` and re-loaded with `api.loadPrepared()`. These prepared data files are usually stored by the application in a config directory on a per-user basis. – ekhumoro Nov 08 '17 at 20:26
  • @K.Mulier. Thank for the offer, but I am mostly just passing on information that I have found elsewhere. You are doing all the real work of creating and maintaining the website. If you want to give credit where it's due, please make sure you prominently acknowledge [Phil Thompson (Riverbank Computing)](https://www.riverbankcomputing.com/software/qscintilla/intro), as he is the author of QScintilla and also provides the PyQt bindings. You should probably also provide a prominent link to the [official QScintilla Documentation](http://pyqt.sourceforge.net/Docs/QScintilla2/). – ekhumoro Nov 08 '17 at 20:49
  • Hi @ekhumoro . Thank you for your answer. I have added a link on my homepage to the fundraising site of Neil Hodgson (the creator of Scintilla). Thank you for pointing out the work of Mr. Phil Thompson. As you explain, without Mr. Thompson we couldn't use Scintilla in Python (and PyQt). Do you know of a fundraising page he has for his work on QScintilla and the PyQt bindings? – K.Mulier Nov 08 '17 at 20:54
  • @K.Mulier. QScintilla does **much** more than simply wrap Scintilla for Qt. Your [introduction page](https://qscintilla.com/introduction/) is really misleading on that point. See the [QScintilla Licence Page](https://www.riverbankcomputing.com/software/qscintilla/license) regarding its commercial status. It also seems somewhat strange to mention Nokia - very few people will remember that episode in Qt's history with much fondness. Qt is currently owned by [Digia/The Qt Company](https://en.wikipedia.org/wiki/The_Qt_Company). – ekhumoro Nov 08 '17 at 21:08
  • Hi @ekhumoro , I am happy to get your feedback on the website. I got the impression that QScintilla is doing only a wrap of Scintilla for Qt (or PyQt in particular). But apparently, my impression is wrong, and QScintilla is doing more than that. Please share me some more information so I can correct the intro on my website. – K.Mulier Nov 10 '17 at 13:27
  • @K.Mulier. I don't want to make too much of this, but I think that, at the very least, it's just common courtesy to acknowledge the principal author of QScintilla. If you're going to put up a website called "QScintilla" that uses a "qscintilla.com" domain, I think the onus is on you to make sure you are respecting the author's copyright. If you want to know what QScintilla is comprised of, you can download the source code and see for yourself. There is also a [QScintilla mailing list](https://www.riverbankcomputing.com/mailman/listinfo/qscintilla). – ekhumoro Nov 10 '17 at 17:31
  • Hi @ekhumoro, you are right. I should do that. Where can I find info about the stuff QScintilla adds on top of Scintilla (besides reading both of the source codes)? – K.Mulier Nov 10 '17 at 17:34
  • @K.Mulier. The [class list](http://pyqt.sourceforge.net/Docs/QScintilla2/annotated.html) should give you broad view of what is included. Really, the high-level APIs of QScintilla are more akin to SciTE, than to Scintilla. The APIs of Scintilla are mostly very low-level. A lot of layers have to be added on top of it in order to make use of all of its features. QScintilla was originally created purely for use in the [Eric Python IDE](https://eric-ide.python-projects.org/) - but it's since taken on a life of its own. – ekhumoro Nov 10 '17 at 17:47
  • Interesting, didn't know that QScintilla was originally created for Eric Python IDE :-) – K.Mulier Nov 10 '17 at 17:48
  • How did you learn all this stuff? Do you have a personal project (perhaps an IDE) based on QScintilla? – K.Mulier Nov 10 '17 at 17:49
  • @K.Mulier. I have been using both PyQt and QScintilla in various ways since around the time Qt3 was first released (2002-ish). So I suppose I have just steadily accumulated knowledge over rmany years. There's been no real plan to any of it, though ;-) (PS: I think we should probably end this discussion here, as we are highjacking Vince W's answer). – ekhumoro Nov 10 '17 at 18:03
  • Yeah, you're right. Perhaps we can continue over email? I'll put my email here, you note it down, and then I remove this comment immediately: kristof dot mulier at telenet dot be – K.Mulier Nov 10 '17 at 18:05
  • @K.Mulier. A chat room on SO would be better. – ekhumoro Nov 10 '17 at 18:09
  • Don't know.. didn't use it that much so far. Will the messages we exchange on the chatroom stay present for future reference? I mean, I like to be able to look up conversations if I forgot something interesting. With mail, that's possible. – K.Mulier Nov 10 '17 at 18:30
  • @K.Mulier, and ekhumoro, just to let you know, I've enjoyed reading the back and forth. Curiosity never dies. As a side note, I asked this question after checking qscintilla.com and not finding the answer, so I'm glad it sparked some discussion – Vince W. Nov 10 '17 at 20:24
  • @VinceW. great to hear that :-) – K.Mulier Nov 10 '17 at 20:30
1

qscintilla does not know the keywords of python nor of the libraries, QsciAPIs hopes that you provide information, in the following part I show some functions that return the keywords and the name of the standard libraries. qscintilla will only autocomplete with the words you provide, if you want an intelligent autocomplete, I recommend you do a logic that recognizes the points or parenthesis and filter the words you provide to QsciAPIs.

import sys
from PyQt5 import QtWidgets, Qsci
import keyword
import pkgutil

app = QtWidgets.QApplication(sys.argv)
editor = Qsci.QsciScintilla()
lexer = Qsci.QsciLexerPython()
editor.setLexer(lexer)

## setup autocompletion
api = Qsci.QsciAPIs(lexer)

for key in keyword.kwlist + dir(__builtins__):
    api.add(key)

for importer, name, ispkg in pkgutil.iter_modules():
    api.add(name)

api.prepare()

editor.setAutoCompletionThreshold(1)
editor.setAutoCompletionSource(Qsci.QsciScintilla.AcsAPIs)

editor.show()
editor.setText(open(sys.argv[0]).read())
sys.exit(app.exec_())
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • thanks. Could you explain what role the lexer is playing in the autocomplete? Also, is it relatively simple to implement standard library module autocomplete with functions from the standard library as well? – Vince W. Oct 11 '17 at 21:20
  • I have updated my answer, I added the functionality of knowing the names of the libraries but it is not a smart autocomplete. Please if my answer helps you do not forget to mark it as correct. – eyllanesc Oct 11 '17 at 21:32
  • the answer is in the right direction, but has some undesireable consequences. For example, if I set the autcompletion source to AcsAll so that it includes document words as well as the api, autocompletion makes sense for document words. Namely, when I type `sys.` it suggests `sys.argv` and `sys.exit` bc they are in the document. When I type `range.r`, it will suggest `range.range` and `range.raise` which obviously don't make sense. I'm not sure that an autocomplete which sometimes suggests wrong things is what I'm looking for. – Vince W. Oct 11 '17 at 21:51
  • To that I meant that it is not an intelligent autocomplete, you must filter the data that gives the QsciAPIs. – eyllanesc Oct 11 '17 at 21:53