summaryrefslogtreecommitdiff
path: root/gr-wxgui/src/python/plotter/plotter_base.py
blob: 25811f09b22d6dc5fcd02174119303b07c3240e8 (plain)
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
#
# Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
#
# This file is part of GNU Radio
#
# GNU Radio 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, or (at your option)
# any later version.
#
# GNU Radio 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 GNU Radio; see the file COPYING.  If not, write to
# the Free Software Foundation, Inc., 51 Franklin Street,
# Boston, MA 02110-1301, USA.
#

import wx
import wx.glcanvas
from OpenGL import GL
import common

BACKGROUND_COLOR_SPEC = (1, 0.976, 1, 1) #creamy white

##################################################
# GL caching interface
##################################################
class gl_cache(object):
	"""
	Cache a set of gl drawing routines in a compiled list.
	"""

	def __init__(self, draw):
		"""
		Create a new cache.
		@param draw a function to draw gl stuff
		"""
		self.changed(True)
		self._draw = draw

	def init(self):
		"""
		To be called when gl initializes.
		Create a new compiled list.
		"""
		self._grid_compiled_list_id = GL.glGenLists(1)

	def draw(self):
		"""
		Draw the gl stuff using a compiled list.
		If changed, reload the compiled list.
		"""
		if self.changed():
			GL.glNewList(self._grid_compiled_list_id, GL.GL_COMPILE)
			self._draw()
			GL.glEndList()
			self.changed(False)
		#draw the grid
		GL.glCallList(self._grid_compiled_list_id)

	def changed(self, state=None):
		"""
		Set the changed flag if state is not None.
		Otherwise return the changed flag.
		"""
		if state is None: return self._changed
		self._changed = state

##################################################
# OpenGL WX Plotter Canvas
##################################################
class plotter_base(wx.glcanvas.GLCanvas, common.mutex):
	"""
	Plotter base class for all plot types.
	"""

	def __init__(self, parent):
		"""
		Create a new plotter base.
		Initialize the GLCanvas with double buffering.
		Initialize various plotter flags.
		Bind the paint and size events.
		@param parent the parent widgit
		"""
		attribList = (wx.glcanvas.WX_GL_DOUBLEBUFFER, wx.glcanvas.WX_GL_RGBA)
		wx.glcanvas.GLCanvas.__init__(self, parent, attribList=attribList);
		self.use_persistence=False
		self.persist_alpha=2.0/15
		self.clear_accum=True
		self._gl_init_flag = False
		self._resized_flag = True
		self._init_fcns = list()
		self._draw_fcns = list()
		self._gl_caches = list()
		self.Bind(wx.EVT_PAINT, self._on_paint)
		self.Bind(wx.EVT_SIZE, self._on_size)
		self.Bind(wx.EVT_ERASE_BACKGROUND, lambda e: None)

	def set_use_persistence(self,enable):
		self.use_persistence=enable
		self.clear_accum=True

	def set_persist_alpha(self,analog_alpha):
		self.persist_alpha=analog_alpha

	def new_gl_cache(self, draw_fcn, draw_pri=50):
		"""
		Create a new gl cache.
		Register its draw and init function.
		@return the new cache object
		"""
		cache = gl_cache(draw_fcn)
		self.register_init(cache.init)
		self.register_draw(cache.draw, draw_pri)
		self._gl_caches.append(cache)
		return cache

	def register_init(self, init_fcn):
		self._init_fcns.append(init_fcn)

	def register_draw(self, draw_fcn, draw_pri=50):
		"""
		Register a draw function with a layer priority.
		Large pri values are drawn last.
		Small pri values are drawn first.
		"""
		for i in range(len(self._draw_fcns)):
			if draw_pri < self._draw_fcns[i][0]:
				self._draw_fcns.insert(i, (draw_pri, draw_fcn))
				return
		self._draw_fcns.append((draw_pri, draw_fcn))

	def _on_size(self, event):
		"""
		Flag the resize event.
		The paint event will handle the actual resizing.
		"""
		self.lock()
		self._resized_flag = True
		self.clear_accum=True
		self.unlock()

	def _on_paint(self, event):
		"""
		Respond to paint events.
		Initialize GL if this is the first paint event.
		Resize the view port if the width or height changed.
		Redraw the screen, calling the draw functions.
		"""
		self.lock()
		self.SetCurrent()

		# check if gl was initialized
		if not self._gl_init_flag:
			GL.glClearColor(*BACKGROUND_COLOR_SPEC)
			for fcn in self._init_fcns: fcn()
			self._gl_init_flag = True

		# check for a change in window size
		if self._resized_flag:
			self.width, self.height = self.GetSize()
			GL.glMatrixMode(GL.GL_PROJECTION)
			GL.glLoadIdentity()
			GL.glOrtho(0, self.width, self.height, 0, 1, 0)
			GL.glMatrixMode(GL.GL_MODELVIEW)
			GL.glLoadIdentity()
			GL.glViewport(0, 0, self.width, self.height)
			for cache in self._gl_caches: cache.changed(True)
			self._resized_flag = False

		# clear buffer if needed
		if self.clear_accum or not self.use_persistence:
			GL.glClear(GL.GL_COLOR_BUFFER_BIT)
			self.clear_accum=False

		# apply fading
		if self.use_persistence:
			GL.glEnable(GL.GL_BLEND)
			GL.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA)

			GL.glBegin(GL.GL_QUADS)
			GL.glColor4f(1,1,1,self.persist_alpha)
			GL.glVertex2f(0, self.height)
			GL.glVertex2f(self.width, self.height)
			GL.glVertex2f(self.width, 0)
			GL.glVertex2f(0, 0)
			GL.glEnd()

			GL.glDisable(GL.GL_BLEND)

		# draw functions
		for fcn in self._draw_fcns: fcn[1]()

		# show result
		self.SwapBuffers()
		self.unlock()

	def update(self):
		"""
		Force a paint event.
		"""
		if not self._gl_init_flag: return
		wx.PostEvent(self, wx.PaintEvent())