Browse Source

Show the console during exports

The only major issue with the console based progress solution is that
the user would have to remember to press "Toggle System Console" before
the export. This button corresponds to the operator
`bpy.ops.wm.console_toggle`. Unfortunately, Blender does not expose a
way for us to query the console state. So, we have to get nasty and use
ctypes to ask the OS if the console window is active. The user may
already have it open and hidden behind Blender's UI, after all.

This changeset causes the console to open during the export (unless
disabled in the export operator). If the console was closed before the
export, it closes again once the export is finished, unless there is an
error. If the console was open, it remains open.

Unfortunately, this only works on Windows. But, according to the source
code of `bpy.ops.wm.console_toggle`, Blender's `ghost_toggleConsole`
only functions on the Win32 subsystem... SDL, X11, and Cocoa are all
no-ops. Future work would include a patch submitted to Blender adding an
enum property to the console operator to avoid this code duplication.
pull/58/head
Adam Johnson 8 years ago
parent
commit
80ce28ddc8
Signed by: Hoikas
GPG Key ID: 0B6515D6FF6F271E
  1. 3
      korman/exporter/convert.py
  2. 3
      korman/exporter/logger.py
  3. 1
      korman/korlib/__init__.py
  4. 83
      korman/korlib/console.py
  5. 8
      korman/operators/op_export.py

3
korman/exporter/convert.py

@ -14,6 +14,7 @@
# along with Korman. If not, see <http://www.gnu.org/licenses/>. # along with Korman. If not, see <http://www.gnu.org/licenses/>.
import bpy import bpy
from ..korlib import ConsoleToggler
from pathlib import Path from pathlib import Path
from PyHSPlasma import * from PyHSPlasma import *
import time import time
@ -43,7 +44,7 @@ class Exporter:
def run(self): def run(self):
log = logger.ExportVerboseLogger if self._op.verbose else logger.ExportProgressLogger log = logger.ExportVerboseLogger if self._op.verbose else logger.ExportProgressLogger
with log(self._op.filepath) as self.report: with ConsoleToggler(self._op.show_console), log(self._op.filepath) as self.report:
# Step 0: Init export resmgr and stuff # Step 0: Init export resmgr and stuff
self.mgr = manager.ExportManager(self) self.mgr = manager.ExportManager(self)
self.mesh = mesh.MeshConverter(self) self.mesh = mesh.MeshConverter(self)

3
korman/exporter/logger.py

@ -13,6 +13,7 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with Korman. If not, see <http://www.gnu.org/licenses/>. # along with Korman. If not, see <http://www.gnu.org/licenses/>.
from ..korlib import ConsoleToggler
from pathlib import Path from pathlib import Path
import threading import threading
import time import time
@ -40,6 +41,8 @@ class _ExportLogger:
return self return self
def __exit__(self, type, value, traceback): def __exit__(self, type, value, traceback):
if value is not None:
ConsoleToggler().keep_console = True
self._file.close() self._file.close()
return False return False

1
korman/korlib/__init__.py

@ -71,4 +71,5 @@ except ImportError:
size = stream.readInt() size = stream.readInt()
return (header, size) return (header, size)
else: else:
from .console import ConsoleToggler
from .texture import TEX_DETAIL_ALPHA, TEX_DETAIL_ADD, TEX_DETAIL_MULTIPLY from .texture import TEX_DETAIL_ALPHA, TEX_DETAIL_ADD, TEX_DETAIL_MULTIPLY

83
korman/korlib/console.py

@ -0,0 +1,83 @@
# This file is part of Korman.
#
# Korman is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Korman is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Korman. If not, see <http://www.gnu.org/licenses/>.
import bpy
import ctypes
import math
import sys
class ConsoleToggler:
_instance = None
def __init__(self, want_console=None):
if want_console is not None:
self._console_wanted = want_console
def __new__(cls, want_console=None):
if cls._instance is None:
assert want_console is not None
cls._instance = object.__new__(cls)
cls._instance._console_was_visible = cls.is_console_visible()
cls._instance._console_wanted = want_console
cls._instance._context_active = False
cls._instance.keep_console = False
return cls._instance
def __enter__(self):
if self._context_active:
raise RuntimeError("ConsoleToggler context manager is not reentrant")
self._console_visible = self.is_console_visible()
self._context_active = True
self.activate_console()
return self
def __exit__(self, type, value, traceback):
if not self._console_was_visible and self._console_wanted:
if self.keep_console:
# Blender thinks the console is currently not visible. However, it actually is.
# So, we will fire off the toggle operator to keep Blender's internal state valid
bpy.ops.wm.console_toggle()
else:
self.hide_console()
self._context_active = False
self.keep_console = False
return False
def activate_console(self):
if sys.platform == "win32":
hwnd = ctypes.windll.kernel32.GetConsoleWindow()
if self._console_wanted:
ctypes.windll.user32.ShowWindow(hwnd, 1)
if self._console_was_visible or self._console_wanted:
ctypes.windll.user32.BringWindowToTop(hwnd)
@staticmethod
def hide_console():
if sys.platform == "win32":
hwnd = ctypes.windll.kernel32.GetConsoleWindow()
ctypes.windll.user32.ShowWindow(hwnd, 0)
@staticmethod
def is_console_visible():
if sys.platform == "win32":
hwnd = ctypes.windll.kernel32.GetConsoleWindow()
return bool(ctypes.windll.user32.IsWindowVisible(hwnd))
@staticmethod
def is_platform_supported():
# If you read Blender's source code, GHOST_toggleConsole (the "Toggle System Console" menu
# item) is only implemented on Windows. The majority of our audience is on Windows as well,
# so I honestly don't see this as an issue...
return sys.platform == "win32"

8
korman/operators/op_export.py

@ -22,6 +22,7 @@ import pstats
from .. import exporter from .. import exporter
from ..properties.prop_world import PlasmaAge from ..properties.prop_world import PlasmaAge
from ..properties.modifiers.logic import game_versions from ..properties.modifiers.logic import game_versions
from ..korlib import ConsoleToggler
class ExportOperator(bpy.types.Operator): class ExportOperator(bpy.types.Operator):
"""Exports ages for Cyan Worlds' Plasma Engine""" """Exports ages for Cyan Worlds' Plasma Engine"""
@ -49,6 +50,10 @@ class ExportOperator(bpy.types.Operator):
"verbose": (BoolProperty, {"name": "Display Verbose Log", "verbose": (BoolProperty, {"name": "Display Verbose Log",
"description": "Shows the verbose export log in the console", "description": "Shows the verbose export log in the console",
"default": False}), "default": False}),
"show_console": (BoolProperty, {"name": "Display Log Console",
"description": "Forces the Blender System Console open during the export",
"default": True}),
} }
# This wigs out and very bad things happen if it's not directly on the operator... # This wigs out and very bad things happen if it's not directly on the operator...
@ -62,6 +67,9 @@ class ExportOperator(bpy.types.Operator):
# The crazy mess we're doing with props on the fly means we have to explicitly draw them :( # The crazy mess we're doing with props on the fly means we have to explicitly draw them :(
layout.prop(age, "version") layout.prop(age, "version")
layout.prop(age, "bake_lighting") layout.prop(age, "bake_lighting")
row = layout.row()
row.enabled = ConsoleToggler.is_platform_supported()
row.prop(age, "show_console")
layout.prop(age, "verbose") layout.prop(age, "verbose")
layout.prop(age, "profile_export") layout.prop(age, "profile_export")

Loading…
Cancel
Save