I'm writing a new PyQt app. I'm trying to do everything related to the program and ui using as much of the PyQt APIs as possible as a means to improve my knowledge of PyQt and Qt in general.
The question I have is, is there an API within PyQt/Qt to handle command line argument parsing elegantly?
My research so far has turned up:
- an example of how to make it play nice with python's opt_parser module, except it doesn't handle QApplication's built in arg parsing.
- PyKDE's KCmdLineArgs (which introduces an unwanted KDE dependency)
- it looks like KCmdLineArgs is being ported upstream for Qt5.1 as QCommandLineParser, which is cool, but I'd like to be able to use it now, not 18 months from now.
So how do PyQt applications normally handle this? or is opt_parser/argparse the way to go?
This is far from a nice solution...
#!/usr/bin/python
# -*- coding: utf-8 -*-
import sys, argparse
from PyQt4 import QtGui
def main(argv):
app = QtGui.QApplication(argv) # QApplication eats argv in constructor
# We can get a QStringList out of QApplication of those arguments it
# didn't decide were reserved by Qt.
argv2 = app.arguments()
# now we need to turn them back into something that optparse/argparse
# can understand, since a QStringList is not what it wants
argv3 = []
for i in argv2:
argv3.append(str(i))
# now we can pass this to optparse/argparse
process_args(argv3)
# dummy app
mw = QtGui.QMainWindow()
mw.show()
sys.exit(app.exec_())
def process_args(argv):
parser = argparse.ArgumentParser(description='PyQt4 argstest',
add_help=False)
# we now have to add all of the options described at
# http://qt-project.org/doc/qt-4.8/qapplication.html#QApplication
# but have them do nothing - in order to have them show up in the help list
# add this to the list if Qt is a debug build (How to detect this?)
parser.add_argument("-nograb", action=ignore,
help="don't grab keyboard/mouse for debugging")
# add these to the list if Qt is a debug build for X11
parser.add_argument("-dograb", action=ignore,
help="grab keyboard/mouse for debugging")
parser.add_argument("-sync", action=ignore,
help="run in synchronous mode for debugging")
# add all the standard args that Qt will grab on all platforms
parser.add_argument("-reverse", action=ignore,
help="run program in Right-to-Left mode")
# an example -- there are 10 such items in the docs for QApplication
# then we need to figure out if we're running on X11 and add these
parser.add_argument("-name", action=ignore,
help="sets the application name")
# an example -- there are 13 such items in the docs
# reimplement help (which we disabled above) so that -help works rather
# than --help; done to be consistent with the style of args Qt wants
parser.add_argument("-h", "-help", action='help',
help="show this help message and exit")
parser.parse_args(argv[1:])
class ignore(argparse.Action):
# we create an action that does nothing, so the Qt args do nothing
def __call__(self, parser, namespace, values, option_string=None):
pass
if __name__ == "__main__":
main(sys.argv)
You should really try docopt. It doesn't need any argument definitions. You have "Usage: " string and unparsed arguments as input, pass them into docopt() function, and you have parsed arguments as output.
The best solution here is using the
argparse
module'sparse_known_args()
method (docs) to process the non-Qt command line options first. It's no more work in configuring theArgumentParser
—it just changes which method you call, and gives you a tuple instead of a single object in the return. That gives you the best of both worlds.A simplified example that catches only a couple of the Qt 4.8 arguments and a few others, but gives the general idea.
Assume you were to run this like so:
Then
parsed_args
would have the usualNamespace
withholy_hand_grenade
set to3
and--swallow
set to'unladen'
, whileunparsed_args
would have a simple list:['-dograb', '-style,' 'cde']
. This in turn can be passed normally to theQApplication
with the inclusion of the program name fromsys.argv[0]
(thanks to marcin for pointing this out). We usesys.argv[:1]
to get an array for concatenation withunparsed_args
; you could just as well do[sys.argv[0]]
Among other things, this lets you set up your application to specify whether to launch the Qt UI, to run as a command line, to run unit tests, and so forth instead if you so desired. Handling the non-Qt (or anything else) arguments first is better because it does not leave
argparse
dependent on the setup you're using, but the other way around.Use argparse if you're using
Python 2.7
(optparse if <2.7
), the package doesn't have to be specific to PyQt for you to handle commandline options.