Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Close dialogue to prevent save dialogue hidden behind qt widgets #56

Merged
merged 11 commits into from
Dec 28, 2022
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ python setup.py install
### Environment variables
- BQT_DISABLE_STARTUP if set, completely disable bqt
- BQT_DISABLE_WRAP if set to 1, disable wrapping blender in a QWindow
- BQT_DISABLE_CLOSE_DIALOGUE if set to 1, use the standard blender close dialogue

### Sample code
[bqt_demo](bqt_demo) shows you how to use bqt with several qt demos you can run in Blender
Expand Down
3 changes: 3 additions & 0 deletions bqt/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@


# CORE FUNCTIONS #

def instantiate_application() -> BlenderApplication:
"""
Create an instance of Blender Application
Expand Down Expand Up @@ -124,3 +125,5 @@ def on_exit():
if app:
app.store_window_geometry()
app.quit()


13 changes: 10 additions & 3 deletions bqt/blender_applications/blender_application.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
from PySide2.QtWidgets import QApplication, QWidget
from PySide2.QtGui import QCloseEvent, QIcon, QImage, QPixmap, QWindow
from PySide2.QtCore import QEvent, QObject, QRect, QSettings
from bqt.quit_dialogue import BlenderClosingDialog
import bpy


class BlenderApplication(QApplication):
Expand Down Expand Up @@ -66,7 +68,7 @@ def _get_application_icon() -> QIcon:
Returns QIcon: Application Icon
"""

icon_filepath = Path(__file__).parents[1] / "blender_icon_16.png"
icon_filepath = Path(__file__).parents[1] / "images" / "blender_icon_16.png"
icon = QIcon()

if icon_filepath.exists():
Expand Down Expand Up @@ -123,9 +125,14 @@ def notify(self, receiver: QObject, event: QEvent) -> bool:
# ignore the event, and ask user if they want to close blender if unsaved changes.
# if this is successful, blender will trigger bqt.on_exit()
event.ignore()
import bpy

bpy.ops.wm.quit_blender({"window": bpy.context.window_manager.windows[0]}, "INVOKE_DEFAULT")
if os.getenv("BQT_DISABLE_CLOSE_DIALOGUE") == "1":
# this triggers the default blender close event, showing the save dialog if needed
bpy.ops.wm.quit_blender({"window": bpy.context.window_manager.windows[0]}, "INVOKE_DEFAULT")
else:
closing_dialog = BlenderClosingDialog(self.blender_widget)
closing_dialog.execute()

return False

return super().notify(receiver, event)
Expand Down
7 changes: 7 additions & 0 deletions bqt/blender_stylesheet.qss
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,13 @@ QProgressBar::chunk {
border: 1px solid #373737;
}

QMessageBox {
background-color: rgb(24, 24, 24);
}

QMessageBox QPushButton {
min-width: 105px;
}

QPushButton {
background-color: #585858;
Expand Down
File renamed without changes
8 changes: 8 additions & 0 deletions bqt/images/question.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
64 changes: 64 additions & 0 deletions bqt/quit_dialogue.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import bpy
from PySide2.QtWidgets import QMessageBox
from PySide2.QtCore import Qt
import os
import bqt.ui

def shutdown_blender(*args):
bpy.ops.wm.quit_blender()


class WINDOW_OT_SaveFileFromQt(bpy.types.Operator):
bl_idname = "wm.save_from_qt"
bl_label = "Save_from_Qt"

def execute(self, context):
# TODO not sure what we are doing here, Friederman?
if context.blend_data.is_saved:
bpy.ops.wm.save_mainfile({"window": bpy.context.window_manager.windows[0]}, 'EXEC_AREA', check_existing=False)
else:
bpy.ops.wm.save_mainfile({"window": bpy.context.window_manager.windows[0]}, 'INVOKE_AREA', check_existing=False)
# https://docs.blender.org/api/current/bpy.ops.html
# EXEC_AREA - execute the operator in a certain context
return {'FINISHED'}

# todo
# when clicking the icon, the dialogue resets to center screen position
# support dragging the dialogue around
# add qshortcuts https://stackoverflow.com/questions/19845774/is-it-possible-to-use-an-underlined-letter-as-keyboard-shortcut-in-qt


class BlenderClosingDialog(QMessageBox):
def __init__(self, parent):
super().__init__(parent) #, Qt.WindowCloseButtonHint | Qt.WindowSystemMenuHint | Qt.WindowTitleHint | Qt.WindowStaysOnTopHint)

# hide title bar
self.setWindowFlag(Qt.FramelessWindowHint)

filepath = bpy.data.filepath
if not filepath:
filepath = 'untitled.blend'
filename = os.path.split(filepath)[1]

question_icon = bqt.ui.get_question_pixmap()

self.setText("Save changes before closing?\n\n" + filename)
self.setStandardButtons(QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel)
self.setDefaultButton(QMessageBox.Save)
self.setIconPixmap(question_icon)

def execute(self):
if not bpy.data.is_dirty:
shutdown_blender()
return

choice = super().exec_()
if choice == QMessageBox.Save:
bpy.utils.register_class(WINDOW_OT_SaveFileFromQt)
bpy.app.handlers.save_post.append(shutdown_blender)
bpy.ops.wm.save_from_qt()
elif choice == QMessageBox.Discard:
shutdown_blender()
else: # user clicked cancel
pass
return choice
14 changes: 14 additions & 0 deletions bqt/ui/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from pathlib import Path
from PySide2.QtGui import QCloseEvent, QIcon, QImage, QPixmap, QWindow
import PySide2.QtCore as QtCore


def get_question_pixmap():
icon_filepath = Path(__file__).parents[1] / "images" / "question.svg"
pixmap = QPixmap()
if icon_filepath.exists():
image = QImage(str(icon_filepath))
if not image.isNull():
pixmap = pixmap.fromImage(image)
pixmap = pixmap.scaledToWidth(64, QtCore.Qt.SmoothTransformation)
return pixmap