上一节里说过把解释器调整到默认的python3.8.3后可以做很多事, 本文就演示一下在Inkscape插件里如何使用Qt做输入界面, 并用全局变量方式传递参数, 实现修改svg的目的. 关于如何在python里使用QT做界面, 网上有很多教程, 这里不详细说, 我安装Qt Designer遇到的一点小问题可以参阅另一篇文章. https://blog.csdn.net/majian/article/details/106792573 , 总之这个方法使用的前提是安好Qt Designer, python能运行pytqt5 , 我用vscode编辑.
首先在Qt Designer里设计一个如图的界面保存, 在vscode里打开这个ui文件, 转换为py文件(在vscode里安装 PYQT Integration 插件), 我直接贴出转换后的py文件, 没装PYQT或者Qt Designer启动不成功的同学直接用这个py文件也可以 .
PYQT
文件名: Ui_InkQtDemo.py
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'd:\Inkscape\share\inkscape\extensions\MyExtension\InkQtDemo.ui'
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_Form(object):
def setupUi(self, Form):
Form.setObjectName("Form")
Form.resize(258, 178)
self.lineEdit = QtWidgets.QLineEdit(Form)
self.lineEdit.setGeometry(QtCore.QRect(80, 70, 41, 31))
self.lineEdit.setObjectName("lineEdit")
self.lineEdit_2 = QtWidgets.QLineEdit(Form)
self.lineEdit_2.setGeometry(QtCore.QRect(150, 70, 51, 31))
self.lineEdit_2.setObjectName("lineEdit_2")
self.label = QtWidgets.QLabel(Form)
self.label.setGeometry(QtCore.QRect(10, 80, 72, 15))
self.label.setObjectName("label")
self.label_2 = QtWidgets.QLabel(Form)
self.label_2.setGeometry(QtCore.QRect(130, 70, 31, 21))
self.label_2.setObjectName("label_2")
self.label_3 = QtWidgets.QLabel(Form)
self.label_3.setGeometry(QtCore.QRect(20, 10, 331, 31))
self.label_3.setObjectName("label_3")
self.label_4 = QtWidgets.QLabel(Form)
self.label_4.setGeometry(QtCore.QRect(20, 40, 191, 16))
self.label_4.setObjectName("label_4")
self.pushButton = QtWidgets.QPushButton(Form)
self.pushButton.setGeometry(QtCore.QRect(80, 120, 101, 41))
self.pushButton.setObjectName("pushButton")
self.retranslateUi(Form)
QtCore.QMetaObject.connectSlotsByName(Form)
def retranslateUi(self, Form):
_translate = QtCore.QCoreApplication.translate
Form.setWindowTitle(_translate("Form", "Form"))
self.lineEdit.setText(_translate("Form", "0"))
self.lineEdit_2.setText(_translate("Form", "0"))
self.label.setText(_translate("Form", "请输入 X"))
self.label_2.setText(_translate("Form", "Y"))
self.label_3.setText(_translate("Form", "这是QT界面输入参数演示"))
self.label_4.setText(_translate("Form", "XY值决定path显示位置"))
self.pushButton.setText(_translate("Form", "PushButton"))
第二步, 在Python里把这个界面运行起来, 我的主程序 MyQtDemo.py
#!/usr/bin/env python
# coding=utf-8
import sys
# Qt所需
from PyQt5.QtWidgets import *
# #这个是ui文件对应的py文件的文件名
from Ui_InkQtDemo import Ui_Form
#我的Form是用的QWidget作为基类
class MyWindow(QWidget,Ui_Form):
def __init__(self, parent=None):
super().__init__(parent)
self.setupUi(self)
self.pushButton.clicked.connect(self.OnClick_pushButton) #设置按钮和事件的连接
def OnClick_pushButton(self):
self.close() #点击后关闭窗口
if __name__ == '__main__':
app=QApplication(sys.argv)
w=MyWindow()
w.show()
sys.exit(app.exec_())
先试运行这个py程序, 正常情况下会弹出 QtDesigner里设计的那个窗口. 成功后再添加几行代码把输入值保存到一个全局变量InputXY里.
def OnClick_pushButton(self):
x = self.lineEdit.text()
y = self.lineEdit_2.text()
global inputXY
inputXY={"x":x, "y":y } # 用字典的方式把输入值保存到全局变量
以上的步骤均可以在python里直接调试, 不用打开Inkspace. 这是我想提高效率的地方, 不涉及到图层元素操作的算法,设置类代码就不用self.msg, 那个效率太低. 好了, 现在可以加入 Inkex代码了. 加入一个Inkex类,
class MyExtension(inkex.EffectExtension):
def effect(self):
global inputXY
x = inputXY["x"]
y = inputXY["y"]
直接运行编译不成功, 提示: class MyExtension(inkex.EffectExtension): NameError: name 'inkex' is not defined
没有 import 当然不行, 添加两行 import:
import inkex
from inkex.elements import Group, PathElement
这回错误提示变成 : ModuleNotFoundError: No module named 'inkex' , 找不到模块, 这是因为默认python环境里没有inkex的查找路径, 所以在 import inkex前面 添加一行把搜索路径加入进去 (注意修改为你自己的Inkscape安装路径 ) :
sys.path.append("D:\Inkscape\share\inkscape\extensions")
再次运行, 成功弹出窗口, 说明我们已经成功地在python运行起inkex/ Qt混合程序了. 这时在effect()里添加图层相关代码直接在python跑会没反应,最后程序挂掉, (如果inkex无关则一切正常) , 所以后面的步骤还是要进入Inkscape执行, 用self.msg调试
编辑一个MyQtDemo.inx, 把py和inx一起放入到extensions目录里. MyQtDemo.inx 内容如下
<?xml version="1.0" encoding="UTF-8"?>
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
<name>Qt Input Example</name>
<id>org.inkscape.filter.qt_input_example</id>
<effect>
<object-type>path</object-type>
<effects-menu>
<submenu name="AAA Tools"/>
</effects-menu>
</effect>
<script>
<command location="inx" interpreter="python">MyQtDemo.py</command>
</script>
</inkscape-extension>
完整的 MyQtDemo.py
#!/usr/bin/env python
# coding=utf-8
import sys
sys.path.append("D:\Inkscape\share\inkscape\extensions")
# # #inkscape 插件所需
import inkex
from inkex.elements import Group, PathElement
# Qt所需
from PyQt5.QtWidgets import *
# #这个是ui文件对应的py文件的文件名
from Ui_InkQtDemo import Ui_Form
# 全局变量
inputXY={}
class MyExtension(inkex.EffectExtension):
def effect(self):
global inputXY
x = inputXY["x"]
y = inputXY["y"]
# self.msg(x)
# self.msg(y)
cur_layer = self.svg.get_current_layer()
my_shape = PathElement()
my_shape.style = {'stroke': 'red', 'stroke-width': '2', 'fill': 'none'}
my_shape.path = " M " + str(x) +" " + str(y) +"L 150 190 L 120 15 z"
my_shape.set_id ("QtDemoPath") # path id
cur_layer.append( my_shape ) # 添加一个路径
class MyWindow(QWidget,Ui_Form):
def __init__(self, parent=None):
super().__init__(parent)
self.setupUi(self)
self.pushButton.clicked.connect(self.OnClick_pushButton)
def OnClick_pushButton(self):
x = self.lineEdit.text()
y = self.lineEdit_2.text()
global inputXY
inputXY={"x":x, "y":y } # 用字典的方式把输入值保存到全局变量
self.close()
MyExtension().run()
if __name__ == '__main__':
app=QApplication(sys.argv)
w=MyWindow()
w.show()
sys.exit(app.exec_())
打开Inkscape, 主菜单 -> 扩展 -> AAA Tools -> Qt Input Example, 在弹出界面里输入x,y值, 会显示一个三角形, 形状因输入值不同而变化, 而在图层上也会看到path的id是 "QtDemoPath"
当然这个方法也有个缺陷, 就是参数没保存到 preferences.xml 里面, 如果按快捷键 alt+Q, 还是会弹出界面要求输入参数, 这点比使用inx文件的用户体验要差一点, 但是Qt界面很全面, 这种方法让开发者几乎可以使用python全部的功能, 让Inkscape变得更好用.