Changeset 8404:43ac64b1017f in orange


Ignore:
Timestamp:
07/21/11 02:15:46 (3 years ago)
Author:
matejd <matejd@…>
Branch:
default
Convert:
f4d3ec1ad9049de180b7f1efec3871cf07861ff2
Message:

owscatterplot3d: tooltips now work

Location:
orange/OrangeWidgets
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • orange/OrangeWidgets/Prototypes/OWScatterPlot3D.py

    r8403 r8404  
    1010 
    1111import numpy 
     12 
     13TooltipKind = enum('NONE', 'VISIBLE', 'ALL') # Which attributes should be displayed? 
    1214 
    1315class OWScatterPlot3D(OWWidget): 
     
    123125        self.toolbarSelection = None 
    124126 
     127        self.tooltip_kind = TooltipKind.NONE 
     128        box = OWGUI.widgetBox(self.settings_tab, "Tooltips Settings") 
     129        OWGUI.comboBox(box, self, 'tooltip_kind', items = [ 
     130            'Don\'t Show Tooltips', 'Show Visible Attributes', 'Show All Attributes'], callback = self.on_axis_change) 
     131 
     132        self.plot.mouseover_callback = self.mouseover_callback 
     133        self.shown_attr_indices = [] 
     134 
    125135        self.main_tab.layout().addStretch(100) 
    126136        self.settings_tab.layout().addStretch(100) 
     
    132142        self.subsetData = None 
    133143        self.resize(1000, 600) 
     144 
     145    def mouseover_callback(self, index): 
     146        if self.tooltip_kind == TooltipKind.VISIBLE: 
     147            self.plot.show_tooltip(self.get_example_tooltip(self.data[index], self.shown_attr_indices)) 
     148        elif self.tooltip_kind == TooltipKind.ALL: 
     149            self.plot.show_tooltip(self.get_example_tooltip(self.data[index])) 
     150 
     151    def get_example_tooltip(self, example, indices=None, max_indices=20): 
     152        if indices and type(indices[0]) == str: 
     153            indices = [self.attr_name_index[i] for i in indices] 
     154        if not indices: 
     155            indices = range(len(self.data.domain.attributes)) 
     156 
     157        if example.domain.classVar: 
     158            classIndex = self.attr_name_index[example.domain.classVar.name] 
     159            while classIndex in indices: 
     160                indices.remove(classIndex) 
     161 
     162        text = '<b>Attributes:</b><br>' 
     163        for index in indices[:max_indices]: 
     164            attr = self.attr_name[index] 
     165            if attr not in example.domain:  text += '&nbsp;'*4 + '%s = ?<br>' % (attr) 
     166            elif example[attr].isSpecial(): text += '&nbsp;'*4 + '%s = ?<br>' % (attr) 
     167            else:                           text += '&nbsp;'*4 + '%s = %s<br>' % (attr, str(example[attr])) 
     168 
     169        if len(indices) > max_indices: 
     170            text += '&nbsp;'*4 + ' ... <br>' 
     171 
     172        if example.domain.classVar: 
     173            text = text[:-4] 
     174            text += '<hr><b>Class:</b><br>' 
     175            if example.getclass().isSpecial(): text += '&nbsp;'*4 + '%s = ?<br>' % (example.domain.classVar.name) 
     176            else:                              text += '&nbsp;'*4 + '%s = %s<br>' % (example.domain.classVar.name, str(example.getclass())) 
     177 
     178        if len(example.domain.getmetas()) != 0: 
     179            text = text[:-4] 
     180            text += '<hr><b>Meta attributes:</b><br>' 
     181            for key in example.domain.getmetas(): 
     182                try: text += '&nbsp;'*4 + '%s = %s<br>' % (example.domain[key].name, str(example[key])) 
     183                except: pass 
     184        return text[:-4] 
    134185 
    135186    def change_selection_type(self): 
     
    155206            self.axis_candidate_attrs = [attr for attr in self.all_attrs 
    156207                if attr.varType in [orange.VarTypes.Continuous, orange.VarTypes.Discrete]] 
     208 
     209            self.attr_name_index = {} 
     210            for i, attr in enumerate(self.all_attrs): 
     211                self.attr_name_index[attr.name] = i 
     212 
     213            self.attr_name = {} 
     214            for i, attr in enumerate(self.all_attrs): 
     215                self.attr_name[i] = attr.name 
    157216 
    158217            self.color_attr_cb.addItem('(Same color)') 
     
    174233            array, c, w = self.data.toNumpyMA() 
    175234            if len(c): 
    176               array = numpy.hstack((array, c.reshape(-1,1))) 
     235                array = numpy.hstack((array, c.reshape(-1,1))) 
    177236            self.data_array = array 
    178237 
     
    181240                                                              ], axis=0) 
    182241            self.color_attr = max(len(self.axis_candidate_attrs) - 1, 0) 
     242            self.shown_attr_indices = [self.x_attr, self.y_attr, self.z_attr, self.color_attr] 
    183243            self.openContext('', data) 
    184244 
  • orange/OrangeWidgets/owplot3d.py

    r8403 r8404  
    3737        Removes everything from the graph. 
    3838""" 
    39  
    40 __all__ = ['OWPlot3D', 'Symbol', 'SelectionType'] 
    4139 
    4240from PyQt4.QtCore import * 
     
    5452from OpenGL.GLU import * 
    5553from OpenGL.arrays import ArrayDatatype 
    56 from ctypes import byref, c_char_p, c_int, create_string_buffer 
     54from ctypes import byref, c_char_p, c_int, create_string_buffer, c_ubyte 
    5755import sys 
    5856import numpy 
     
    9795glBindBuffer = gl.glBindBuffer 
    9896glBufferData = gl.glBufferData 
     97glReadPixels = gl.glReadPixels 
    9998 
    10099def normalize(vec): 
     
    142141PlotState = enum('IDLE', 'DRAGGING_LEGEND', 'ROTATING', 'SCALING', 'SELECTING', 'PANNING') 
    143142 
    144 # TODO: more symbols 
    145143Symbol = enum('TRIANGLE', 'RECTANGLE', 'PENTAGON', 'CIRCLE') 
    146  
    147144SelectionType = enum('ZOOM', 'RECTANGLE', 'POLYGON') 
    148145 
     
    378375        self.setMouseTracking(True) 
    379376 
     377        self.mouseover_callback = None 
     378 
    380379    def __del__(self): 
    381380        # TODO: delete shaders and vertex buffer 
     
    396395 
    397396        vertex_shader_source = ''' 
    398             attribute vec3 position; 
     397            attribute vec4 position; 
    399398            attribute vec3 offset; 
    400399            attribute vec4 color; 
    401400 
    402401            uniform bool face_symbols; 
     402            uniform bool tooltip_mode; 
    403403            uniform float symbol_scale; 
    404404            uniform float transparency; 
     
    434434                offset_rotated = invs * offset_rotated; 
    435435 
    436               //position += translation; 
    437               position *= scale; 
    438               vec4 off_pos = vec4(position+offset_rotated, 1); 
     436              vec3 pos = position.xyz; 
     437              //pos += translation; 
     438              pos *= scale; 
     439              vec4 off_pos = vec4(pos+offset_rotated, 1); 
    439440 
    440441              gl_Position = gl_ProjectionMatrix * gl_ModelViewMatrix * off_pos; 
    441               position = abs(position); 
    442               float manhattan_distance = max(max(position.x, position.y), position.z)+5.; 
    443               var_color = vec4(color.rgb, pow(min(1, 10. / manhattan_distance), 5)); 
     442 
     443              if (tooltip_mode) { 
     444                // We've packed example index into .w component of this vertex, 
     445                // to output it to the screen, it has to be broken down into RGBA. 
     446                int index = int(position.w); 
     447                var_color = vec4(((index & 0xFF)) / 255., 
     448                                 ((index & 0xFF00) >> 8) / 255., 
     449                                 ((index & 0xFF0000) >> 16) / 255., 
     450                                 ((index & 0xFF000000) >> 24) / 255.); 
     451              } 
     452              else { 
     453                pos = abs(pos); 
     454                float manhattan_distance = max(max(pos.x, pos.y), pos.z)+5.; 
     455                var_color = vec4(color.rgb, pow(min(1, 10. / manhattan_distance), 5)); 
     456              } 
    444457            } 
    445458            ''' 
     
    488501        self.symbol_shader_scale        = glGetUniformLocation(self.symbol_shader, 'scale') 
    489502        self.symbol_shader_translation  = glGetUniformLocation(self.symbol_shader, 'translation') 
     503        self.symbol_shader_tooltip_mode = glGetUniformLocation(self.symbol_shader, 'tooltip_mode') 
    490504        linked = c_int() 
    491505        glGetProgramiv(self.symbol_shader, GL_LINK_STATUS, byref(linked)) 
     
    493507            print('Failed to link shader!') 
    494508        else: 
    495             print('Shaders compiled and linked!') 
     509            print('Shaders compiled and linked.') 
     510 
     511        self.tooltip_fbo = QtOpenGL.QGLFramebufferObject(512, 512) 
     512        if self.tooltip_fbo.isValid(): 
     513            print('Tooltip FBO created.') 
     514        else: 
     515            print('Failed to create tooltip FBO!') 
    496516 
    497517    def resizeGL(self, width, height): 
     
    532552                vao, vao_outline, (X, Y, Z), labels = params 
    533553                glUseProgram(self.symbol_shader) 
     554                glUniform1i(self.symbol_shader_tooltip_mode, False) 
    534555                glUniform1i(self.symbol_shader_face_symbols, self.face_symbols) 
    535556                glUniform1f(self.symbol_shader_symbol_scale, self.symbol_scale) 
     
    541562                    glBindVertexArray(vao.value) 
    542563                    glDrawArrays(GL_TRIANGLES, 0, vao.num_vertices) 
     564 
     565                    self.tooltip_fbo.bind() 
     566                    glClearColor(1, 1, 1, 1) 
     567                    glClear(GL_COLOR_BUFFER_BIT) 
     568                    glDisable(GL_BLEND) 
     569                    glUniform1i(self.symbol_shader_tooltip_mode, True) 
     570                    glDrawArrays(GL_TRIANGLES, 0, vao.num_vertices) 
     571                    self.tooltip_fbo.release() 
     572 
    543573                    glBindVertexArray(0) 
    544574                else: 
     
    843873        outline_indices = [] 
    844874        index = 0 
     875        ai = -1 # Array index (used in color-picking). 
    845876        for x, y, z, (r,g,b,a), size, symbol in zip(X, Y, Z, colors, sizes, symbols): 
    846877            x -= self.initial_center[0] 
     
    854885            angle_inc = 2.*pi / n 
    855886            angle = angle_inc / 2. 
     887            ai += 1 
    856888            for i in range(n): 
    857                 vertices.extend([x,y,z, 0,0,0, r,g,b,a]) 
    858                 vertices.extend([x,y,z, -cos(angle)*sO2, -sin(angle)*sO2, 0, r,g,b,a]) 
     889                vertices.extend([x,y,z, ai, 0,0,0, r,g,b,a]) 
     890                vertices.extend([x,y,z, ai, -cos(angle)*sO2, -sin(angle)*sO2, 0, r,g,b,a]) 
    859891                angle += angle_inc 
    860                 vertices.extend([x,y,z, -cos(angle)*sO2, -sin(angle)*sO2, 0, r,g,b,a]) 
     892                vertices.extend([x,y,z, ai, -cos(angle)*sO2, -sin(angle)*sO2, 0, r,g,b,a]) 
    861893                outline_indices.extend([index+1, index+2]) 
    862894                index += 3 
     
    871903        glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer.value) 
    872904 
    873         vertex_size = (3+3+4)*4 
    874         glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, vertex_size, 0) 
     905        vertex_size = (4+3+4)*4 
     906        glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, vertex_size, 0) 
     907        glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, vertex_size, 4*4) 
     908        glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, vertex_size, 7*4) 
    875909        glEnableVertexAttribArray(0) 
    876         glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, vertex_size, 3*4) 
    877910        glEnableVertexAttribArray(1) 
    878         glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, vertex_size, 6*4) 
    879911        glEnableVertexAttribArray(2) 
    880912 
     
    906938        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_buffer.value) 
    907939        glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer.value) 
    908         glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, vertex_size, 0) 
     940        glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, vertex_size, 0) 
     941        glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, vertex_size, 4*4) 
     942        glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, vertex_size, 7*4) 
    909943        glEnableVertexAttribArray(0) 
    910         glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, vertex_size, 3*4) 
    911944        glEnableVertexAttribArray(1) 
    912         glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, vertex_size, 6*4) 
    913945        glEnableVertexAttribArray(2) 
    914946 
     
    10211053        pos = event.pos() 
    10221054 
     1055        if self.mouseover_callback != None: 
     1056            # Use pixel-color-picking to read example index under mouse cursor. 
     1057            value = c_int(-1) 
     1058            self.tooltip_fbo.bind() 
     1059            glReadPixels(pos.x(), self.height() - pos.y(), 
     1060                         1, 1, 
     1061                         GL_RGBA, 
     1062                         GL_UNSIGNED_BYTE, 
     1063                         byref(value)) 
     1064            self.tooltip_fbo.release() 
     1065            if value.value != -1: 
     1066                self.mouseover_callback(value.value) 
     1067 
    10231068        if self.state == PlotState.IDLE: 
    10241069            for selection in self.selections: 
     
    10281073            else: 
    10291074                self.setCursor(Qt.ArrowCursor) 
    1030             # Don't do anything else if idle 
     1075            self.mouse_pos = pos 
    10311076            return 
    10321077 
     
    11111156        self.updateGL() 
    11121157 
     1158    def show_tooltip(self, text): 
     1159        x, y = self.mouse_pos.x(), self.mouse_pos.y() 
     1160        QToolTip.showText(self.mapToGlobal(QPoint(x, y)), text, self, QRect(x-3, y-3, 6, 6)) 
     1161 
    11131162    def clear(self): 
    11141163        self.commands = [] 
Note: See TracChangeset for help on using the changeset viewer.