Changeset 9077:ac363d355baa in orange


Ignore:
Timestamp:
10/07/11 14:49:53 (3 years ago)
Author:
ales_erjavec <ales.erjavec@…>
Branch:
default
Convert:
37ccedb81ec7a11081bbd8db578e53e5ea014c3c
Message:

Added "Commit on change" check box (added more signals in DataTool subclasses to report when editing is finished).
Remove class variable from the data if it has only one value.
Select/Lasso now remember selections between multiple moves.
Implements #976.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • orange/OrangeWidgets/Data/OWPaintData.py

    r9044 r9077  
    2828class PaintDataGraph(OWGraph): 
    2929    def setData(self, data, attr1, attr2): 
     30        """ Set the data to display. 
     31         
     32        :param data: data 
     33        :param attr1: attr for X axis 
     34        :param attr2: attr for Y axis 
     35        """ 
    3036        OWGraph.setData(self, data) 
    3137        self.data = data 
     
    5662            painter.drawPixmap(0, 0, pixmap) 
    5763         
     64         
    5865class DataTool(QObject): 
    59     """ A base class for data tools that operate on OWGraph 
    60     widgets by installing itself as its event filter. 
     66    """ A base class for data tools that operate on PaintDataGraph 
     67    widget by installing itself as its event filter. 
     68      
    6169    """ 
    62     classIndex = 0 
    6370    cursor = Qt.ArrowCursor 
    6471    class optionsWidget(QFrame): 
     72        """ An options (parameters) widget for the tool (this will 
     73        be put in the "Options" box in the main OWPaintData widget 
     74        when this tool is selected. 
     75         
     76        """ 
    6577        def __init__(self, tool, parent=None): 
    6678            QFrame.__init__(self, parent) 
     
    7284         
    7385    def setGraph(self, graph): 
     86        """ Install this tool to operate on ``graph``. If another tool 
     87        is already operating on the graph it will first be removed. 
     88         
     89        """ 
    7490        self.graph = graph 
    7591        if graph: 
     
    7793            if installed: 
    7894                self.graph.canvas().removeEventFilter(installed) 
     95                installed.removed() 
    7996            self.graph.canvas().setMouseTracking(True) 
    8097            self.graph.canvas().installEventFilter(self) 
     
    83100            self.graph.setCursor(self.cursor) 
    84101            self.graph.replot() 
     102            self.installed() 
     103             
     104    def removed(self): 
     105        """ Called when the tool is removed from a graph. 
     106        """ 
     107        pass 
     108     
     109    def installed(self): 
     110        """ Called when the tool is installed on a graph. 
     111        """ 
    85112         
    86113    def eventFilter(self, obj, event): 
     
    99126        elif event.type() == QEvent.Enter: 
    100127            return self.enterEvent(event) 
    101         return False   
    102      
     128        return False 
     129     
     130    # These are actually event filters (note the return values) 
    103131    def paintEvent(self, event): 
    104132        return False 
     
    142170    def dataTransform(self, *args): 
    143171        pass 
     172     
    144173     
    145174class GraphSelections(QObject): 
     
    187216        index = self.regionAt(event) 
    188217        if index == -1 or not self.movable: 
    189             if event.modifiers() & Qt.ControlModifier and self.multipleSelection:  
     218            if event.modifiers() & Qt.ControlModifier and self.multipleSelection: 
    190219                self.addSelectionRegion((pos, pos)) 
    191220            else: 
     
    214243        self.update(event) 
    215244        if self._moving_index != -1: 
    216             self.emit(SIGNAL("selectionRegionMoveEnd(int)"), self._moving_index) 
     245            self.emit(SIGNAL("selectionRegionMoveFinished(int, QPointF, QPainterPath)"),  
     246                      self._moving_index, self.getPos(event), 
     247                      self.toPath(self.selection[self._moving_index])) 
    217248        self._moving_index = -1 
    218249                       
    219          
    220250    def regionAt(self, event): 
    221251        pos = self.getPos(event) 
     
    260290        return QTransform(sx, 0.0, 0.0, sy, dx, dy) 
    261291     
     292     
    262293class SelectTool(DataTool): 
    263294    class optionsWidget(QFrame): 
    264         actions = [("Move", ""), 
    265                    ("Delete selected", "")] 
    266295        def __init__(self, tool, parent=None): 
    267296            QFrame.__init__(self, parent) 
     
    273302             
    274303            layout.addWidget(delete) 
     304            layout.addStretch(10) 
    275305            self.setLayout(layout) 
    276306         
    277              
    278     def __init__(self, graph, parent=None): 
     307    def __init__(self, graph, parent=None, graphSelection=None): 
    279308        DataTool.__init__(self, graph, parent) 
    280         self.selection = GraphSelections(graph) 
     309        if graphSelection is None: 
     310            self.selection = GraphSelections(graph) 
     311        else: 
     312            self.selection = graphSelection 
     313             
    281314        self.pen = QPen(Qt.black, 1, Qt.DashDotLine) 
    282315        self.pen.setCosmetic(True) 
     
    285318        self.connect(self.selection, SIGNAL("selectionRegionMoveStarted(int, QPointF, QPainterPath)"), self.onMoveStarted) 
    286319        self.connect(self.selection, SIGNAL("selectionRegionMoved(int, QPointF, QPainterPath)"), self.onMove) 
    287          
     320        self.connect(self.selection, SIGNAL("selectionRegionMoveFinished(int, QPointF, QPainterPath)"), self.onMoveFinished) 
     321        self.connect(self.selection, SIGNAL("selectionRegionUpdated(int, QPainterPath)"), self.invalidateMoveSelection) 
     322        self._validMoveSelection = False 
     323        self._moving = None 
    288324         
    289325    def setGraph(self, graph): 
     
    292328            self.selection.setParent(graph) 
    293329 
     330    def installed(self): 
     331        DataTool.installed(self) 
     332        self.invalidateMoveSelection() 
     333         
    294334    def paintEvent(self, event): 
    295335        if self.selection: 
     
    331371            self.graph.replot() 
    332372        return True 
     373     
     374    def invalidateMoveSelection(self, *args): 
     375        self._validMoveSelection = False 
     376        self._moving = None 
    333377         
    334378    def onMoveStarted(self, index, pos, path): 
    335379        data = self.graph.data 
    336380        attr1, attr2 = self.graph.attr1, self.graph.attr2 
    337         self._moving = [(i, float(ex[attr1]), float(ex[attr2])) for i, ex in enumerate(data)] 
    338         self._moving = [(i, x, y) for i, x, y in self._moving if path.contains(QPointF(x, y))] 
    339         self._moving_pos = pos 
     381        if not self._validMoveSelection: 
     382            self._moving = [(i, float(ex[attr1]), float(ex[attr2])) for i, ex in enumerate(data)] 
     383            self._moving = [(i, x, y) for i, x, y in self._moving if path.contains(QPointF(x, y))] 
     384            self._validMoveSelection = True 
     385        self._move_anchor = pos 
    340386         
    341387    def onMove(self, index, pos, path): 
     
    343389        attr1, attr2 = self.graph.attr1, self.graph.attr2 
    344390         
    345         diff = pos - self._moving_pos 
     391        diff = pos - self._move_anchor  
    346392        for i, x, y in self._moving: 
    347393            ex = data[i] 
     
    349395            ex[attr2] = y + diff.y() 
    350396        self.graph.updateGraph() 
     397        self.emit(SIGNAL("editing()")) 
     398         
     399    def onMoveFinished(self, index, pos, path): 
     400        self.onMove(index, pos, path) 
     401        diff = pos - self._move_anchor 
     402        self._moving = [(i, x + diff.x(), y + diff.y()) \ 
     403                        for i, x, y in self._moving] 
     404         
     405        self.emit(SIGNAL("editingFinished()")) 
    351406         
    352407    def deleteSelected(self, *args): 
     
    357412        for i in reversed(selected): 
    358413            del data[i] 
    359         self.graph.updateGraph()  
     414        self.graph.updateGraph() 
     415        if selected: 
     416            self.emit(SIGNAL("editing()")) 
     417            self.emit(SIGNAL("editingFinished()")) 
    360418         
    361419class GraphLassoSelections(GraphSelections): 
     
    387445        self.update(event) 
    388446        if self._moving_index != -1: 
    389             self.emit(SIGNAL("selectionRegionMoveEnd(int)"), self._moving_index) 
     447            self.emit(SIGNAL("selectionRegionMoveFinished(int, QPointF, QPainterPath)"),  
     448                      self._moving_index, self.getPos(event), 
     449                      self.toPath(self.selection[self._moving_index])) 
    390450        self._moving_index = -1 
     451         
    391452         
    392453class LassoTool(SelectTool): 
    393454    def __init__(self, graph, parent=None): 
    394         DataTool.__init__(self, graph, parent) 
    395         self.selection = GraphLassoSelections(graph) 
    396         self.pen = QPen(Qt.black, 1, Qt.DashDotLine) 
    397         self.pen.setCosmetic(True) 
    398         self.pen.setJoinStyle(Qt.RoundJoin) 
    399         self.pen.setCapStyle(Qt.RoundCap) 
    400         self.connect(self.selection, SIGNAL("selectionRegionMoveStarted(int, QPointF, QPainterPath)"), self.onMoveStarted) 
    401         self.connect(self.selection, SIGNAL("selectionRegionMoved(int, QPointF, QPainterPath)"), self.onMove) 
     455        SelectTool.__init__(self, graph, parent,  
     456                            graphSelection=GraphLassoSelections(graph)) 
     457#        self.selection = GraphLassoSelections(graph) 
     458#        self.pen = QPen(Qt.black, 1, Qt.DashDotLine) 
     459#        self.pen.setCosmetic(True) 
     460#        self.pen.setJoinStyle(Qt.RoundJoin) 
     461#        self.pen.setCapStyle(Qt.RoundCap) 
     462#        self.connect(self.selection, SIGNAL("selectionRegionMoveStarted(int, QPointF, QPainterPath)"), self.onMoveStarted) 
     463#        self.connect(self.selection, SIGNAL("selectionRegionMoved(int, QPointF, QPainterPath)"), self.onMove) 
     464#        self.connect(self.selection, SIGNAL("selectionRegionMoveFinished(int, QPointF, QPainterPath)"), self.onMoveFinished) 
     465     
    402466     
    403467class ZoomTool(DataTool): 
     
    422486    def keyPressEvent(self, event): 
    423487        return False 
     488     
    424489     
    425490class PutInstanceTool(DataTool): 
     
    431496            attr1, attr2 = self.attributes() 
    432497            self.dataTransform(attr1, val1, attr2, val2) 
     498            self.emit(SIGNAL("editing()")) 
     499            self.emit(SIGNAL("editingFinished()")) 
    433500        return True 
    434501         
     
    440507        self.graph.data.append(example) 
    441508        self.graph.updateGraph(dataInterval=(-1, sys.maxint)) 
     509         
    442510         
    443511class BrushTool(DataTool): 
     
    476544            attr1, attr2 = self.attributes() 
    477545            self.dataTransform(attr1, x, rx, attr2, y, ry) 
     546            self.emit(SIGNAL("editing()")) 
    478547        self.graph.replot() 
    479548        return True 
     
    485554            attr1, attr2 = self.attributes() 
    486555            self.dataTransform(attr1, x, rx, attr2, y, ry) 
     556            self.emit(SIGNAL("editing()")) 
    487557        self.graph.replot() 
    488558        return True 
     
    490560    def mouseReleaseEvent(self, event): 
    491561        self.graph.replot() 
     562        if event.button() & Qt.LeftButton: 
     563            self.emit(SIGNAL("editingFinished()")) 
    492564        return True 
    493565     
     
    534606        self.graph.data.extend(new) 
    535607        self.graph.updateGraph(dataInterval=(-len(new), sys.maxint)) 
     608     
    536609     
    537610class MagnetTool(BrushTool): 
     
    549622            ex[attr2] = y1 + dy 
    550623        self.graph.updateGraph() 
     624     
    551625     
    552626class JitterTool(BrushTool): 
     
    566640        self.graph.updateGraph() 
    567641         
     642         
    568643class EnumVariableModel(PyListModel): 
    569644    def __init__(self, var, parent=None, **kwargs): 
     
    604679        return QIcon(pixmap) 
    605680    
     681    
    606682class OWPaintData(OWWidget): 
    607683    TOOLS = [("Brush", "Create multiple instances", BrushTool,  icon_brush), 
     
    613689             ("Zoom", "Zoom", ZoomTool, OWToolbars.dlg_zoom) #"GenerateDataZoomTool.png") 
    614690             ] 
     691    settingsList = ["commitOnChange"] 
    615692    def __init__(self, parent=None, signalManager=None, name="Data Generator"): 
    616693        OWWidget.__init__(self, parent, signalManager, name, wantGraph=True) 
     
    621698        self.attributes = [] 
    622699        self.cov = [] 
     700        self.commitOnChange = False 
    623701         
    624702        self.loadSettings() 
     
    685763         
    686764#        OWGUI.checkBox(self.controlArea, self, "addClassAsMeta", "Add class ids as meta attributes") 
    687          
    688765        OWGUI.rubber(self.controlArea) 
    689         OWGUI.button(self.controlArea, self, "Commit", callback=self.commit, default=True) 
     766        box = OWGUI.widgetBox(self.controlArea, "Commit") 
     767         
     768        cb = OWGUI.checkBox(box, self, "commitOnChange", "Commit on change", 
     769                            tooltip="Send the data on any change.", 
     770                            callback=self.commitIf,) 
     771        b = OWGUI.button(box, self, "Commit",  
     772                         callback=self.commit, default=True) 
     773        OWGUI.setStopper(self, b, cb, "dataChangedFlag", callback=self.commit) 
    690774         
    691775        self.graph = PaintDataGraph(self) 
     
    697781        self.currentOptionsWidget = None 
    698782        self.data = [] 
     783        self.dataChangedFlag = False  
    699784        self.domain = None 
    700785         
     
    766851        if index is not None: 
    767852            self.classVariable.baseValue = index 
    768          
    769     def onAddFeature(self): 
    770         self.variablesModel.append(orange.FloatVariable("New feature")) 
    771         self.varListView.edit(self.variablesModel.index(len(self.variablesModel) - 1)) 
    772          
    773     def onRemoveFeature(self): 
    774         raise NotImplemented 
    775    
    776              
    777     def onVariableSelectionChange(self, selected, deselected): 
    778         self.selected 
    779853     
    780854    def onToolAction(self, tool): 
     
    786860            option = newtool.optionsWidget(newtool, self) 
    787861            self.optionsLayout.addWidget(option) 
    788             self.connect(newtool, SIGNAL("dataChanged()"), self.graph.updateGraph) 
     862#            self.connect(newtool, SIGNAL("dataChanged()"), self.graph.updateGraph) 
     863#            self.connect(newtool, SIGNAL("dataChanged()"), self.onDataChanged) 
     864            self.connect(newtool, SIGNAL("editing()"), self.onDataChanged) 
     865            self.connect(newtool, SIGNAL("editingFinished()"), self.commitIf) 
    789866            self.toolsStackCache[tool] = (newtool, option) 
    790867         
     
    801878                self.data = orange.ExampleTable(self.domain) 
    802879            self.graph.setData(self.data, 0, 1) 
    803      
     880             
     881    def onDataChanged(self): 
     882        self.dataChangedFlag = True 
     883     
     884    def commitIf(self): 
     885        if self.commitOnChange and self.dataChangedFlag: 
     886            self.commit() 
     887        else: 
     888            self.dataChangedFlag = True 
     889             
    804890    def commit(self): 
    805         if self.addClassAsMeta: 
    806             domain = orange.Domain(self.graph.data.domain.attributes, None) 
    807             domain.addmeta(orange.newmetaid(), self.graph.data.domain.classVar) 
    808             data = orange.ExampleTable(domain, self.graph.data) 
    809         else: 
    810             data = self.graph.data 
    811         self.send("Example Table", self.graph.data) 
     891        data = self.graph.data 
     892        values = set([str(ex.getclass()) for ex in data]) 
     893        if len(values) == 1: 
     894            # Remove the useless class variable. 
     895            domain = orange.Domain(data.domain.attributes, None) 
     896            data = orange.ExampleTable(domain, data) 
     897        self.send("Example Table", data) 
    812898         
    813899         
Note: See TracChangeset for help on using the changeset viewer.