上一節裏說過把解釋器調整到默認的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變得更好用.