Fer*_*ndr 7 python pyqt pycharm
假设我在 Qt Designer 中创建了一个 ui 文件,我想动态加载它然后操作小部件,例如:
例子.py:
from PyQt5 import QtWidgets, uic
class MyWidget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(MyWidget, self).__init__(parent)
uic.loadUi('example.ui', self)
# No code completion here for self.myPushButton:
self.myPushButton.clicked.connect(self.handleButtonClick)
self.show()
Run Code Online (Sandbox Code Playgroud)
在 PyCharm (2017.1.4) 中,是否有标准/方便的方式为以这种方式加载的小部件启用代码完成?
目前我正在使用这个(在加载 ui 文件后在构造函数中编写):
self.myPushButton = self.myPushButton # type: QtWidgets.QPushButton
# Code completion for myPushButton works at this point
Run Code Online (Sandbox Code Playgroud)
我也想到了这一点,但它似乎并没有做到这一点:
assert isinstance(self.myPushButton, QtWidgets.QPushButton)
# PyCharm does not even recognise myPushButton as an attribute of self at this point
Run Code Online (Sandbox Code Playgroud)
最后,我也想到了使用python stubs,比如:
例子.pyi:
class MyWidget(QtWidgets.QWidget):
def __init__(self):
self.myPushButton: QtWidgets.QPushButton = ...
Run Code Online (Sandbox Code Playgroud)
但是,在 example.py 之外的代码中正确识别了 myPushButton,但在 example.py 本身内部的代码中无法正确识别,这与我想要的相反。
我也在考虑采用我的第一种方法,但所有这些行都放在一个永远不会被调用的私有方法中,例如:
例子.py:
from PyQt5 import QtWidgets, uic
class MyWidget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(MyWidget, self).__init__(parent)
uic.loadUi('example.ui', self)
# Code completion now works here for self.myPushButton:
self.myPushButton.clicked.connect(self.handleButtonClick)
self.show()
def __my_private_method_never_called():
self.myPushButton = self.myPushButton # type: QtWidgets.QPushButton
# Or even this (it should have the same effect if this
# function is never called, plus it is less verbose):
self.myPushButton = QtWidgets.QPushButton()
# If I want to make sure that this is never called
# could raise an error at some point:
raise YouShouldNotHaveCalledThisError()
Run Code Online (Sandbox Code Playgroud)
这似乎工作正常,它还允许我将所有类型提示代码组合在一起,与其余代码隔离。我什至可以通过解析 ui 文件来制作一些脚本来为我编写所有这些行。我只是想知道阅读我的代码的人是否会发现这种方法非常非正统,即使我清楚地评论了为什么我要编写技术上无用的私有函数。
If anybody is interested, I made the script I mentioned to parse the .ui files and generate stub code ready to be copied to my class:
ui_stub_generator.py:
from __future__ import print_function
import os
import sys
import xml.etree.ElementTree
def generate_stubs(file):
root = xml.etree.ElementTree.parse(file).getroot()
print('Stub for file: ' + os.path.basename(file))
print()
print(' def __stubs(self):')
print(' """ This just enables code completion. It should never be called """')
for widget in root.findall('.//widget'):
name = widget.get('name')
if len(name) > 3 and name[:2] == 'ui' and name[2].isupper():
cls = widget.get('class')
print(' self.{} = QtWidgets.{}()'.format(
name, cls
))
print(' raise AssertionError("This should never be called")')
print()
def main():
for file in sys.argv[1:]:
generate_stubs(file)
if __name__ == '__main__':
main()
Run Code Online (Sandbox Code Playgroud)
This only parses widgets whose names start with 'ui' followed by an uppercase letter, such as 'uiMyWidget', which is the naming convention that I typically follow in the Qt Designer. By doing this, the widgets with names automatically generated by the Qt Designer are ignored (if I cared about these, I would have given them a proper name). It should be straightforward to update this for any other naming conventions, or other type of objects, such as actions.
For convenience, I have set this up as an external tool in PyCharm as well; see screenshot here (change the paths as appropriate). That way, I only have to right-click my ui file in the project window, then External Tools -> Stub Generator for Qt UI Files, and I get the following output in the Run window ready to be copied:
from __future__ import print_function
import os
import sys
import xml.etree.ElementTree
def generate_stubs(file):
root = xml.etree.ElementTree.parse(file).getroot()
print('Stub for file: ' + os.path.basename(file))
print()
print(' def __stubs(self):')
print(' """ This just enables code completion. It should never be called """')
for widget in root.findall('.//widget'):
name = widget.get('name')
if len(name) > 3 and name[:2] == 'ui' and name[2].isupper():
cls = widget.get('class')
print(' self.{} = QtWidgets.{}()'.format(
name, cls
))
print(' raise AssertionError("This should never be called")')
print()
def main():
for file in sys.argv[1:]:
generate_stubs(file)
if __name__ == '__main__':
main()
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1148 次 |
| 最近记录: |