PyQt的优雅命令行参数解析

12

我正在编写一个新的PyQt应用程序。我试图尽可能使用PyQt API来处理与程序和UI相关的所有内容,以提高我的PyQt和Qt知识。

我的问题是,在PyQt/Qt中是否有API可以优雅地处理命令行参数解析?

到目前为止,我的研究结果有:

  • 一个示例,展示如何与Python的opt_parser模块良好配合,但它不处理QApplication内置的arg解析。
  • PyKDE的KCmdLineArgs(引入了不需要的KDE依赖)
  • 看起来KCmdLineArgs正在被上游移植为Qt5.1的QCommandLineParser,这很酷,但我想现在就能使用它,而不是18个月后。

那么PyQt应用程序通常如何处理这个问题呢?opt_parser / argparse是正确的方法吗?

这远非完美的解决方案...

#!/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)

2
我在lateral.netmanagers.com.ar上发布的帖子的重点是它确实处理了QApplication的内置命令行解析。您可以使用opt_parse/argparse/等定义自己的参数,只要您不定义与QApplication使用的相同参数,一切都可以正常工作。 - Roberto Alsina
3个回答

10
这里最好的解决方案是使用argparse模块的parse_known_args()方法(docs)先处理非Qt命令行选项。在配置ArgumentParser时不需要额外工作,只需更改调用的方法,并在返回中给出一个元组而不是单个对象。这样可以兼顾两全其美。
下面是一个简化的示例,仅捕获了一些Qt 4.8参数和其他一些参数,但可以给出一般想法。
# 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.

假设您这样运行:
$ python my_script.py -dograb --swallow=unladen 3 -style cde

parsed_args将包含通常的Namespace,其中holy_hand_grenade设置为3--swallow设置为'unladen',而unparsed_args将只是一个简单的列表:['-dograb', '-style,' 'cde']。这可以正常传递给QApplication,并包括sys.argv[0]中的程序名称(感谢marcin指出此问题)。我们使用sys.argv[:1]获取一个数组,用于与unparsed_args连接;你也可以做[sys.argv[0]]

除其他外,这使得您可以设置应用程序以指定是否启动Qt UI、作为命令行运行、运行单元测试等,如果您愿意的话。首先处理非Qt(或其他任何内容)参数更好,因为它不会使argparse依赖于您正在使用的设置,而反过来则不行。

如果我想让Qt应用程序访问已解析的参数怎么办? - qed
在这种情况下,您可以直接传递 sys.argv 而不是 unparsed_args - Chris Krycho
我可以在QApplication中使用argparse吗? - qed
好的,我明白了。parsed_args在应用程序内是可访问的。谢谢。 - qed
没问题!当然,你也可以在整个应用程序中随意使用 parsed_args - Chris Krycho
最好使用QApplication(sys.argv[:1] + unparsed_args),因为列表中的第一个项应该是程序名称,而不是选项。 - marcin

5

如果您使用的是 Python 2.7(小于 2.7 使用optparse),请使用argparse来处理命令行选项。虽然包不必特定于PyQt。


2
+1,因为在Python标准库中已经有一个完整的功能模块,没有必要使用pyqt提供的设施。 - jdi
2
好的,我研究了argparse,发现它很不错,但是它不能解决与QApplication内置参数解析器相协作的问题。例如:如果你在命令行中运行myPyQyApp.py -style cde,QApplication将拦截该参数并使一切看起来像1996年,但argparse并不知道这正在发生。这就是为什么我正在寻找一个PyQt的解决方案。如果没有这样的解决方案,那就算了,但是stdlib的解决方案并不理想。 - troy.unrau
2
如果您不想让Qt解析命令行,只需按照以下方式创建应用程序:app = QtGui.QApplication([]) - pwuertz
我不介意Qt选项。特别是我发现以下选项非常有用于测试:-style(用于捕获布局和绘图故障 - 有时更改样式会使这些问题变得明显)和-reverse(用于i18n)。 - troy.unrau
3
好的,经过花费了很多时间试图找到更好的方法之后,我将使用我在原问题中发布的临时解决方法,并接受您的答案,即argparse是最好的选择。这并不意味着我对此感到非常兴奋,并会标记该代码以便在Qt 5.1发布时进行替换。尽管如此,我仍然非常感谢您的帮助。 - troy.unrau

0

你真的应该尝试一下docopt。它不需要任何参数定义。你只需要将“Usage:”字符串和未解析的参数作为输入传递给docopt()函数,然后你就可以得到解析后的参数作为输出。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接