CreativePython 1.1.3__py3-none-any.whl → 1.1.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- CreativePython/GuiHandler.py +41 -29
- CreativePython/GuiRenderer.py +731 -468
- {creativepython-1.1.3.dist-info → creativepython-1.1.4.dist-info}/METADATA +1 -1
- {creativepython-1.1.3.dist-info → creativepython-1.1.4.dist-info}/RECORD +9 -9
- gui.py +20 -111
- {creativepython-1.1.3.dist-info → creativepython-1.1.4.dist-info}/WHEEL +0 -0
- {creativepython-1.1.3.dist-info → creativepython-1.1.4.dist-info}/licenses/LICENSE +0 -0
- {creativepython-1.1.3.dist-info → creativepython-1.1.4.dist-info}/licenses/LICENSE-PSF +0 -0
- {creativepython-1.1.3.dist-info → creativepython-1.1.4.dist-info}/top_level.txt +0 -0
CreativePython/GuiHandler.py
CHANGED
|
@@ -66,37 +66,49 @@ def _createEvent(eventType, target, args=None):
|
|
|
66
66
|
# Child process entry point
|
|
67
67
|
#######################################################################################
|
|
68
68
|
|
|
69
|
-
def _launchRenderer(
|
|
69
|
+
def _launchRenderer(childCommandConnection, childPriorityConnection, parentPriorityConnection):
|
|
70
70
|
"""
|
|
71
71
|
Entry point for the GuiRenderer child process.
|
|
72
72
|
Defined here so GuiHandler can reference it without importing from GuiRenderer
|
|
73
73
|
globally (which would also import Qt/PySide6 in the parent process).
|
|
74
74
|
|
|
75
|
-
|
|
75
|
+
childPriorityConnection is the child end of the duplex admin pipe; the child watches it
|
|
76
76
|
with QSocketNotifier. Admin commands (setRate, getRate) arrive here, bypassing
|
|
77
|
-
the command buffer. When the parent closes its end (
|
|
77
|
+
the command buffer. When the parent closes its end (parentPriorityConnection), the child
|
|
78
78
|
receives EOF and initiates shutdown.
|
|
79
79
|
|
|
80
|
-
|
|
80
|
+
parentPriorityConnection is the parent's end; the child closes it immediately so that
|
|
81
81
|
only the parent holds it — ensuring the child sees EOF when the parent closes.
|
|
82
82
|
|
|
83
83
|
At runtime this function only executes in the child process.
|
|
84
84
|
"""
|
|
85
|
-
import sys, os
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
#
|
|
90
|
-
#
|
|
91
|
-
#
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
os.
|
|
95
|
-
sys.stderr =
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
85
|
+
import sys, os, traceback
|
|
86
|
+
|
|
87
|
+
parentPriorityConnection.close() # child must not hold the parent end open
|
|
88
|
+
|
|
89
|
+
# Redirect stderr (fd 2) to a log file so both Python tracebacks and C-level
|
|
90
|
+
# output from Qt/Cocoa/XPC are captured for diagnostics.
|
|
91
|
+
# The log is removed on clean exit if nothing was written to it.
|
|
92
|
+
_logPath = os.path.join(os.getcwd(), "PENCIL_debug.log")
|
|
93
|
+
_logFile = open(_logPath, "w")
|
|
94
|
+
os.dup2(_logFile.fileno(), 2)
|
|
95
|
+
sys.stderr = _logFile
|
|
96
|
+
|
|
97
|
+
try:
|
|
98
|
+
from CreativePython.GuiRenderer import GuiRenderer
|
|
99
|
+
renderer = GuiRenderer(childCommandConnection, childPriorityConnection)
|
|
100
|
+
renderer.run()
|
|
101
|
+
except Exception:
|
|
102
|
+
traceback.print_exc()
|
|
103
|
+
raise
|
|
104
|
+
finally:
|
|
105
|
+
_logFile.flush()
|
|
106
|
+
if os.path.getsize(_logPath) == 0:
|
|
107
|
+
try:
|
|
108
|
+
_logFile.close()
|
|
109
|
+
os.unlink(_logPath)
|
|
110
|
+
except OSError:
|
|
111
|
+
pass
|
|
100
112
|
|
|
101
113
|
|
|
102
114
|
#######################################################################################
|
|
@@ -119,8 +131,8 @@ class GuiHandler:
|
|
|
119
131
|
Registers an atexit handler so the child is always cleaned up when
|
|
120
132
|
the parent process exits — no cleanup code required in gui.py.
|
|
121
133
|
"""
|
|
122
|
-
|
|
123
|
-
self.connection =
|
|
134
|
+
parentCommandConnection, childCommandConnection = multiprocessing.Pipe(duplex=True)
|
|
135
|
+
self.connection = parentCommandConnection
|
|
124
136
|
|
|
125
137
|
# Choose the spawn context based on the runtime environment:
|
|
126
138
|
#
|
|
@@ -157,13 +169,13 @@ class GuiHandler:
|
|
|
157
169
|
else:
|
|
158
170
|
ctx = multiprocessing.get_context('fork')
|
|
159
171
|
|
|
160
|
-
# Dedicated duplex admin pipe. The parent holds
|
|
161
|
-
# holds
|
|
162
|
-
# bypassing the command buffer entirely. Closing
|
|
172
|
+
# Dedicated duplex admin pipe. The parent holds parentPriorityConnection; the child
|
|
173
|
+
# holds childPriorityConnection. Admin commands (setRate, getRate) travel here,
|
|
174
|
+
# bypassing the command buffer entirely. Closing parentPriorityConnection sends EOF
|
|
163
175
|
# to the child, which Qt detects via QSocketNotifier and treats as shutdown —
|
|
164
176
|
# independent of the command queue and independent of whether atexit runs.
|
|
165
|
-
|
|
166
|
-
self._adminConn =
|
|
177
|
+
childPriorityConnection, parentPriorityConnection = multiprocessing.Pipe(duplex=True)
|
|
178
|
+
self._adminConn = parentPriorityConnection
|
|
167
179
|
|
|
168
180
|
# With 'spawn' on POSIX (fork+exec), the forked helper calls
|
|
169
181
|
# get_preparation_data() which reads sys.modules['__main__'].__file__.
|
|
@@ -184,7 +196,7 @@ class GuiHandler:
|
|
|
184
196
|
try:
|
|
185
197
|
self.childProcess = ctx.Process(
|
|
186
198
|
target = _launchRenderer,
|
|
187
|
-
args = (
|
|
199
|
+
args = (childCommandConnection, childPriorityConnection, parentPriorityConnection),
|
|
188
200
|
daemon = True # child is killed automatically if parent dies unexpectedly
|
|
189
201
|
)
|
|
190
202
|
self.childProcess.start()
|
|
@@ -194,8 +206,8 @@ class GuiHandler:
|
|
|
194
206
|
_main_mod.__file__ = _saved_file
|
|
195
207
|
if _saved_spec is not _UNSET:
|
|
196
208
|
_main_mod.__spec__ = _saved_spec
|
|
197
|
-
|
|
198
|
-
|
|
209
|
+
childCommandConnection.close() # parent no longer needs the child end of the command pipe
|
|
210
|
+
childPriorityConnection.close() # parent no longer needs the child end of the admin pipe
|
|
199
211
|
|
|
200
212
|
# responseId counter — each sendQuery() call gets a unique ID so the
|
|
201
213
|
# listener thread can route the response back to the correct caller
|