Add Qt for Python templates and better support

QtCreator:
* Add new icons
* Add support for `.pyproject` files,
* Set `.pyproject` as default, but keep compatibility with `.pyqtc`
    * `.pyproject` is a JSON file, while `.pyqtc` is a plain-text.

Python class:
* Add option to ask if use PySide2 or PyQt5
* Remove the old import try-except structure
* Remove iconText and add icon option
* Remove shebang
* Add utf-8 support

Python file:
* Remove code
* Remove iconText and add icon option
* Remove shebang
* Add utf-8 support

Qt for Python - Empty
* Add file with basic statements to execute a QApplication

Qt for Python - Window
* Add file with basic statements to execute a QApplication,
  which contains a QMainWindow

Task-number: QTCREATORBUG-21824
Change-Id: I4adb3ab6b179f084c7b674a6d4f643445fe24929
Reviewed-by: hjk <hjk@qt.io>
Reviewed-by: Alessandro Portale <alessandro.portale@qt.io>
Reviewed-by: Eike Ziller <eike.ziller@qt.io>
This commit is contained in:
Cristian Maureira-Fredes
2019-01-16 13:04:51 +01:00
parent 2edd9c5012
commit f7e1354ae5
19 changed files with 371 additions and 44 deletions

View File

@@ -1,30 +1,26 @@
# -*- coding: utf-8 -*-
@if '%{Imports}'
try:
@if '%{ImportQtCore}'
from PySide import QtCore
@endif
@if '%{ImportQtWidgets}'
from PySide import QtWidgets
@endif
@if '%{ImportQtQuick}'
from PySide import QtQuick
@endif
except:
@if '%{ImportQtCore}'
from PyQt5.QtCore import pyqtSlot as Slot
from PyQt5 import QtCore
@endif
@if '%{ImportQtWidgets}'
from PyQt5 import QtWidgets
@endif
@if '%{ImportQtQuick}'
from PyQt5 import QtQuick
# This Python file uses the following encoding: utf-8
@if '%{Module}' === 'PySide2'
@if '%{ImportQtCore}'
from PySide2 import QtCore
@endif
@if '%{ImportQtWidgets}'
from PySide2 import QtWidgets
@endif
@if '%{ImportQtQuick}'
from PySide2 import QtQuick
@endif
@else
@if '%{ImportQtCore}'
from PyQt5 import QtCore
@endif
@if '%{ImportQtWidgets}'
from PyQt5 import QtWidgets
@endif
@if '%{ImportQtQuick}'
from PyQt5 import QtQuick
@endif
@endif
@endif
@if '%{Base}'
class %{Class}(%{Base}):
@else

View File

