source: anuga_core/source/anuga/visualiser/visualiser.py @ 3540

Last change on this file since 3540 was 3540, checked in by steve, 18 years ago

Fixed gui on realtime vis.

File size: 6.1 KB
Line 
1from threading import Thread
2from Tkinter import Tk, Button, N, E, S, W
3from types import FunctionType, TupleType
4from vtk import vtkActor, vtkFloatArray, vtkPolyDataMapper, vtkRenderer
5from vtk.tk.vtkTkRenderWidget import vtkTkRenderWidget
6
7class Visualiser(Thread):
8    """Superclass of both the realtime and offline VTK visualisers
9    """
10    def __init__(self, source):
11        Thread.__init__(self)
12
13        self.source = source
14
15        # Structures for Height Based quantities
16        self.height_quantities = []
17        self.height_zScales = {}
18        self.height_dynamic = {}
19        self.height_offset = {}
20
21        # Structures for colouring quantities
22        self.colours_height = {}
23
24        # Structures used for VTK
25        self.vtk_actors = {}
26        self.vtk_mappers = {}
27        self.vtk_polyData = {}
28
29    def run(self):
30        self.setup_gui()
31        self.setup_grid()
32        # Draw Height Quantities
33        for q in self.height_quantities:
34            self.update_height_quantity(q, self.height_dynamic[q])
35            self.draw_height_quantity(q)
36        self.tk_root.mainloop()
37
38    def redraw_quantities(self, dynamic_only=False):
39        """Redraw all dynamic quantities, unless dynamic_only is True.
40        """
41        # Height quantities
42        for q in self.height_quantities:
43            if (dynamic_only is False) or (self.height_dynamic[q]):
44                self.update_height_quantity(q, self.height_dynamic[q])
45                self.draw_height_quantity(q)
46
47    # --- Height Based Rendering --- #
48
49    def setup_grid(self):
50        """Create the vtkCellArray instance that represents the
51        triangles. Subclasses are expected to override this function
52        to read from their source as appropriate.
53        """
54        pass
55
56    def render_quantity_height(self, quantityName, zScale=1.0, offset=0.0, dynamic=True):
57        """Instruct the visualiser to render a quantity using the
58        value at a point as its height.  The value at each point is
59        multiplied by z_scale and is added to offset, and if
60        dynamic=False, the quantity is not recalculated on each
61        update.
62        """
63        self.height_quantities.append(quantityName)
64        self.height_zScales[quantityName] = zScale
65        self.height_offset[quantityName] = offset
66        self.height_dynamic[quantityName] = dynamic
67
68    def update_height_quantity(self, quantityName, dynamic=True):
69        """Create a vtkPolyData object and store it in
70        self.vtk_polyData[q]. Subclasses are expected to override this
71        function.
72        """
73        pass
74
75
76    def draw_height_quantity(self, quantityName):
77        """Use the vtkPolyData and prepare/update the rest of the VTK
78        rendering pipeline.
79        """
80        if self.vtk_mappers.has_key(quantityName):
81            mapper = self.vtk_mappers[quantityName]
82        else:
83            mapper = self.vtk_mappers[quantityName] = vtkPolyDataMapper()
84        mapper.SetInput(self.vtk_polyData[quantityName])
85        mapper.Update()
86
87        if not self.vtk_actors.has_key(quantityName):
88            actor = self.vtk_actors[quantityName] = vtkActor()
89            actor.SetMapper(mapper)
90            self.vtk_renderer.AddActor(actor)
91
92        if self.colours_height.has_key(quantityName):
93            colour = self.colours_height[quantityName]
94            if type(colour) == TupleType:
95                if type(colour[0]) == FunctionType:
96                    # It's a function, so take colour[1] as the
97                    # lower bound on the scalar range and
98                    # colour[2] as the upper bound on the scalar
99                    # range.
100                    scalars = vtkFloatArray()
101                    map(scalars.InsertNextValue, colour[0](self.build_quantity_dict()))
102                    self.vtk_polyData[quantityName].GetPointData().SetScalars(scalars)
103                    mapper.SetScalarRange(colour[1:])
104                    mapper.Update()
105                else:
106                    # It's a 3-tuple representing an RGB value.
107                    actor.GetProperty().SetColor(colour)
108            else:
109                actor.GetProperty().SetColor(0.5, 0.5, 0.5)
110        else:
111            actor.GetProperty().SetColor(0.5, 0.5, 0.5)
112
113    # --- Colour Coding --- #
114
115    def build_quantity_dict(self):
116        """Build a dictionary mapping quantity name->Numeric array of vertex
117        values for that quantity. Subclasses are expected to override
118        this function."""
119        pass
120
121    def colour_height_quantity(self, quantityName, colour=(0.5, 0.5, 0.5)):
122        """Add colouring to a height based quantity.
123
124        The colour parameter can be one of the following:
125        - a 3-tuple of values in [0,1] to specify R, G, B values
126        - a 3-tuple of values:
127          - a function that takes a dictionary mapping quantity name->Numeric array of vertex values.
128            This function returns a list of vertex values to be used in the colour coding.
129          - a float for the lower bound on the colouring
130          - a float for the upper bound on the colouring
131        """
132        self.colours_height[quantityName] = colour
133           
134    # --- Vector Fields --- #
135
136    # --- GUI Setup --- #
137
138    def setup_gui(self):
139        self.tk_root = Tk()
140        self.tk_root.title("Visualisation")
141        self.tk_root.after(100, self.redraw)
142        self.tk_root.bind("<Destroy>", self.destroyed)
143
144        self.tk_renderWidget = vtkTkRenderWidget(self.tk_root, width=400, height=400)
145        self.tk_renderWidget.grid(row=0, column=0, sticky=N+S+E+W)
146        self.tk_quit = Button(self.tk_root, text="Quit", command=self.shutdown)
147        self.tk_quit.grid(row=2, column=0, sticky=E+W)
148        self.vtk_renderer = vtkRenderer()
149        self.tk_renderWidget.GetRenderWindow().AddRenderer(self.vtk_renderer)
150
151    # --- GUI Events --- #
152
153    def destroyed(self, event):
154        if event.widget == self.tk_root:
155            self.shutdown()
156
157    def redraw(self):
158        self.tk_renderWidget.GetRenderWindow().Render()
159        self.tk_root.update_idletasks()
160        self.tk_root.after(100, self.redraw)
161
162    def shutdown(self):
163        self.tk_root.withdraw()
164        self.tk_root.quit()
Note: See TracBrowser for help on using the repository browser.