Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Elegant command line argument parsing for PyQt

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)
like image 447
troy.unrau Avatar asked Jul 29 '12 21:07

troy.unrau


People also ask

How are command-line arguments parsed?

Argument Parsing using sys. Your program will accept an arbitrary number of arguments passed from the command-line (or terminal) while getting executed. The program will print out the arguments that were passed and the total number of arguments. Notice that the first argument is always the name of the Python file.

How do you parse command line arguments in Java?

To parse command line parameters, Commons CLI uses the DefaultParser implementation of the CommandlineParser interface. DefaultParser has a method called parse() which accepts the options object and the args from the command line. Finally, you can use the HelpFormatter to print the help information to the user.

How do you pass arguments to Argparse?

After importing the library, argparse. ArgumentParser() initializes the parser so that you can start to add custom arguments. To add your arguments, use parser. add_argument() .


2 Answers

The best solution here is using the argparse module's parse_known_args() method (docs) to process the non-Qt command line options first. It's no more work in configuring the ArgumentParser—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.

# my_script.py

import argparse
from PyQt4 import QtGui  # this will work with PySide.QtGui, too

def process_cl_args():
    parser = argparse.ArgumentParser()
    parser.add_argument('-s', '--swallow', action='store')  # optional flag
    parser.add_argument('holy_hand_grenade', action='store')  # positional argument

    parsed_args, unparsed_args = parser.parse_known_args()
    return parsed_args, unparsed_args

if __name__ == '__main__':
    parsed_args, unparsed_args = process_cl_args()
    # QApplication expects the first argument to be the program name.
    qt_args = sys.argv[:1] + unparsed_args
    app = QtGui.QApplication(qt_args)
    # ... the rest of your handling: `sys.exit(app.exec_())`, etc.

Assume you were to run this like so:

$ python my_script.py -dograb --swallow=unladen 3 -style cde

Then parsed_args would have the usual Namespace with holy_hand_grenade set to 3 and --swallow set to 'unladen', while unparsed_args would have a simple list: ['-dograb', '-style,' 'cde']. This in turn can be passed normally to the QApplication with the inclusion of the program name from sys.argv[0] (thanks to marcin for pointing this out). We use sys.argv[:1] to get an array for concatenation with unparsed_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.

like image 153
Chris Krycho Avatar answered Sep 30 '22 15:09

Chris Krycho


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.

like image 30
aayoubi Avatar answered Sep 30 '22 13:09

aayoubi