├── .gitignore ├── MyPlugin ├── Content │ └── Python │ │ ├── init_unreal.py │ │ └── my_module.py ├── MyPlugin.uplugin └── Resources │ └── icon128.png ├── README.md └── requirements.txt /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | -------------------------------------------------------------------------------- /MyPlugin/Content/Python/init_unreal.py: -------------------------------------------------------------------------------- 1 | # code in init_unreal.py wil run on startup if the plugin is enabled 2 | import unreal 3 | 4 | 5 | section_name = 'Plugins' 6 | se_command = 'import my_module;w = my_module.show()' # todo replace with your code 7 | label = 'My Plugin' 8 | tooltip = "my tooltip" 9 | 10 | 11 | def create_script_editor_button(): 12 | """Add a tool button to the tool bar""" 13 | menus = unreal.ToolMenus.get() 14 | level_menu_bar = menus.find_menu('LevelEditor.LevelEditorToolBar.PlayToolBar') 15 | level_menu_bar.add_section(section_name=section_name, label=section_name) 16 | 17 | entry = unreal.ToolMenuEntry(type=unreal.MultiBlockType.TOOL_BAR_BUTTON) 18 | entry.set_label(label) 19 | entry.set_tool_tip(tooltip) 20 | entry.set_icon('EditorStyle', 'DebugConsole.Icon') 21 | entry.set_string_command( 22 | type=unreal.ToolMenuStringCommandType.PYTHON, 23 | custom_type=unreal.Name(''), # not sure what this is 24 | string=se_command 25 | ) 26 | level_menu_bar.add_menu_entry(section_name, entry) 27 | menus.refresh_all_widgets() 28 | 29 | 30 | def add_cmd_to_menu(label=None, command=None, tooltip=None, icon=None): 31 | unreal_menus = unreal.ToolMenus.get() 32 | parent_menu = unreal_menus.find_menu("LevelEditor.MainMenu.Tools") 33 | 34 | # name kwargs needs to be unique! if not set, it's autogenerated 35 | entry = unreal.ToolMenuEntry( 36 | type=unreal.MultiBlockType.MENU_ENTRY, 37 | insert_position=unreal.ToolMenuInsert("", unreal.ToolMenuInsertType.FIRST), 38 | ) 39 | if label: 40 | entry.set_label(label) 41 | if command: 42 | entry.set_string_command( 43 | type=unreal.ToolMenuStringCommandType.PYTHON, 44 | string=command, 45 | custom_type=unreal.Name("_placeholder_"), 46 | ) 47 | if tooltip: 48 | entry.set_tool_tip(tooltip) 49 | if icon: 50 | entry.set_icon(icon) 51 | 52 | parent_menu.add_menu_entry("Configuration", entry) 53 | 54 | 55 | # create_script_editor_button() ## uncomment to add a button to the toolbar to launch your tool 56 | add_cmd_to_menu(label=label, command=se_command, tooltip=tooltip) 57 | print("My plugin is enabled") 58 | -------------------------------------------------------------------------------- /MyPlugin/Content/Python/my_module.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from PySide6.QtWidgets import QApplication, QWidget, QLabel, QPushButton, QVBoxLayout 3 | 4 | 5 | class HelloWorldWidget(QWidget): 6 | def __init__(self): 7 | super().__init__() 8 | self.label = QLabel('Hello') 9 | self.button = QPushButton('OK') 10 | self.button.clicked.connect(self.print_hello) 11 | layout = QVBoxLayout() 12 | layout.addWidget(self.label) 13 | layout.addWidget(self.button) 14 | self.setLayout(layout) 15 | 16 | def print_hello(self): 17 | print('Hello') 18 | 19 | 20 | def show(): 21 | widget = HelloWorldWidget() 22 | widget.show() 23 | return widget 24 | -------------------------------------------------------------------------------- /MyPlugin/MyPlugin.uplugin: -------------------------------------------------------------------------------- 1 | { 2 | "FileVersion": 3, 3 | "Version": 1, 4 | "VersionName": "0.1", 5 | "FriendlyName": "My Plugin", 6 | "Description": "A template for your Unreal Python plugin", 7 | "Category": "tool", 8 | "CreatedBy": "Hannes", 9 | "CreatedByURL": "", 10 | "DocsURL": "", 11 | "MarketplaceURL": "", 12 | "SupportURL": "", 13 | "CanContainContent": true, 14 | "IsBetaVersion": true, 15 | "IsExperimentalVersion": true, 16 | "Installed": false, 17 | "Plugins": [ 18 | { 19 | "Name": "PythonScriptPlugin", 20 | "Enabled": true 21 | } 22 | ] 23 | } -------------------------------------------------------------------------------- /MyPlugin/Resources/icon128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hannesdelbeke/unreal-python-plugin-template/612145179217f3b7c7a5c848be98a29c8b4038f3/MyPlugin/Resources/icon128.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Unreal Python plugin template 2 | A minimalism template for pure Python plugins in Unreal.
3 | Example repos using this template: 4 | - [unrealScriptEditor-plugin](https://github.com/hannesdelbeke/unrealScriptEditor-plugin) 5 | - [texture-browser-unreal-plugin](https://github.com/hannesdelbeke/texture-browser-unreal-plugin) 6 | - [unreal-qt-plugin](https://github.com/hannesdelbeke/unreal-qt-plugin) 7 | - [plugget-unreal-plugin](https://github.com/plugget/plugget-unreal-plugin) 8 | 9 | 10 | ### Content 11 | ``` 12 | 📂 MyPlugin 13 | ├── 📂 Content 14 | │ └── 📂 Python 15 | │ └── 📄 init_unreal.py 16 | ├── 📂 Resources 17 | │ └── 🖼️ icon128.png 18 | └── 📄 MyPlugin.uplugin 19 | 📄 .gitignore 20 | 📄 README.md 21 | 📄 requirements.txt 22 | ``` 23 | 24 | ### Info 25 | - `MyPlugin` rename the folder to your plugin name. Unreal's naming convention uses PascalCase. 26 | - `MyPlugin.uplugin` Rename this file to your plugin name, and open it with a text editor & edit the content. 27 | - `.gitignore` is setup to prevent unneeded python files from being commit to your git-repo. 28 | - `requirements.txt` Add your pip/pypi dependencies to this file, delete it if not used. 29 | - `README.md`: include an image & description, so people see what's your plugin about. 30 | - `Python` This folder is added to the PYTHONPATH, put the modules you want to import in here 31 | 32 | # Installation 33 | 34 | ### Manual install 35 | 1. save the plugin in your Unreal plugins folder 36 | 2. pip install the Python dependencies from `requirements.txt` to Unreal's site packages folder 37 | (This can be too technical for some people) 38 | 3. enable the plugin in Unreal, and restart Unreal 39 | 40 | ### (OPTIONAL) Add Plugget install support 41 | To support 1-click install & automatically install all dependencies in the `requirements.txt` file, you can add [plugget](https://github.com/hannesdelbeke/plugget) support. 42 | It's a bit more work for you, the developer. But it removes the technical steps for the end user. 43 | 44 | 1. Upload your plugin to a repo. ([example repo](https://github.com/hannesdelbeke/unreal-python-plugin-template)) 45 | 2. Create a plugget manifest ([sample manifest](https://github.com/plugget/plugget-pkgs/blob/main/unreal/hello-world-template/latest.json)) that points to your repo, 46 | 3. Make a PR in [plugget-pkgs](https://github.com/hannesdelbeke/plugget-pkgs) to merge it in the public Plugget database. 47 | 4. Add the plugget-install instructions to your README: 48 | ``` 49 | Installation with plugget automatically installs all dependencies. 50 | 1. Install the [plugget Qt Unreal plugin](https://github.com/plugget/plugget-unreal-plugin) 51 | 2. Install the package: 52 | - go to the menu `Edit > Plugget Packages` to open the package manager 53 | - search & install `unreal-script-editor` <=========== EDIT THIS TEXT ⚠️ 54 | ``` 55 | 56 |
57 | example of a more advanced plugin made from this template, and installable through plugget 58 | 59 | - [repo](https://github.com/hannesdelbeke/unreal-plugin-python-script-editor) 60 | - [plugget manifest](https://github.com/plugget/plugget-pkgs/blob/main/unreal/python-script-editor/latest.json) 61 | - plugget package name `unreal-script-editor` 62 |
63 | 64 | 65 | ### Community 66 | - unreal forum [thread](https://forums.unrealengine.com/t/made-a-python-plugin-template/1089878) 67 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | unreal-qt 2 | --------------------------------------------------------------------------------