Faced with such a problem. I linked my blender and PyQt6. We even managed to find an example of how to start the process without the main window of the blender hanging. However, when I started to complicate the design, namely! If I try to switch windows via QtWidgets.QStackedWidget(), then I can’t add the same switch. With grief in half, I found which part of the code is responsible for the part I need and entered it there
self.widget = QtWidgets.QStackedWidget()
self.widget.addWidget(self._widget(*self._args, **self._kwargs))
self.widget.show()
however, this did not affect access to the variable as I expected
I will attach the code that needs to be corrected
import os
import bpy
import sys
sys.path.append('/home/user/venv/lib/python3.12/site-packages')
import logging
from PyQt6 import QtWidgets, QtCore
from PyQt6.QtWidgets import QDialog, QStackedWidget, QApplication
logger = logging.getLogger('qtutils')
class QtWindowEventLoop(bpy.types.Operator):
"""Allows PyQt or PySide to run inside Blender"""
bl_idname = 'screen.qt_event_loop'
bl_label = 'Qt Event Loop'
def __init__(self, widget, *args, **kwargs):
self._widget = widget
self._args = args
self._kwargs = kwargs
def modal(self, context, event):
# bpy.context.window_manager
wm = context.window_manager
if not self.widget.isVisible():
# if widget is closed
logger.debug('finish modal operator')
wm.event_timer_remove(self._timer)
return {'FINISHED'}
else:
logger.debug('process the events for Qt window')
self.event_loop.processEvents()
self.app.sendPostedEvents(None, 0)
return {'PASS_THROUGH'}
def execute(self, context):
logger.debug('execute operator')
self.app = QtWidgets.QApplication.instance()
# instance() gives the possibility to have multiple windows
# and close it one by one
if not self.app:
# create the first instance
self.app = QtWidgets.QApplication(sys.argv)
if 'stylesheet' in self._kwargs:
stylesheet = self._kwargs['stylesheet']
self.set_stylesheet(self.app, stylesheet)
self.event_loop = QtCore.QEventLoop()
self.widget = QtWidgets.QStackedWidget()
self.widget.addWidget(self._widget(*self._args, **self._kwargs))
self.widget.show()
#self.widget = self._widget(*self._args, **self._kwargs)
logger.debug(self.app)
logger.debug(self.widget)
# run modal
wm = context.window_manager
self._timer = wm.event_timer_add(1 / 120, window=context.window)
context.window_manager.modal_handler_add(self)
return {'RUNNING_MODAL'}
def set_stylesheet(self, app, filepath):
file_qss = QtCore.QFile(filepath)
if file_qss.exists():
file_qss.open(QtCore.QFile.ReadOnly)
stylesheet = QtCore.QTextStream(file_qss).readAll()
app.setStyleSheet(stylesheet)
file_qss.close()
class Login_Form(QDialog):
def __init__(self, parent=None):
super(Login_Form, self).__init__(parent)
self.setFixedSize(261, 250)
self.setWindowTitle('login form')
self.log = QtWidgets.QLineEdit()
self.label_log = QtWidgets.QLabel("Login")
self.passw = QtWidgets.QLineEdit()
self.label_passw = QtWidgets.QLabel("Password")
self.login_botton = QtWidgets.QPushButton(text="login")
self.login_botton.clicked.connect(self.login)
layout = QtWidgets.QGridLayout()
layout.addWidget(self.log, 0, 1, 1, 1)
layout.addWidget(self.login_botton, 5, 0, 1, 2)
layout.addWidget(self.passw, 1, 1, 1, 1)
layout.addWidget(self.label_passw, 1, 0, 1, 1)
layout.addWidget(self.label_log, 0, 0, 1, 1)
self.setLayout(layout)
#self.show()
def login(self):
second_window = Second_window()
widget.addWidget(second_window)
widget.setCurrentIndex(widget.currentIndex()+1)
class Second_window(QDialog):
def __init__(self, parent=None):
super(Second_window, self).__init__(parent)
# def show_second_window(self):
#=====================================================================================
self.setFixedSize(500, 500)
self.setWindowTitle('main')
layout = QtWidgets.QGridLayout()
self.setLayout(layout)
#self.show()
class RedmineWindowOperator(QtWindowEventLoop):
bl_idname = 'screen_login.redmine'
bl_label = 'Cam Controller'
def __init__(self):
super().__init__(Login_Form)
if __name__ == '__main__':
bpy.utils.register_class(RedmineWindowOperator)
bpy.ops.screen_login.redmine() # load the tool upon initializing the addon?
and the code that works well if you run it from the console (but there’s nothing there to work with blender, it’s just a test field)
import os
import sys
from PyQt6 import QtWidgets, QtCore
from PyQt6.QtWidgets import QDialog, QStackedWidget, QApplication
class Login_Form(QDialog):
def __init__(self, parent=None):
super(Login_Form, self).__init__(parent)
self.setFixedSize(261, 250)
self.setWindowTitle('login form')
self.log = QtWidgets.QLineEdit()
self.label_log = QtWidgets.QLabel("Login")
self.passw = QtWidgets.QLineEdit()
self.label_passw = QtWidgets.QLabel("Password")
self.login_botton = QtWidgets.QPushButton(text="login")
self.login_botton.clicked.connect(self.login)
layout = QtWidgets.QGridLayout()
layout.addWidget(self.log, 0, 1, 1, 1)
layout.addWidget(self.login_botton, 5, 0, 1, 2)
layout.addWidget(self.passw, 1, 1, 1, 1)
layout.addWidget(self.label_passw, 1, 0, 1, 1)
layout.addWidget(self.label_log, 0, 0, 1, 1)
self.setLayout(layout)
def login(self):
second_window = Second_window()
widget.addWidget(second_window)
widget.setCurrentIndex(widget.currentIndex()+1)
class Second_window(QDialog):
def __init__(self, parent=None):
super(Second_window, self).__init__(parent)
# def show_second_window(self):
#=====================================================================================
self.setFixedSize(500, 500)
self.setWindowTitle('main')
layout = QtWidgets.QGridLayout()
self.setLayout(layout)
app = QApplication(sys.argv)
mainwindow = Login_Form()
widget = QtWidgets.QStackedWidget()
widget.addWidget(mainwindow)
widget.show()
app.exec()
I tried different approaches that either led to a crash, or simply did not work as the last option…
Welet is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.