-
Notifications
You must be signed in to change notification settings - Fork 14
/
Copy pathPyQtOFInterfaceC.py
297 lines (236 loc) · 8.61 KB
/
PyQtOFInterfaceC.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
# -*- coding: utf-8 -*-
"""
Qt Widgets for implementing OpenFrames
"""
from PyQt5.QtWidgets import QWidget, QGridLayout, QSizePolicy
from PyQt5.QtGui import QWindow, QOpenGLContext
from PyQt5.QtCore import Qt, QSize, QCoreApplication, QEventLoop
from .PyOFInterfaceC import *
DEFAULT_WIDTH = 320
DEFAULT_HEIGHT = 240
class Window(QWindow):
"""
A QWindow for rendering of WindowProxy
Attributes
----------
_context : QOpenGLContext
Context used to render the window contents
_window_proxy_id : int
The WindowProxy responsible for drawing
_proxy_started_once : bool
True after the first time that _of has been started
"""
def __init__(self, nrow=1, ncol=1, id=0):
"""
Create an instance of OFWindow
Attributes
----------
nrow : int, optional
Number of rows in WindowProxy grid, default = 1
ncol : int, optional
Number of columns in WindowProxy grid, default = 1
id : int, optional
Identifier for the WindowProxy (each WindowProxy should have a unique identifier), default = 0
"""
super().__init__()
self._context = None
self._proxy_started = False
self.setSurfaceType(QWindow.OpenGLSurface)
self._window_proxy_id = id
ofwin_createproxy(0, 0, int(DEFAULT_WIDTH*self.devicePixelRatio()), int(DEFAULT_HEIGHT*self.devicePixelRatio()), nrow, ncol, True, self._window_proxy_id, False)
ofwin_setmakecurrentfunction(self.make_current)
ofwin_setupdatecontextfunction(self.make_current)
ofwin_setswapbuffersfunction(self.swap_buffers)
def stopRendering(self):
"""
Stop OpenFrames rendering
"""
ofwin_activate(self._window_proxy_id)
ofwin_stop()
self._proxyStarted = False
def exposeEvent(self, event):
"""
Overrides QWindow.exposeEvent()
Per QWindow documentation, rendering should be started/stopped based on this event.
"""
ofwin_activate(self._window_proxy_id)
# Enable rendering when window is exposed
if self.isExposed():
ofwin_pauseanimation(False)
if not self._proxy_started:
self._proxy_started = True
ofwin_start()
# Disable rendering when window is not exposed
else:
ofwin_pauseanimation(True)
def resizeEvent(self, event):
"""
Overrides QWindow.resizeEvent()
"""
ofwin_activate(self._window_proxy_id)
ofwin_resizewindow(0, 0, int(event.size().width()*self.devicePixelRatio()), int(event.size().height()*self.devicePixelRatio()))
def mousePressEvent(self, event):
"""
Overrides QWindow.mousePressEvent()
"""
ofwin_activate(self._window_proxy_id)
if ofwin_isrunning() == 1:
button = Window._map_qt_button_to_of_button(event.button())
if button != 0:
ofwin_buttonpress(int(event.x()*self.devicePixelRatio()), int(event.y()*self.devicePixelRatio()), button)
def mouseReleaseEvent(self, event):
"""
Overrides QWindow.mouseReleaseEvent()
"""
ofwin_activate(self._window_proxy_id)
if ofwin_isrunning() == 1:
button = Window._map_qt_button_to_of_button(event.button())
if button != 0:
ofwin_buttonrelease(int(event.x()*self.devicePixelRatio()), int(event.y()*self.devicePixelRatio()), button)
def mouseMoveEvent(self, event):
"""
Overrides QWindow.mouseMoveEvent()
"""
ofwin_activate(self._window_proxy_id)
if ofwin_isrunning() == 1:
ofwin_mousemotion(int(event.x()*self.devicePixelRatio()), int(event.y()*self.devicePixelRatio()))
def keyPressEvent(self, event):
"""
Overrides QWindow.keyPressEvent()
"""
ofwin_activate(self._window_proxy_id)
if ofwin_isrunning() == 1:
key = Window._map_qt_key_event_to_osg_key(event)
ofwin_keypress(key)
# TODO call glGetError() to print any errors that may have occurred
def make_current(self):
"""
Makes _context current for the surface of this window
Returns
-------
bool
True if successful
False if an error occurs
"""
success = False
if self._context is None:
self._context = QOpenGLContext()
self._context.create()
success = self._context.makeCurrent(self)
if success:
# self.initializeOpenGLFunctions()
self._context.doneCurrent()
else:
return success
if self._context is not None:
success = self._context.makeCurrent(self)
# err = glGetError()
return success
def swap_buffers(self):
"""
Swaps the buffer from _context to the surface of this window
"""
if self._context is not None:
self._context.swapBuffers(self)
@staticmethod
def _map_qt_button_to_of_button(qt_button):
"""
Maps a Qt.MouseButton enumeration to an int for OpenFrames
Parameters
----------
qt_button : Qt.MouseButton
The button to map
Returns
-------
int
The corresponding button for OpenFrames
"""
if qt_button == Qt.LeftButton:
return 1
elif qt_button == Qt.RightButton:
return 3
elif qt_button == Qt.MiddleButton:
return 2
elif qt_button == Qt.BackButton:
return 6
elif qt_button == Qt.ForwardButton:
return 7
else:
return 0
@staticmethod
def _map_qt_key_event_to_osg_key(event):
"""
Maps a QKeyEvent to an int for OpenFrames
Parameters
----------
event : PyQt5.QtGui.QKeyEvent.QKeyEvent
The key event to map
Returns
-------
int
The corresponding key code for OpenFrames
"""
if Qt.Key_A <= event.key() <= Qt.Key_Z:
if event.modifiers() & Qt.ShiftModifier:
key = event.key()
else:
key = event.key() 0x20
else:
key = event.key()
return key
class Widget(QWidget):
"""
Encapsulates a QWindow into a widget
QWindow is preferred for rendering over a QWidget because there is more control over OpenGL. A QOpenGLWidget
limitations impose undesirable effects onto WindowProxy. To use QOpenGLWidget, WindowProxy would need to draw to
a QOffscreenSurface and blit the result onto the QOpenGLWidget at appropriate times.
Attributes
----------
_size_hint : QSize
The hint that this widget provides to Qt for sizing
"""
def __init__(self, window_type=Window):
"""
Instantiates a widget that creates a new object of window_type and encapsulates it
Attributes
----------
window_type : class, optional
The class type to encapsulated by this widget, default class is Window.
"""
super().__init__()
self._size_hint = QSize(DEFAULT_WIDTH*self.devicePixelRatio(), DEFAULT_HEIGHT*self.devicePixelRatio())
self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
self.ofwindow = window_type()
container = QWidget.createWindowContainer(self.ofwindow, self)
self.setLayout(QGridLayout())
self.layout().addWidget(container)
def sizeHint(self):
"""
Overrides QWidget.sizeHint() to provide the user set size hint
Returns
-------
QSize
Size hint to Qt
"""
return self._size_hint
def set_size_hint(self, width, height):
"""
Set the preferred size for this widget
The default size policy is QSizePolicy.Expanding. Therefore, Qt tries to make this widget as large as possible.
Under this policy, Qt is allowed to shrink the widget below this size if necessary.
Parameters
----------
width : int
The desired width
height : int
The desired height
References
----------
[1] https://doc.qt.io/qt-5/qsizepolicy.html#Policy-enum
"""
self._size_hint.setWidth(width*self.devicePixelRatio())
self._size_hint.setHeight(height*self.devicePixelRatio())
def closeEvent(self, event):
self.stopRendering()
def stopRendering(self):
self.ofwindow.stopRendering()