@@ -6,7 +6,7 @@
"trDescription": "Creates new Python class file.",
"trDisplayName": "Python Class",
"trDisplayCategory": "Python",
"iconText": "py",
"icon": "../../files/python/icon.png",
"enabled": "%{JS: [ %{Plugins} ].indexOf('PythonEditor') >= 0}",
"options":
@@ -30,6 +30,15 @@
"type": "LineEdit",
"data": { "validator": "^(?:[^\\d\\W]\\w*|)$" }
},
{
"name": "Module",
"trDisplayName": "Python module:",
"type": "ComboBox",
"data":
{
"items": ["PySide2", "PyQt5"]
}
},
{
"name": "BaseCB",
"trDisplayName": "Base class:",

View File

@@ -1,2 +1,4 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# This Python file uses the following encoding: utf-8
# if__name__ == "__main__":
# pass

Binary file not shown.

After

Width:  |  Height:  |  Size: 606 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 989 B

View File

@@ -6,7 +6,7 @@
"trDescription": "Creates an empty Python script file using UTF-8 charset.",
"trDisplayName": "Python File",
"trDisplayCategory": "Python",
"iconText": "py",
"icon": "icon.png",
"enabled": "%{JS: [ %{Plugins} ].indexOf('PythonEditor') >= 0}",
"pages" :

Binary file not shown.

After

Width:  |  Height:  |  Size: 728 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -0,0 +1,51 @@
{
"version": 1,
"supportedProjectTypes": [ "PythonProject" ],
"id": "U.QtForPythonApplicationEmpty",
"category": "F.Application",
"trDescription": "Creates a Qt for Python application that only the main code for a QApplication",
"trDisplayName": "Qt for Python - Empty",
"trDisplayCategory": "Application",
"icon": "icon.png",
"enabled": "%{JS: [ %{Plugins} ].indexOf('PythonEditor') >= 0}",
"featuresRequired": [ "QtSupport.Wizards.FeatureQt.5.6" ],
"options":
[
{ "key": "MainPyFileName", "value": "main.py" },
{ "key": "PyProjectFile", "value": "main.pyproject" }
],
"pages":
[
{
"trDisplayName": "Project Location",
"trShortTitle": "Location",
"typeId": "Project"
},
{
"trDisplayName": "Project Management",
"trShortTitle": "Summary",
"typeId": "Summary"
}
],
"generators":
[
{
"typeId": "File",
"data":
[
{
"source": "../main.pyproject",
"target": "%{PyProjectFile}",
"openAsProject": true
},
{
"source": "../main_empty.py",
"target": "%{MainPyFileName}",
"openInEditor": true
}
]
}
]
}

View File

@@ -0,0 +1,3 @@
{
"files": ["main.py"]
}

View File

@@ -0,0 +1,9 @@
# This Python file uses the following encoding: utf-8
import sys
from PySide2.QtWidgets import QApplication
if __name__ == "__main__":
app = QApplication([])
# ...
sys.exit(app.exec_())

View File

@@ -0,0 +1,15 @@
# This Python file uses the following encoding: utf-8
import sys
from PySide2.QtWidgets import QApplication, QMainWindow
class MainWindow(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
if __name__ == "__main__":
app = QApplication([])
window = MainWindow()
window.show()
sys.exit(app.exec_())

Binary file not shown.

After

Width:  |  Height:  |  Size: 855 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -0,0 +1,51 @@
{
"version": 1,
"supportedProjectTypes": [ "Qt4ProjectManager.Qt4Project" ],
"id": "U.QtForPythonApplicationWindow",
"category": "F.Application",
"trDescription": "Creates a Qt for Python application that contains an empty window.",
"trDisplayName": "Qt for Python - Window",
"trDisplayCategory": "Application",
"icon": "icon.png",
"enabled": "%{JS: [ %{Plugins} ].indexOf('PythonEditor') >= 0}",
"featuresRequired": [ "QtSupport.Wizards.FeatureQt.5.6" ],
"options":
[
{ "key": "MainPyFileName", "value": "main.py" },
{ "key": "PyProjectFile", "value": "main.pyproject" }
],
"pages":
[
{
"trDisplayName": "Project Location",
"trShortTitle": "Location",
"typeId": "Project"
},
{
"trDisplayName": "Project Management",
"trShortTitle": "Summary",
"typeId": "Summary"
}
],
"generators":
[
{
"typeId": "File",
"data":
[
{
"source": "../main.pyproject",
"target": "%{PyProjectFile}",
"openAsProject": true
},
{
"source": "../main_mainwindow.py",
"target": "%{MainPyFileName}",
"openInEditor": true
}
]
}
]
}

View File

@@ -28,6 +28,7 @@
\" <mime-type type=\'text/x-python-project\'>\",
\" <sub-class-of type=\'text/x-python\'/>\",
\" <comment>Qt Creator Python project file</comment>\",
\" <glob pattern=\'*.pyproject\'/>\",
\" <glob pattern=\'*.pyqtc\'/>\",
\" </mime-type>\",
\"</mime-info>\"

View File

@@ -59,6 +59,10 @@
#include <QRegularExpression>
#include <QRegularExpressionMatch>
#include <QTextCursor>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonValue>
#include <QJsonArray>
using namespace Core;
using namespace ProjectExplorer;
@@ -340,6 +344,35 @@ static QStringList readLines(const Utils::FileName &projectFile)
return lines;
}
static QStringList readLinesJson(const Utils::FileName &projectFile)
{
const QString projectFileName = projectFile.fileName();
QStringList lines = { projectFileName };
QFile file(projectFile.toString());
if (!file.open(QFile::ReadOnly))
return lines;
const QByteArray content = file.readAll();
// This assumes te project file is formed with only one field called
// 'files' that has a list associated of the files to include in the project.
if (!content.isEmpty()) {
const QJsonDocument doc = QJsonDocument::fromJson(content);
const QJsonObject obj = doc.object();
if (obj.contains("files")) {
QJsonValue files = obj.value("files");
QJsonArray files_array = files.toArray();
QSet<QString> visited;
for (const auto &file : files_array)
visited.insert(file.toString());
lines.append(visited.toList());
}
}
return lines;
}
bool PythonProject::saveRawFileList(const QStringList &rawFileList)
{
bool result = saveRawList(rawFileList, projectFilePath().toString());
@@ -418,7 +451,15 @@ bool PythonProject::renameFile(const QString &filePath, const QString &newFilePa
void PythonProject::parseProject()
{
m_rawListEntries.clear();
m_rawFileList = readLines(projectFilePath());
const Utils::FileName filePath = projectFilePath();
// The PySide project file is JSON based
if (filePath.endsWith(".pyproject"))
m_rawFileList = readLinesJson(filePath);
// To keep compatibility with PyQt we keep the compatibility with plain
// text files as project files.
else if (filePath.endsWith(".pyqtc"))
m_rawFileList = readLines(filePath);
m_files = processEntries(m_rawFileList, &m_rawListEntries);
}
@@ -449,7 +490,7 @@ void PythonProject::refresh(Target *target)
auto newRoot = std::make_unique<PythonProjectNode>(this);
for (const QString &f : m_files) {
const QString displayName = baseDir.relativeFilePath(f);
FileType fileType = f.endsWith(".pyqtc") ? FileType::Project : FileType::Source;
FileType fileType = f.endsWith(".pyproject") || f.endsWith(".pyqtc") ? FileType::Project : FileType::Source;
newRoot->addNestedNode(std::make_unique<PythonFileNode>(FileName::fromString(f),
displayName, fileType));
if (fileType == FileType::Source) {

View File

@@ -2645,18 +2645,21 @@
id="path5662-0"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccccc" />
<g
id="tempateIconGuiWizarfTitleBar">
<path
sodipodi:nodetypes="sssccss"
inkscape:connector-curvature="0"
id="rect7867-7-1-4-6-13-8-3"
style="fill:#3a4055"
d="m 587.94531,306.94336 h 33.10938 c 0.81824,0 1.3335,0.62105 1.44531,1.43164 V 311 h -36 v -2.625 c 0,-0.81826 0.62707,-1.43164 1.44531,-1.43164 z"
style="fill:#3a4055" />
<path
id="rect5668-6"
style="fill:#dfe0e3"
d="m 619.5,308 h 2 v 2 h -2 z m -32,0 h 10 v 2 h -10 z"
id="rect7867-7-1-4-6-13-8-3"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccccccc" />
sodipodi:nodetypes="sssccss" />
<path
sodipodi:nodetypes="cccccccccc"
inkscape:connector-curvature="0"
d="m 619.5,308 h 2 v 2 h -2 z m -32,0 h 10 v 2 h -10 z"
style="fill:#dfe0e3"
id="rect5668-6" />
</g>
</g>
<use
x="0"
@@ -2666,6 +2669,152 @@
transform="translate(60,0)"
width="100%"
height="100%" />
<g
id="pythonSnake"
transform="matrix(1.03639,0,0,1.03639,42.698825,-127.31617)">
<path
style="stroke-width:0.62522024"
d="m 493.40157,381.58661 c -1.64186,0.28728 -1.95327,1.12936 -1.98311,2.23791 v 1.47075 h 3.8497 l 0.0197,0.96489 h -3.88913 -1.45286 c -1.12761,0 -2.07681,0.6104 -2.5352,1.9502 -0.45839,1.3398 -0.43103,2.68855 0,4.05417 0.43103,1.36562 0.93069,1.7217 2.05828,1.71476 l 0.98462,-5e-5 v -1.46166 c 0,-1.43884 1.23626,-2.39787 2.71574,-2.39787 h 4.24283 c 1.07877,0 1.73984,-0.59015 1.73527,-1.66316 l -0.0197,-4.63208 c -0.004,-1.04142 -0.79695,-2.06408 -1.84987,-2.2379 -0.66651,-0.10988 -1.35625,-0.15393 -2.01952,-0.15085 -0.66326,0.003 -1.2994,0.0532 -1.85674,0.15089 z m -0.53661,1.53569 c 0.40077,0 0.72642,0.32748 0.72642,0.73256 0,0.40364 -0.32565,0.72932 -0.72642,0.72932 -0.40221,0 -0.72644,-0.3258 -0.72644,-0.72944 0,-0.40508 0.32423,-0.73244 0.72644,-0.73244 z"
id="path1631"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ssccccszzscssssscccssssss" />
</g>
<g
id="pythonLogo">
<use
style="fill:#3a4055;fill-opacity:1"
height="100%"
width="100%"
transform="translate(16)"
id="use1658"
xlink:href="#pythonSnake"
y="0"
x="0" />
<use
style="fill:#848895;fill-opacity:1"
height="100%"
width="100%"
transform="rotate(180,564.00512,277.49996)"
id="use1660"
xlink:href="#pythonSnake"
y="0"
x="0" />
</g>
<g
id="share/qtcreator/templates/wizards/projects/qtforpythonapplication/mainwindow/icon">
<use
style="display:inline"
x="0"
y="0"
xlink:href="#transparentBackgroundRect_60_60"
id="use6218"
width="100%"
height="100%"
transform="translate(540,60)" />
<use
style="display:inline"
x="0"
y="0"
xlink:href="#wizardicons_laptop"
id="use6220"
width="100%"
height="100%"
transform="translate(55,-78)" />
<use
transform="translate(-11.997289,-36)"
height="100%"
width="100%"
id="use6176"
xlink:href="#pythonLogo"
y="0"
x="0"
style="display:inline" />
<path
style="display:inline;fill:#3a4055;stroke-width:1"
d="m 571,238 -6e-4,10.32943 2.47755,-1.72702 1.42512,3.25135 2.17205,-0.94811 -1.42511,-3.25136 2.95258,-0.64325 z"
id="path6222"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccccc" />
<use
transform="translate(-38.5,-80)"
style="display:inline"
x="0"
y="0"
xlink:href="#tempateIconGuiWizarfTitleBar"
id="use6247"
width="100%"
height="100%" />
</g>
<g
transform="translate(115,-78)"
id="share/qtcreator/templates/wizards/projects/qtforpythonapplication/empty/icon">
<use
transform="translate(485,138)"
height="100%"
width="100%"
id="use6264"
xlink:href="#transparentBackgroundRect_60_60"
y="0"
x="0"
style="display:inline" />
<g
transform="translate(0,-1)"
id="g6272">
<path
sodipodi:nodetypes="sssccss"
inkscape:connector-curvature="0"
id="path6266"
d="m 494.5,304.5 h 33 c 1.58695,0 3,1.41305 3,3 v 25 h -39 v -25 c 0,-1.58696 1.41305,-3 3,-3 z"
style="fill:#fbfbfb;stroke:#6b7080;stroke-width:3" />
<path
sodipodi:nodetypes="ccccc"
inkscape:connector-curvature="0"
id="path6268"
d="m 535,333 h -48 c 0,2 1,3 3,4 h 42 c 2,-1 3,-2 3,-4 z"
style="fill:#848895" />
<path
sodipodi:nodetypes="csscc"
inkscape:connector-curvature="0"
id="path6270"
d="m 517,333 c 0,0.77906 -0.69638,1 -1.61124,1 H 506.5 c -0.91487,0 -1.69151,-0.22094 -1.69151,-1 z"
style="fill:#53586b" />
</g>
<path
sodipodi:nodetypes="cccccccccccc"
inkscape:connector-curvature="0"
d="m 502,325 h 6 v 1 h -6 z m -5.75012,-7 h 1.5 l 4,4 -4,4 h -1.5 l 4,-4 z"
style="fill:#3a4055;stroke-width:1"
id="path6274" />
<use
style="display:inline"
x="0"
y="0"
xlink:href="#pythonLogo"
id="use6278"
width="100%"
height="100%"
transform="translate(-54.999998,40)" />
</g>
<g
id="share/qtcreator/templates/wizards/files/python/icon">
<use
height="100%"
width="100%"
transform="translate(480,60)"
id="use8679"
xlink:href="#src/libs/utils/images/wizardicon-file"
y="0"
x="0" />
<use
height="100%"
width="100%"
id="use8681"
xlink:href="#use6278"
y="0"
x="0"
style="display:inline"
transform="translate(169,-74)" />
</g>
<g
id="src/plugins/coreplugin/images/settingscategory_core"
transform="translate(-364,-84)">

Before

Width:  |  Height:  |  Size: 363 KiB

After

Width:  |  Height:  |  Size: 368 KiB

View File

@@ -1,4 +1,4 @@
- qtcreator -load PythonEditor ./python.pyqtc
- qtcreator -load PythonEditor ./python.pyproject (or ./python.pyqtc)
- Switch active runconfiguration to main.py
- <F10>