├── .gitignore ├── imgs ├── preview.png └── pyqt-integration-icon.png ├── .vscodeignore ├── .vscode ├── extensions.json ├── settings.json ├── tasks.json └── launch.json ├── tslint.json ├── CHANGELOG.md ├── tsconfig.json ├── LICENSE ├── src ├── extension.ts └── controller.ts ├── README.md └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | out 2 | node_modules 3 | .vscode-test/ 4 | *.vsix 5 | -------------------------------------------------------------------------------- /imgs/preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mine2chow/PYQT-Integration/HEAD/imgs/preview.png -------------------------------------------------------------------------------- /imgs/pyqt-integration-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mine2chow/PYQT-Integration/HEAD/imgs/pyqt-integration-icon.png -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode/** 2 | .vscode-test/** 3 | out/test/** 4 | out/**/*.map 5 | src/** 6 | .gitignore 7 | tsconfig.json 8 | vsc-extension-quickstart.md 9 | tslint.json -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 3 | // for the documentation about the extensions.json format 4 | "recommendations": [ 5 | "eg2.tslint" 6 | ] 7 | } -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "no-string-throw": true, 4 | "no-unused-expression": true, 5 | "no-duplicate-variable": true, 6 | "curly": true, 7 | "class-name": true, 8 | "semicolon": [ 9 | true, 10 | "always" 11 | ], 12 | "triple-equals": true 13 | }, 14 | "defaultSeverity": "warning" 15 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "files.exclude": { 4 | "out": false // set this to true to hide the "out" folder with the compiled JS files 5 | }, 6 | "search.exclude": { 7 | "out": true // set this to false to include "out" folder in search results 8 | } 9 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | // See https://go.microsoft.com/fwlink/?LinkId=733558 2 | // for the documentation about the tasks.json format 3 | { 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "type": "npm", 8 | "script": "watch", 9 | "problemMatcher": "$tsc-watch", 10 | "isBackground": true, 11 | "presentation": { 12 | "reveal": "never" 13 | }, 14 | "group": { 15 | "kind": "build", 16 | "isDefault": true 17 | } 18 | } 19 | ] 20 | } -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | - 0.2.1 Support ${workspace} for qt designer path 4 | - 0.2.0 5 | - Add menu "`PYQT: Generate Translation File (.ts)`" 6 | - Add menu "`PYQT: Open With Qt Linguist`" 7 | - 0.1.5 Add Property "`pyqt-integration.pyuic.compile.addOptions`" 8 | - 0.1.4 Bug fix for default save path 9 | - 0.1.3 10 | - Default location for saving a new form will be inherited 11 | - Use "${qrc_name}_rc.py" as default name for compiling a qrc file 12 | - 0.1.2 Fix a bug when using space in path 13 | - 0.1.1 14 | - .qrc file supported 15 | - '.qss', '.ui', '.qrc' file language id mapping 16 | - 0.1.0 First version -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es6", 5 | "outDir": "out", 6 | "lib": [ 7 | "es6" 8 | ], 9 | "sourceMap": true, 10 | "rootDir": "src", 11 | /* Strict Type-Checking Option */ 12 | "strict": true, /* enable all strict type-checking options */ 13 | /* Additional Checks */ 14 | "noUnusedLocals": true /* Report errors on unused locals. */ 15 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 16 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 17 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 18 | }, 19 | "exclude": [ 20 | "node_modules", 21 | ".vscode-test" 22 | ] 23 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Feng Zhou 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | // A launch configuration that compiles the extension and then opens it inside a new window 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | { 6 | "version": "0.2.0", 7 | "configurations": [ 8 | { 9 | "name": "Extension", 10 | "type": "extensionHost", 11 | "request": "launch", 12 | "runtimeExecutable": "${execPath}", 13 | "args": [ 14 | "--extensionDevelopmentPath=${workspaceFolder}" 15 | ], 16 | "outFiles": [ 17 | "${workspaceFolder}/out/**/*.js" 18 | ], 19 | "preLaunchTask": "npm: watch" 20 | }, 21 | { 22 | "name": "Extension Tests", 23 | "type": "extensionHost", 24 | "request": "launch", 25 | "runtimeExecutable": "${execPath}", 26 | "args": [ 27 | "--extensionDevelopmentPath=${workspaceFolder}", 28 | "--extensionTestsPath=${workspaceFolder}/out/test" 29 | ], 30 | "outFiles": [ 31 | "${workspaceFolder}/out/test/**/*.js" 32 | ], 33 | "preLaunchTask": "npm: watch" 34 | } 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /src/extension.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | // The module 'vscode' contains the VS Code extensibility API 3 | // Import the module and reference it with the alias vscode in your code below 4 | import * as vscode from 'vscode'; 5 | import { PYQTController } from './controller'; 6 | 7 | // this method is called when your extension is activated 8 | // your extension is activated the very first time the command is executed 9 | export function activate(context: vscode.ExtensionContext) { 10 | 11 | const controller = new PYQTController(context); 12 | 13 | // The command has been defined in the package.json file 14 | // Now provide the implementation of the command with registerCommand 15 | // The commandId parameter must match the command field in package.json 16 | 17 | context.subscriptions.push( 18 | vscode.commands.registerCommand('pyqt-integration.createNewForm', 19 | (fileUri: vscode.Uri) => controller.createNewForm(fileUri)) 20 | ); 21 | context.subscriptions.push( 22 | vscode.commands.registerCommand('pyqt-integration.editInDesigner', 23 | (fileUri: vscode.Uri) => controller.editInDesigner(fileUri)) 24 | ); 25 | context.subscriptions.push( 26 | vscode.commands.registerCommand('pyqt-integration.Preview', 27 | (fileUri: vscode.Uri) => controller.preview(fileUri)) 28 | ); 29 | context.subscriptions.push( 30 | vscode.commands.registerCommand('pyqt-integration.compileForm', 31 | (fileUri: vscode.Uri) => controller.compileForm(fileUri)) 32 | ); 33 | context.subscriptions.push( 34 | vscode.commands.registerCommand('pyqt-integration.compileQRC', 35 | (fileUri: vscode.Uri) => controller.compileQRC(fileUri)) 36 | ); 37 | context.subscriptions.push( 38 | vscode.commands.registerCommand('pyqt-integration.pylupdate', 39 | (fileUri: vscode.Uri) => controller.pylupdate(fileUri)) 40 | ); 41 | context.subscriptions.push( 42 | vscode.commands.registerCommand('pyqt-integration.linguist', 43 | (fileUri: vscode.Uri) => controller.linguist(fileUri)) 44 | ); 45 | } 46 | 47 | // this method is called when your extension is deactivated 48 | export function deactivate() { 49 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PYQT Integration 2 | 3 | [![Marketplace Version](https://vsmarketplacebadge.apphb.com/version-short/zhoufeng.pyqt-integration.svg)](https://marketplace.visualstudio.com/items?itemName=zhoufeng.pyqt-integration) [![Extension Installs](https://vsmarketplacebadge.apphb.com/installs/zhoufeng.pyqt-integration.svg)](https://marketplace.visualstudio.com/items?itemName=zhoufeng.pyqt-integration) [![Extension Rating](https://vsmarketplacebadge.apphb.com/rating/zhoufeng.pyqt-integration.svg)](https://marketplace.visualstudio.com/items?itemName=zhoufeng.pyqt-integration) 4 | 5 | An extension help you coding PYQT form in vsocde. Support "`.ui`", "`.qrc`", "`.pro`", "`.ts`" files. 6 | 7 | ![preview](./imgs/preview.png) 8 | 9 | ## Explorer context menu 10 | 11 | |No.|Name|Description| 12 | |:---:|---|---| 13 | |1|PYQT: New Form|Open designer| 14 | |2|PYQT: Edit In Designer|Open designer with current ui form| 15 | |3|PYQT: Preview|Preview current ui form| 16 | |4|PYQT: Compile Form|Compile ui form to path defined in "`pyqt-integration.pyuic.compile.filepath`"| 17 | |5|PYQT: Compile Resource|Compile qrc file to path defined in "`pyqt-integration.pyrcc.compile.filepath`"| 18 | |6|PYQT: Generate Translation File (.ts)|Compile UI file (.py) to translation file with path defined in "`pyqt-integration.pylupdate.compile.filepath`"
Compile .pro file| 19 | |7|PYQT: Open With Qt Linguist|Open with Qt Linguist for translation file (.ts)| 20 | 21 | ## Properties 22 | 23 | |No.|Name|Description| 24 | |:---:|---|---| 25 | |1|`pyqt-integration.qtdesigner.path`|Path of executable file of qt designer, the extension will ask you to set at the first time it runs, e.g. c:\\\\Users\\\\username\\\\AppData\\\\Local\\\\Programs\\\\Python\\\\Python35\\\\Lib\\\\site-packages\\\\pyqt5-tools\\\\designer.exe| 26 | |2|`pyqt-integration.pyuic.cmd`|"pyuic" command, default "`pyuic5`"| 27 | |3|`pyqt-integration.pyuic.compile.filepath`|Compile file path, relative path as default, switch to absolute path by involving ${workspace}, e.g. \${workspace}\\\\UI\\\\Ui_\${ui_name}.py| 28 | |4|`pyqt-integration.pyuic.compile.addOptions`|Additional options for pyuic compiling, it can be a combination of '-x', '-d', '-i', etc.| 29 | |5|`pyqt-integration.pyrcc.cmd`|"pyrcc" command, default "`pyrcc5`"| 30 | |6|`pyqt-integration.pyrcc.compile.filepath`|Compile file path, relative path as default, switch to absolute path by involving ${workspace}, e.g. \${workspace}\\\\QRC\\\\\${qrc_name}_rc.py| 31 | |7|`pyqt-integration.pyrcc.compile.addOptions`|Additional options for pyrcc compiling, it can be a combination of '-root', '-threshold', '-compress', '-no-compress', etc.| 32 | |8|`pyqt-integration.pylupdate.cmd`|"pylupdate" command, default "`pylupdate5`"| 33 | |9|`pyqt-integration.pylupdate.compile.filepath`|Only works when compiling an UI file (.py), Stores the target '.ts' file's path, relative path as default, switch to absolute path by involving ${workspace}, e.g. ${workspace}\\\\TS\\\\\${ts_name}.ts| 34 | |10|`pyqt-integration.pylupdate.compile.addOptions`|Additional options for pylupdate, it can be a combination of '-verbose', '-noobsolete', '-tr-function', '-translate-function', etc.| 35 | |11|`pyqt-integration.linguist.cmd`|"linguist" command, default "`linguist`"| 36 | 37 | ```text 38 | Compilation will overwite the target file without confirmation! 39 | ``` -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pyqt-integration", 3 | "displayName": "PYQT Integration", 4 | "description": "PYQT integration", 5 | "license": "MIT", 6 | "version": "0.2.0", 7 | "publisher": "zhoufeng", 8 | "engines": { 9 | "vscode": "^1.22.0" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/mine2chow/PYQT-Integration.git" 14 | }, 15 | "icon": "imgs/pyqt-integration-icon.png", 16 | "homepage": "https://github.com/mine2chow/PYQT-Integration/blob/master/README.md", 17 | "keywords": [ 18 | "pyqt", 19 | "qt" 20 | ], 21 | "categories": [ 22 | "Other" 23 | ], 24 | "activationEvents": [ 25 | "*" 26 | ], 27 | "main": "./out/extension", 28 | "contributes": { 29 | "commands": [ 30 | { 31 | "command": "pyqt-integration.createNewForm", 32 | "title": "PYQT: New Form" 33 | }, 34 | { 35 | "command": "pyqt-integration.editInDesigner", 36 | "title": "PYQT: Edit In Designer" 37 | }, 38 | { 39 | "command": "pyqt-integration.Preview", 40 | "title": "PYQT: Preview" 41 | }, 42 | { 43 | "command": "pyqt-integration.compileForm", 44 | "title": "PYQT: Compile Form" 45 | }, 46 | { 47 | "command": "pyqt-integration.compileQRC", 48 | "title": "PYQT: Compile Resource" 49 | }, 50 | { 51 | "command": "pyqt-integration.pylupdate", 52 | "title": "PYQT: Generate Translation File (.ts)" 53 | }, 54 | { 55 | "command": "pyqt-integration.linguist", 56 | "title": "PYQT: Open With Qt Linguist" 57 | } 58 | ], 59 | "menus": { 60 | "explorer/context": [ 61 | { 62 | "command": "pyqt-integration.createNewForm", 63 | "when": "filesExplorerFocus == true", 64 | "group": "pyqt-integration@1" 65 | }, 66 | { 67 | "command": "pyqt-integration.Preview", 68 | "when": "resourceScheme == file && resourceExtname == .ui", 69 | "group": "pyqt-integration@2" 70 | }, 71 | { 72 | "command": "pyqt-integration.editInDesigner", 73 | "when": "resourceScheme == file && resourceExtname == .ui", 74 | "group": "pyqt-integration@3" 75 | }, 76 | { 77 | "command": "pyqt-integration.compileForm", 78 | "when": "resourceScheme == file && resourceExtname == .ui", 79 | "group": "pyqt-integration@4" 80 | }, 81 | { 82 | "command": "pyqt-integration.compileQRC", 83 | "when": "resourceScheme == file && resourceExtname == .qrc", 84 | "group": "pyqt-integration@5" 85 | }, 86 | { 87 | "command": "pyqt-integration.pylupdate", 88 | "when": "resourceScheme == file && resourceExtname == .py", 89 | "group": "pyqt-integration@6" 90 | }, 91 | { 92 | "command": "pyqt-integration.pylupdate", 93 | "when": "resourceScheme == file && resourceExtname == .pro", 94 | "group": "pyqt-integration@6" 95 | }, 96 | { 97 | "command": "pyqt-integration.linguist", 98 | "when": "resourceScheme == file && resourceExtname == .ts", 99 | "group": "pyqt-integration@7" 100 | } 101 | ], 102 | "commandPalette": [ 103 | { 104 | "command": "pyqt-integration.editInDesigner", 105 | "when": "false" 106 | }, 107 | { 108 | "command": "pyqt-integration.Preview", 109 | "when": "false" 110 | }, 111 | { 112 | "command": "pyqt-integration.compileForm", 113 | "when": "false" 114 | }, 115 | { 116 | "command": "pyqt-integration.compileQRC", 117 | "when": "false" 118 | }, 119 | { 120 | "command": "pyqt-integration.pylupdate", 121 | "when": "false" 122 | }, 123 | { 124 | "command": "pyqt-integration.linguist", 125 | "when": "false" 126 | } 127 | ] 128 | }, 129 | "configuration": { 130 | "title": "PYQT integration configuration", 131 | "properties": { 132 | "pyqt-integration.qtdesigner.path": { 133 | "type": "string", 134 | "default": "", 135 | "description": "Path of QT designer", 136 | "scope": "window" 137 | }, 138 | "pyqt-integration.pyuic.cmd": { 139 | "type": "string", 140 | "default": "pyuic5", 141 | "description": "'pyuic' command file, you can also specify a path", 142 | "scope": "window" 143 | }, 144 | "pyqt-integration.pyrcc.cmd": { 145 | "type": "string", 146 | "default": "pyrcc5", 147 | "description": "'pyrcc' command file, you can also specify a path", 148 | "scope": "window" 149 | }, 150 | "pyqt-integration.pylupdate.cmd": { 151 | "type": "string", 152 | "default": "pylupdate5", 153 | "description": "'pylupdate' command file, you can also specify a path", 154 | "scope": "window" 155 | }, 156 | "pyqt-integration.linguist.cmd": { 157 | "type": "string", 158 | "default": "linguist", 159 | "description": "'linguist' command file, you can also specify a path", 160 | "scope": "window" 161 | }, 162 | "pyqt-integration.pyuic.compile.filepath": { 163 | "type": "string", 164 | "default": "Ui_${ui_name}.py", 165 | "description": "Target '.py' file path for compiling, relative path as default, switch to absolute path by involving ${workspace}, e.g. ${workspace}\\\\UI\\\\Ui_${ui_name}.py", 166 | "scope": "window" 167 | }, 168 | "pyqt-integration.pyuic.compile.addOptions": { 169 | "type": "string", 170 | "default": "", 171 | "description": "Additional options for pyuic compiling, it can be a combination of '-x', '-d', '-i', etc.", 172 | "scope": "window" 173 | }, 174 | "pyqt-integration.pyrcc.compile.filepath": { 175 | "type": "string", 176 | "default": "${qrc_name}_rc.py", 177 | "description": "Target '.py' file path for compiling, relative path as default, switch to absolute path by involving ${workspace}, e.g. ${workspace}\\\\QRC\\\\${qrc_name}_rc.py", 178 | "scope": "window" 179 | }, 180 | "pyqt-integration.pyrcc.compile.addOptions": { 181 | "type": "string", 182 | "default": "", 183 | "description": "Additional options for pyrcc compiling, it can be a combination of '-root', '-threshold', '-compress', '-no-compress', etc.", 184 | "scope": "window" 185 | }, 186 | "pyqt-integration.pylupdate.compile.filepath": { 187 | "type": "string", 188 | "default": "${ts_name}.ts", 189 | "description": "Only works when compiling an UI file (.py), Stands for the target '.ts' file's path, relative path as default, switch to absolute path by involving ${workspace}, e.g. ${workspace}\\\\TS\\\\${ts_name}.ts", 190 | "scope": "window" 191 | }, 192 | "pyqt-integration.pylupdate.compile.addOptions": { 193 | "type": "string", 194 | "default": "", 195 | "description": "Additional options for pylupdate, it can be a combination of '-verbose', '-noobsolete', '-tr-function', '-translate-function', etc.", 196 | "scope": "window" 197 | } 198 | } 199 | }, 200 | "languages": [ 201 | { 202 | "id": "css", 203 | "extensions": [ 204 | ".qss" 205 | ] 206 | }, 207 | { 208 | "id": "xml", 209 | "extensions": [ 210 | ".ui", 211 | ".qrc" 212 | ] 213 | } 214 | ] 215 | }, 216 | "scripts": { 217 | "vscode:prepublish": "", 218 | "compile": "tsc -p ./", 219 | "watch": "tsc -watch -p ./", 220 | "postinstall": "node ./node_modules/vscode/bin/install", 221 | "test": "npm run compile && node ./node_modules/vscode/bin/test" 222 | }, 223 | "devDependencies": { 224 | "typescript": "^2.6.1", 225 | "vscode": "^1.1.6", 226 | "tslint": "^5.8.0", 227 | "@types/node": "^7.0.43", 228 | "@types/mocha": "^2.2.42" 229 | } 230 | } -------------------------------------------------------------------------------- /src/controller.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import * as vscode from 'vscode'; 4 | 5 | export class PYQTController{ 6 | private context:vscode.ExtensionContext; 7 | private cp = require('child_process'); 8 | private fs = require('fs'); 9 | private path = require('path'); 10 | private _outputChannel: vscode.OutputChannel; 11 | 12 | constructor(context:vscode.ExtensionContext){ 13 | this.context = context; 14 | this._outputChannel = vscode.window.createOutputChannel("PYQT"); 15 | } 16 | 17 | private initFolder(filePath:string, {isRelativeToScript = false} = {}) { 18 | const sep = this.path.sep; 19 | const folderPath = filePath.replace(/(.*[\\\/]).*$/, "$1").replace(/[\\\/]/g, sep); 20 | 21 | const initDir = this.path.isAbsolute(folderPath) ? sep : ''; 22 | const baseDir = isRelativeToScript ? __dirname : '.'; 23 | folderPath.split(sep).reduce((parentDir:string, childDir:string) => { 24 | const curDir = this.path.resolve(baseDir, parentDir, childDir); 25 | try { 26 | if(!this.fs.existsSync(curDir)){ 27 | this.fs.mkdirSync(curDir); 28 | this._outputChannel.appendLine(`[Info] Directory "${curDir}" created.`); 29 | } 30 | } catch (err) { 31 | if (err.code !== 'EEXIST') { 32 | vscode.window.showErrorMessage(err.toString()); 33 | throw err; 34 | } 35 | 36 | //console.log(`Directory ${curDir} already exists!`); 37 | } 38 | 39 | return curDir; 40 | }, initDir); 41 | } 42 | 43 | private exec(cmd: string, {successMessage="", stdoutPath="", cwd=""} = {}){ 44 | //this._outputChannel.show(true); 45 | this._outputChannel.appendLine(`[Running] ${cmd}`); 46 | this.cp.exec(cmd, {cwd: cwd}, (err:any, stdout:any, stderr:any) => { 47 | if(stdout && stdoutPath){ 48 | this.initFolder(stdoutPath); 49 | this.fs.writeFileSync(stdoutPath, stdout, 'utf8'); 50 | } 51 | if(!err){ 52 | if(stdout){ 53 | this._outputChannel.appendLine(`${stdout.toString()}`); 54 | } 55 | if(stderr){ 56 | this._outputChannel.appendLine(`${stderr.toString()}`); 57 | } 58 | } 59 | if (err) { 60 | this._outputChannel.appendLine(`[Error] ${stderr.toString()}`); 61 | vscode.window.showErrorMessage(err.toString()); 62 | throw err; 63 | } else if(successMessage !== ""){ 64 | this._outputChannel.appendLine(`[Done] ${successMessage}`); 65 | vscode.window.showInformationMessage(successMessage); 66 | } 67 | }); 68 | } 69 | 70 | private async getOrConfigDesignerPath() { 71 | let dPath = vscode.workspace.getConfiguration().get('pyqt-integration.qtdesigner.path', ""); 72 | if(dPath === ""){ 73 | vscode.window.showInformationMessage("Select your executable file of QT Designer"); 74 | await vscode.window.showOpenDialog({ 75 | canSelectMany: false 76 | }).then((uris: vscode.Uri[] | undefined) => { 77 | if(uris && uris.length !== 0){ 78 | vscode.workspace.getConfiguration().update('pyqt-integration.qtdesigner.path', uris[0].fsPath, vscode.ConfigurationTarget.Global); 79 | dPath = uris[0].fsPath; 80 | } 81 | }); 82 | } 83 | 84 | if(dPath.indexOf("${workspace}") !== -1){ 85 | // Absolute path 86 | const workspaceFoldersList = vscode.workspace.workspaceFolders; 87 | let workspacePath = ""; 88 | if(workspaceFoldersList && workspaceFoldersList.length !== 0){ 89 | workspacePath = workspaceFoldersList[0].uri.fsPath; 90 | } 91 | 92 | dPath = dPath.replace("${workspace}", workspacePath); 93 | 94 | } 95 | 96 | return dPath; 97 | } 98 | 99 | /** 100 | * createNewForm 101 | */ 102 | public async createNewForm(fileUri: vscode.Uri) { 103 | const dPath = await this.getOrConfigDesignerPath(); 104 | if(dPath !== ""){ 105 | if(!fileUri){ 106 | var workspaceFolders = vscode.workspace.workspaceFolders; 107 | if(workspaceFolders){ 108 | fileUri = workspaceFolders[0].uri; 109 | } 110 | } 111 | this.fs.lstat(fileUri.fsPath, (err:any, stats:any) => { 112 | if(err){ 113 | return vscode.window.showErrorMessage(err); 114 | } 115 | let dirName = fileUri.fsPath; 116 | if(stats.isFile()){ 117 | dirName = this.path.dirname(fileUri.fsPath); 118 | } 119 | this.exec(`"${dPath}"`, {cwd:dirName}); 120 | }); 121 | } 122 | } 123 | 124 | /** 125 | * editInDesigner 126 | */ 127 | public async editInDesigner(fileUri: vscode.Uri) { 128 | const dPath = await this.getOrConfigDesignerPath(); 129 | if(dPath !== ""){ 130 | this.fs.lstat(fileUri.fsPath, (err:any, stats:any) => { 131 | if(err){ 132 | return vscode.window.showErrorMessage(err); 133 | } 134 | let dirName = fileUri.fsPath; 135 | if(stats.isFile()){ 136 | dirName = this.path.dirname(fileUri.fsPath); 137 | } 138 | this.exec(`"${dPath}" "${fileUri.fsPath}"`, {cwd:dirName}); 139 | }); 140 | } 141 | 142 | } 143 | 144 | /** 145 | * preview 146 | */ 147 | public async preview(fileUri: vscode.Uri) { 148 | const pyuic = vscode.workspace.getConfiguration().get('pyqt-integration.pyuic.cmd', ""); 149 | this.fs.lstat(fileUri.fsPath, (err:any, stats:any) => { 150 | if(err){ 151 | return vscode.window.showErrorMessage(err); 152 | } 153 | let dirName = fileUri.fsPath; 154 | if(stats.isFile()){ 155 | dirName = this.path.dirname(fileUri.fsPath); 156 | } 157 | this.exec(`"${pyuic}" -p "${fileUri.fsPath}"`, {cwd:dirName}); 158 | }); 159 | } 160 | 161 | 162 | private resolvePath(fileUri: vscode.Uri, pyPath:string) : string { 163 | // path resolved 164 | let pyPathR = pyPath.replace("${ui_name}", "${name}") 165 | .replace("${qrc_name}", "${name}") 166 | .replace("${ts_name}", "${name}"); 167 | 168 | 169 | if(pyPathR.indexOf("${workspace}") !== -1){ 170 | // Absolute path 171 | const workspaceFoldersList = vscode.workspace.workspaceFolders; 172 | let workspacePath = ""; 173 | if(workspaceFoldersList && workspaceFoldersList.length !== 0){ 174 | workspacePath = workspaceFoldersList[0].uri.fsPath; 175 | } 176 | 177 | let fileNameNoSuffix = fileUri.fsPath.replace(/(.*[\\\/])(.*)\..*$/, "$2"); 178 | 179 | pyPathR = pyPathR.replace("${workspace}", workspacePath).replace("${name}", fileNameNoSuffix); 180 | 181 | } else { 182 | if(!this.path.isAbsolute(pyPathR)){ 183 | let pattern = "$1" + pyPathR.replace("${name}", "$2"); 184 | pyPathR = fileUri.fsPath.replace(/(.*[\\\/])(.*)\..*$/, pattern); 185 | } else { 186 | let fileNameNoSuffix = fileUri.fsPath.replace(/(.*[\\\/])(.*)\..*$/, "$2"); 187 | pyPathR = pyPathR.replace("${name}", fileNameNoSuffix); 188 | } 189 | } 190 | 191 | return pyPathR; 192 | } 193 | 194 | /** 195 | * compileForm 196 | */ 197 | public async compileForm(fileUri: vscode.Uri) { 198 | const pyuic = vscode.workspace.getConfiguration().get('pyqt-integration.pyuic.cmd', ""); 199 | const pyPath = vscode.workspace.getConfiguration().get('pyqt-integration.pyuic.compile.filepath', ""); 200 | const addOpts = vscode.workspace.getConfiguration().get('pyqt-integration.pyuic.compile.addOptions', ""); 201 | 202 | // path resolved 203 | let pyPathR = this.resolvePath(fileUri, pyPath); 204 | 205 | this.initFolder(pyPathR); 206 | this.fs.lstat(fileUri.fsPath, (err:any, stats:any) => { 207 | if(err){ 208 | return vscode.window.showErrorMessage(err); 209 | } 210 | let dirName = fileUri.fsPath; 211 | if(stats.isFile()){ 212 | dirName = this.path.dirname(fileUri.fsPath); 213 | } 214 | this.exec(`"${pyuic}" "${fileUri.fsPath}" ${addOpts} -o "${pyPathR}"`, { 215 | successMessage:`Compiled to "${pyPathR}" successfully`, 216 | cwd:dirName 217 | }); 218 | }); 219 | } 220 | 221 | /** 222 | * compileQRC 223 | */ 224 | public async compileQRC(fileUri: vscode.Uri) { 225 | const pyrcc = vscode.workspace.getConfiguration().get('pyqt-integration.pyrcc.cmd', ""); 226 | const pyPath = vscode.workspace.getConfiguration().get('pyqt-integration.pyrcc.compile.filepath', ""); 227 | const addOpts = vscode.workspace.getConfiguration().get('pyqt-integration.pyrcc.compile.addOptions', ""); 228 | 229 | // path resolved 230 | let pyPathR = this.resolvePath(fileUri, pyPath); 231 | 232 | this.initFolder(pyPathR); 233 | this.fs.lstat(fileUri.fsPath, (err:any, stats:any) => { 234 | if(err){ 235 | return vscode.window.showErrorMessage(err); 236 | } 237 | let dirName = fileUri.fsPath; 238 | if(stats.isFile()){ 239 | dirName = this.path.dirname(fileUri.fsPath); 240 | } 241 | this.exec(`"${pyrcc}" "${fileUri.fsPath}" ${addOpts} -o "${pyPathR}"`, { 242 | successMessage:`Compiled to "${pyPathR}" successfully`, 243 | cwd:dirName 244 | }); 245 | }); 246 | } 247 | 248 | /** 249 | * pylupdate 250 | */ 251 | public async pylupdate(fileUri: vscode.Uri) { 252 | const pylupdate = vscode.workspace.getConfiguration().get('pyqt-integration.pylupdate.cmd', ""); 253 | const tsPath = vscode.workspace.getConfiguration().get('pyqt-integration.pylupdate.compile.filepath', ""); 254 | const addOpts = vscode.workspace.getConfiguration().get('pyqt-integration.pylupdate.compile.addOptions', ""); 255 | 256 | // path resolved 257 | let tsPathR = this.resolvePath(fileUri, tsPath); 258 | 259 | this.initFolder(tsPathR); 260 | this.fs.lstat(fileUri.fsPath, (err:any, stats:any) => { 261 | if(err){ 262 | return vscode.window.showErrorMessage(err); 263 | } 264 | let dirName = fileUri.fsPath; 265 | if(stats.isFile()){ 266 | dirName = this.path.dirname(fileUri.fsPath); 267 | } 268 | 269 | if(fileUri.fsPath.endsWith(".pro") && stats.isFile()){ 270 | this.exec(`"${pylupdate}" ${addOpts} "${fileUri.fsPath}"`, { 271 | successMessage:`Compiled "${fileUri.fsPath}" successfully`, 272 | cwd:dirName 273 | }); 274 | } else if(fileUri.fsPath.endsWith(".py") && stats.isFile()){ 275 | this.exec(`"${pylupdate}" ${addOpts} "${fileUri.fsPath}" -ts "${tsPathR}"`, { 276 | successMessage:`Compiled to "${tsPathR}" successfully`, 277 | cwd:dirName 278 | }); 279 | } 280 | }); 281 | } 282 | 283 | /** 284 | * linguist 285 | */ 286 | public async linguist(fileUri: vscode.Uri) { 287 | const linguist = vscode.workspace.getConfiguration().get('pyqt-integration.linguist.cmd', "linguist"); 288 | 289 | this.fs.lstat(fileUri.fsPath, (err:any, stats:any) => { 290 | if(err){ 291 | return vscode.window.showErrorMessage(err); 292 | } 293 | let dirName = fileUri.fsPath; 294 | if(stats.isFile()){ 295 | dirName = this.path.dirname(fileUri.fsPath); 296 | } 297 | 298 | this.exec(`"${linguist}" "${fileUri.fsPath}"`, {cwd:dirName}); 299 | }); 300 | } 301 | } --------------------------------------------------------------------------------