📄 owdistancemap.py
字号:
"""
<name>Distance Map</name>
<description>Displays distance matrix as a heat map.</description>
<icon>icons/DistanceMap.png</icon>
<contact>Blaz Zupan (blaz.zupan(@at@)fri.uni-lj.si)</contact>
<priority>1500</priority>
"""
import orange, math
import OWGUI, OWToolbars
from qt import *
from qtcanvas import *
from OWWidget import *
from ColorPalette import *
import OWToolbars
##############################################################################
# parameters that determine the canvas layout
c_offsetX = 10; c_offsetY = 10 # top and left border
c_spaceX = 10; c_spaceY = 10 # space btw graphical elements
c_legendHeight = 15 # height of the legend
c_averageStripeWidth = 12 # width of the stripe with averages
##############################################################################
class EventfulCanvasView(QCanvasView):
def __init__(self, canvas, parent, master):
QCanvasView.__init__(self, canvas,parent)
self.master = master
self.viewport().setMouseTracking(True)
def contentsMousePressEvent (self, event):
self.master.mousePress(event.pos().x(), event.pos().y())
def contentsMouseReleaseEvent (self, event):
self.master.mouseRelease(event.pos().x(), event.pos().y())
def contentsMouseMoveEvent (self, event):
self.master.mouseMove(event.pos().x(), event.pos().y())
# main class
v_sel_width = 2
v_legend_width = 104
v_legend_height = 18
v_legend_offsetX = 5
v_legend_offsetY = 15
class OWDistanceMap(OWWidget):
settingsList = ["CellWidth", "CellHeight", "Merge", "Gamma", "CutLow", "CutHigh", "CutEnabled", "Sort",
"ShowLegend", "ShowAnnotations", "ShowBalloon", "ShowItemsInBalloon", "SendOnRelease", "ColorSchemas"]
def __init__(self, parent=None, signalManager = None):
self.callbackDeposit = [] # deposit for OWGUI callback function
OWWidget.__init__(self, parent, signalManager, 'Distance Map')
self.inputs = [("Distance Matrix", orange.SymMatrix, self.setMatrix)]
self.outputs = [("Examples", ExampleTable), ("Classified Examples", ExampleTableWithClass),("Attribute List", orange.VarList)]
self.clicked = False
self.offsetX = 5
self.offsetY = 5
self.imageWidth = 0
self.imageHeight = 0
self.distanceImage = None
self.legendImage = None
self.ColorSchemas = None
self.shiftPressed = False
#set default settings
self.CellWidth = 15; self.CellHeight = 15
self.Merge = 1;
self.savedMerge = self.Merge
self.Gamma = 1
self.Grid = 1
self.CutLow = 0; self.CutHigh = 0; self.CutEnabled = 0
self.Sort = 0
self.SquareCells = 0
self.ShowLegend = 1;
self.ShowAnnotations = 1;
self.ShowBalloon = 1;
self.ShowItemsInBalloon = 1
self.SendOnRelease = 1
self.loadSettings()
self.maxHSize = 30; self.maxVSize = 30
self.sorting = [("None", self.sortNone), ("Adjacent distance", self.sortAdjDist), ("Random", self.sortRandom)]
self.matrix = self.order = None
# GUI definition
self.tabs = QTabWidget(self.controlArea, 'tabWidget')
# SETTINGS TAB
tab = QVGroupBox(self)
box = QVButtonGroup("Cell Size (Pixels)", tab)
OWGUI.qwtHSlider(box, self, "CellWidth", label='Width: ', labelWidth=38, minValue=1, maxValue=self.maxHSize, step=1, precision=0, callback=self.drawDistanceMap)
self.sliderVSize = OWGUI.qwtHSlider(box, self, "CellHeight", label='Height: ', labelWidth=38, minValue=1, maxValue=self.maxVSize, step=1, precision=0, callback=self.createDistanceMap)
OWGUI.checkBox(box, self, "SquareCells", "Cells as squares", callback = self.drawDistanceMap)
OWGUI.checkBox(box, self, "Grid", "Show grid", callback = self.createDistanceMap)
OWGUI.qwtHSlider(tab, self, "Gamma", box="Gamma", minValue=0.1, maxValue=1, step=0.1, callback=self.drawDistanceMap)
self.colorPalette = ColorPalette(tab, self, "", additionalColors =["Cell outline", "Selected cells"], callback = self.setColor)
self.tabs.insertTab(tab, "Settings")
# FILTER TAB
tab = QVGroupBox(self)
box = QVButtonGroup("Threshold Values", tab)
OWGUI.checkBox(box, self, 'CutEnabled', "Enabled", callback=self.setCutEnabled)
self.sliderCutLow = OWGUI.qwtHSlider(box, self, 'CutLow', label='Low:', labelWidth=33, minValue=-100, maxValue=0, step=0.1, precision=1, ticks=0, maxWidth=80, callback=self.drawDistanceMap)
self.sliderCutHigh = OWGUI.qwtHSlider(box, self, 'CutHigh', label='High:', labelWidth=33, minValue=0, maxValue=100, step=0.1, precision=1, ticks=0, maxWidth=80, callback=self.drawDistanceMap)
if not self.CutEnabled:
self.sliderCutLow.box.setDisabled(1)
self.sliderCutHigh.box.setDisabled(1)
box = QVButtonGroup("Merge", tab)
OWGUI.qwtHSlider(box, self, "Merge", label='Elements:', labelWidth=50, minValue=1, maxValue=100, step=1, callback=self.createDistanceMap, ticks=0)
self.labelCombo = OWGUI.comboBox(tab, self, "Sort", box="Sort", items=[x[0] for x in self.sorting],
tooltip="Choose method to sort items in distance matrix.", callback=self.sortItems)
self.tabs.insertTab(tab, "Filter")
# INFO TAB
tab = QVGroupBox(self)
box = QVButtonGroup("Annotation && Legends", tab)
OWGUI.checkBox(box, self, 'ShowLegend', 'Show legend', callback=self.drawDistanceMap)
OWGUI.checkBox(box, self, 'ShowAnnotations', 'Show annotations', callback=self.drawDistanceMap)
box = QVButtonGroup("Balloon", tab)
OWGUI.checkBox(box, self, 'ShowBalloon', "Show balloon", callback=None)
OWGUI.checkBox(box, self, 'ShowItemsInBalloon', "Display item names", callback=None)
box = QVButtonGroup("Select", tab)
box2 = QHBox(box)
self.box2 = box2
self.buttonUndo = OWToolbars.createButton(box2, 'Undo', self.actionUndo, QPixmap(OWToolbars.dlg_undo), toggle = 0)
self.buttonRemoveAllSelections = OWToolbars.createButton(box2, 'Remove all selections', self.actionRemoveAllSelections, QPixmap(OWToolbars.dlg_clear), toggle = 0)
self.buttonSendSelections = OWToolbars.createButton(box2, 'Send selections', self.sendOutput, QPixmap(OWToolbars.dlg_send), toggle = 0)
OWGUI.checkBox(box, self, 'SendOnRelease', "Send after mouse release", callback=None)
self.tabs.insertTab(tab, "Info")
self.resize(700,400)
self.layout = QVBoxLayout(self.mainArea)
self.canvas = QCanvas()
self.canvasView = EventfulCanvasView(self.canvas, self.mainArea, self)
self.layout.add(self.canvasView)
#construct selector
self.selector = QCanvasRectangle(0, 0, self.CellWidth, self.getCellHeight(), self.canvas)
color = self.colorPalette.getCurrentColorSchema().getAdditionalColors()["Cell outline"]
self.selector.setPen(QPen(self.qrgbToQColor(color),v_sel_width))
self.selector.setZ(20)
self.bubble = BubbleInfo(self.canvas)
self.selection = SelectionManager()
self.selectionLines = []
self.annotationText = []
self.legendText1 = QCanvasText(self.canvas)
self.legendText1.move(0,0)
self.legendText2 = QCanvasText(self.canvas)
self.legendText2.move(v_legend_width,0)
self.errorText = QCanvasText("Bitmap is too large.", self.canvas)
self.errorText.move(10,10)
#restore color schemas from settings
if self.ColorSchemas:
self.colorPalette.setColorSchemas(self.ColorSchemas)
def createColorStripe(self, palette):
dx = v_legend_width
dy = v_legend_height
bmp = chr(252)*dx*2 + reduce(lambda x,y:x+y, [chr(i*250/dx) for i in range(dx)] * (dy-4)) + chr(252)*dx*2
image = ImageItem(bmp, self.canvas, dx, dy, palette, x=v_legend_offsetX, y=v_legend_offsetY, z=0)
return image
def colFromMousePos(self, x, y):
if (x <= self.offsetX or x >= self.offsetX + self.imageWidth):
return -1
else:
return int((x - self.offsetX)/self.CellWidth)
def rowFromMousePos(self, x,y):
if (y <= self.offsetY or y >= self.offsetY + self.imageHeight):
return -1
else:
return int((y - self.offsetY)/self.getCellHeight())
def qrgbToQColor(self, color):
return QColor(qRed(color), qGreen(color), qBlue(color))
def getItemFromPos(self, i):
if (len(self.distanceMap.elementIndices)==0):
j = i
else:
j = self.distanceMap.elementIndices[i]
if self.distanceMapConstructor.order:
j = self.distanceMapConstructor.order[j]
return j
def getCellHeight(self):
if self.SquareCells:
return self.CellWidth
else:
return self.CellHeight
def sendOutput(self):
if len(self.matrix.items)<1:
return
selectedIndices = []
tmp = []
if len(self.selection.getSelection())==0:
self.send("Attribute List", None)
self.send("Examples", None)
self.send("Classified Examples", None)
else:
selection = self.selection.getSelection()
for sel in selection:
if (len(self.distanceMap.elementIndices)==0):
tmp += range(sel[0].x(), sel[1].x()+1)
tmp +=range(sel[0].y(), sel[1].y()+1)
else:
tmp += range(self.distanceMap.elementIndices[sel[0].x()], self.distanceMap.elementIndices[sel[1].x()+1])
tmp +=range(self.distanceMap.elementIndices[sel[0].y()], self.distanceMap.elementIndices[sel[1].y()+1])
for i in tmp:
if self.distanceMapConstructor.order:
if not (self.distanceMapConstructor.order[i] in selectedIndices):
selectedIndices += [self.distanceMapConstructor.order[i]]
if not (i in selectedIndices):
selectedIndices += [i]
items = self.matrix.items
if issubclass(orange.EnumVariable, type(items[0])):
selected = orange.VarList()
for i in selectedIndices:
selected.append(items[i])
self.send("Attribute List", selected)
if isinstance(items[0], orange.Example):
ex = [items[x] for x in selectedIndices]
selected = orange.ExampleTable(items[0].domain, ex)
self.send("Examples", selected)
if selected.domain.classVar:
self.send("Classified Examples", selected)
# callbacks (rutines called after some GUI event, like click on a button)
def setColor(self):
color = self.colorPalette.getCurrentColorSchema().getAdditionalColors()["Cell outline"]
self.selector.setPen(QPen(self.qrgbToQColor(color),v_sel_width))
self.ColorSchemas = self.colorPalette.getColorSchemas()
self.drawDistanceMap()
def setCutEnabled(self):
self.sliderCutLow.box.setDisabled(not self.CutEnabled)
self.sliderCutHigh.box.setDisabled(not self.CutEnabled)
self.drawDistanceMap()
def constructDistanceMap(self):
if self.matrix:
self.distanceMapConstructor = orange.DistanceMapConstructor(distanceMatrix = self.matrix)
self.createDistanceMap()
def createDistanceMap(self):
merge = min(self.Merge, float(self.matrix.dim))
squeeze = 1. / merge
self.distanceMapConstructor.order = self.order
self.distanceMap, self.lowerBound, self.upperBound = self.distanceMapConstructor(squeeze)
self.sliderCutLow.setRange(self.lowerBound, self.upperBound, 0.1)
self.sliderCutHigh.setRange(self.lowerBound, self.upperBound, 0.1)
self.CutLow = max(self.CutLow, self.lowerBound)
self.CutHigh = min(self.CutHigh, self.upperBound)
self.sliderCutLow.setValue(self.CutLow)
self.sliderCutHigh.setValue(self.CutHigh)
self.selection.clear()
self.drawDistanceMap()
def drawDistanceMap(self):
if not self.matrix:
return
if self.matrix.dim * max(int(self.CellWidth), int(self.getCellHeight())) > 32767:
self.errorText.show()
return
self.errorText.hide()
lo = self.CutEnabled and self.CutLow or self.lowerBound
hi = round(self.CutEnabled and self.CutHigh or self.upperBound, 1)
self.offsetX = 5
if self.distanceImage:
self.distanceImage.setCanvas(None)
if self.legendImage:
self.legendImage.setCanvas(None)
if self.ShowLegend==1:
self.legendImage = self.createColorStripe(self.colorPalette.getCurrentColorSchema().getPalette())
self.offsetY = v_legend_height + 30
self.legendText1.setText(str(lo))
self.legendText2.setText(str(hi))
self.legendText1.show()
self.legendText2.show()
else:
self.legendText1.hide()
self.legendText2.hide()
self.offsetY = 5
palette = self.colorPalette.getCurrentColorSchema().getPalette()
bitmap, width, height = self.distanceMap.getBitmap(int(self.CellWidth), int(self.getCellHeight()), lo, hi, self.Gamma, self.Grid)
self.canvas.resize(2000, 2000) # this needs adjustment
for tmpText in self.annotationText:
tmpText.setCanvas(None)
self.annotationText = []
if self.ShowAnnotations==1 and self.Merge==1:
items = self.matrix.items
if len(self.distanceMap.elementIndices)==0:
tmp = [i for i in range(0, len(items))]
else:
tmp = [self.distanceMap.elementIndices[i] for i in range(0, len(items))]
if self.distanceMapConstructor.order:
indices = [self.distanceMapConstructor.order[i] for i in tmp]
else:
indices = tmp
maxHeight = 0
maxWidth = 0
for i in range(0, len(indices)):
# text = str(i)
text = items[indices[i]]
if type(text) not in [str, unicode]:
text = text.name
if text<>"":
tmpText = QCustomCanvasText(text, self.canvas, -90.0)
tmpText.show()
if tmpText.height() > maxHeight:
maxHeight = tmpText.height()
self.annotationText += [tmpText]
tmpText = QCanvasText(text, self.canvas)
tmpText.show()
if tmpText.boundingRect().width() > maxWidth:
maxWidth = tmpText.boundingRect().width()
self.annotationText += [tmpText]
for i in range(0, len(self.annotationText)/2):
self.annotationText[i*2].setX(self.offsetX + maxWidth + 10 + i*self.CellWidth)
self.annotationText[i*2].setY(self.offsetY)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -