📄 ownomogramgraph.py
字号:
# Nomogram visualization widget. It is used together with OWNomogram
from OWWidget import *
#from Numeric import *
import Numeric
import math
from qtcanvas import *
import time, statc
# constants
SE_Z = -100
HISTOGRAM_Z = -200
aproxZero = 0.0001
def norm_factor(p):
max = 10.
min = -10.
z = 0.
eps = 0.001
while ((max-min)>eps):
pval = statc.zprob(z)
if pval>p:
max = z
if pval<p:
min = z
z = (max + min)/2.
return z
def unique(lst):
d = {}
for item in lst:
d[item] = None
return d.keys()
# returns difference between continuous label values
def getDiff(d):
if d < 1 and d>0:
mnum = d/pow(10, math.floor(math.log10(d)))
else:
mnum = d
if d==0:
return 0
if str(mnum)[0]>'4':
return math.pow(10,math.floor(math.log10(d))+1)
elif str(mnum)[0]<'2':
return 2*math.pow(10,math.floor(math.log10(d)))
else:
return 5*math.pow(10,math.floor(math.log10(d)))
# Detailed description of selected value in attribute:
# - shows its value on the upper scale (points, log odds, ...)
# - its real value (example: age = 45) or in case of discrete attribute:
# - shows proportional value between two (real,possible) values of attribute
class Descriptor(QCanvasRectangle):
def __init__(self, canvas, attribute, z=60):
apply(QCanvasRectangle.__init__,(self, canvas))
self.canvas = canvas
self.attribute = attribute
self.setPen(QPen(Qt.black, 2))
self.setBrush(QBrush(QColor(135,206,250)))
self.splitLine = QCanvasLine(canvas)
self.header = QCanvasText(canvas)
self.header.setTextFlags(Qt.AlignLeft+Qt.AlignTop)
self.headerValue = QCanvasText(canvas)
self.headerValue.setTextFlags(Qt.AlignRight + Qt.AlignTop)
self.valName = QCanvasText(canvas)
self.valName.setTextFlags(Qt.AlignLeft+Qt.AlignTop)
self.value = QCanvasText(canvas)
self.value.setTextFlags(Qt.AlignRight + Qt.AlignTop)
self.supportingValName = QCanvasText(canvas)
self.supportingValName.setTextFlags(Qt.AlignLeft+Qt.AlignTop)
self.supportingValue = QCanvasText(canvas)
self.supportingValue.setTextFlags(Qt.AlignRight + Qt.AlignTop)
self.setZAll(z)
def drawAll(self, x, y):
def getNearestAtt(selectedBeta):
if isinstance(self.attribute, AttrLineCont):
for i in range(len(self.attribute.attValues)):
if self.attribute.attValues[i].betaValue==selectedBeta:
nearestRight = self.attribute.attValues[i]
nearestLeft = self.attribute.attValues[i]
break
elif i>0 and self.attribute.attValues[i].betaValue>selectedBeta and selectedBeta>self.attribute.attValues[i-1].betaValue:
nearestRight = self.attribute.attValues[i]
nearestLeft = self.attribute.attValues[i-1]
break
elif i>0 and self.attribute.attValues[i].betaValue<selectedBeta and selectedBeta<self.attribute.attValues[i-1].betaValue:
nearestRight = self.attribute.attValues[i-1]
nearestLeft = self.attribute.attValues[i]
break
elif i == len(self.attribute.attValues)-1:
nearestRight = self.attribute.attValues[i]
nearestLeft = self.attribute.attValues[i-1]
break
else:
nearestLeft = filter(lambda x: x.betaValue == max([at.betaValue for at in filter(lambda x: x.betaValue <= selectedBeta, self.attribute.attValues)]) ,self.attribute.attValues)[0]
nearestRight = filter(lambda x: x.betaValue == min([at.betaValue for at in filter(lambda x: x.betaValue >= selectedBeta, self.attribute.attValues)]) ,self.attribute.attValues)[0]
return (nearestLeft, nearestRight)
# I need mapper to calculate various quantities (said in chemistry way) from the attribute and its selected value
# happens at the time of drawing header and footer canvases
# x and y should be on canvas!
if ((not isinstance(self.canvas, BasicNomogram) or not self.canvas.onCanvas(x,y)) and
(not isinstance(self.canvas, BasicNomogramFooter) or not self.canvas.onCanvas(x,y))):
return True
if isinstance(self.canvas, BasicNomogramFooter) and self.canvas.onCanvas(x,y):
self.header.setText(self.attribute.name)
self.headerValue.setText("")
self.valName.setText("Value:")
if self.attribute.selectedValue:
self.value.setText(str(round(self.attribute.selectedValue[2],2)))
else:
self.value.setText("None")
self.supportingValName.setText("")
self.supportingValue.setText("")
points = 1
else:
# get points
selectedBeta = self.attribute.selectedValue[2]
proportionalBeta = self.canvas.mapper.propBeta(selectedBeta, self.attribute)
maxValue = self.canvas.mapper.getMaxMapperValue()
minValue = self.canvas.mapper.getMinMapperValue()
points = minValue+(maxValue-minValue)*proportionalBeta
self.header.setText(self.canvas.parent.pointsName[self.canvas.parent.yAxis]+":")
self.headerValue.setText(str(round(points,2)))
# continuous? --> get attribute value
if isinstance(self.attribute, AttrLineCont):
self.valName.setText("Value:")
if len(self.attribute.selectedValue)==4:
self.value.setText(str(round(self.attribute.selectedValue[3],2)))
else:
(nleft, nright) = getNearestAtt(selectedBeta)
if nright.betaValue>nleft.betaValue:
prop = (selectedBeta-nleft.betaValue)/(nright.betaValue-nleft.betaValue)
else:
prop = 0
if prop == 0:
avgValue = (float(nleft.name)+float(nright.name))/2.
else:
avgValue = float(nleft.name)+prop*(float(nright.name)-float(nleft.name))
self.value.setText(str(round(avgValue,2)))
self.supportingValName.setText("")
self.supportingValue.setText("")
# discrete? --> get left and right value, proportional select values
else:
(nleft, nright) = getNearestAtt(selectedBeta)
if nright.betaValue>nleft.betaValue:
prop = (selectedBeta-nleft.betaValue)/(nright.betaValue-nleft.betaValue)
else:
prop = 0
if prop == 0 or prop == 1:
self.valName.setText("Value:")
self.supportingValName.setText("")
self.supportingValue.setText("")
if prop == 0:
self.value.setText(nleft.name)
else:
self.value.setText(nright.name)
else:
self.valName.setText(nleft.name + ":")
self.supportingValName.setText(nright.name + ":")
self.value.setText(str(round(1-prop,4)*100)+"%")
self.supportingValue.setText(str(round(prop,4)*100)+"%")
# set height
height = 15+ self.valName.boundingRect().height() + self.header.boundingRect().height()
if self.supportingValName.text() != "":
height+= self.supportingValName.boundingRect().height()
# set width
width = 20+max([self.header.boundingRect().width()+2+self.headerValue.boundingRect().width(),
self.valName.boundingRect().width()+2+self.value.boundingRect().width(),
self.supportingValName.boundingRect().width()+2+self.supportingValue.boundingRect().width()])
# if bubble wants to jump of the canvas, better catch it !
selOffset = 20
xTemp, yTemp = x+selOffset, y-selOffset-height
while not self.canvas.onCanvas(xTemp,yTemp) or not self.canvas.onCanvas(xTemp,yTemp+height) or not self.canvas.onCanvas(xTemp+width,yTemp) or not self.canvas.onCanvas(xTemp+width,yTemp+height):
if yTemp == y-selOffset-height and not xTemp <= x-selOffset-width:
xTemp-=1
elif xTemp <= x-selOffset-width and not yTemp >= y+selOffset:
yTemp+=1
elif yTemp >= y+selOffset and not xTemp >= x+selOffset:
xTemp+=1
elif xTemp>= x+selOffset and not yTemp<y-selOffset-height+2:
yTemp-=1
else:
break
x,y = xTemp, yTemp
# set coordinates
self.setX(x)
self.setY(y)
self.setSize(width+2, height+2)
# header
self.header.setX(x+2)
self.header.setY(y+2)
self.headerValue.setX(x+width-4)
self.headerValue.setY(y+2)
#line
self.splitLine.setPoints(x, y+4+self.header.boundingRect().height(), x+width, y+4+self.header.boundingRect().height())
# values
self.valName.setX(x+3)
self.valName.setY(y+7+self.header.boundingRect().height())
self.value.setX(x+width-4)
self.value.setY(y+7+self.header.boundingRect().height())
self.supportingValName.setX(x+3)
self.supportingValName.setY(y+10+self.header.boundingRect().height()+self.valName.boundingRect().height())
self.supportingValue.setX(x+width-4)
self.supportingValue.setY(y+10+self.header.boundingRect().height()+self.valName.boundingRect().height())
#return false if position is at zero and alignment is centered
if round(points,3) == 0.0 and self.canvas.parent.alignType == 1:
return False
return True
def setZAll(self, z):
self.setZ(z)
self.header.setZ(z+1)
self.splitLine.setZ(z+1)
self.headerValue.setZ(z+1)
self.valName.setZ(z+1)
self.value.setZ(z+1)
self.supportingValName.setZ(z+1)
self.supportingValue.setZ(z+1)
def showAll(self):
self.show()
self.splitLine.show()
self.header.show()
self.headerValue.show()
self.valName.show()
self.value.show()
if self.supportingValName.text != "":
self.supportingValName.show()
self.supportingValue.show()
def hideAll(self):
self.hide()
self.header.hide()
self.splitLine.hide()
self.headerValue.hide()
self.valName.hide()
self.value.hide()
self.supportingValName.hide()
self.supportingValue.hide()
# Attribute value selector -- a small circle
class AttValueMarker(QCanvasEllipse):
def __init__(self, attribute, canvas, z=50):
apply(QCanvasEllipse.__init__,(self,10,10,canvas))
self.attribute = attribute
#self.canvas = canvas
self.setZ(z)
self.setBrush(QBrush(Qt.blue))
self.name=""
#self.borderCircle = QCanvasEllipse(15,15,canvas)
#self.borderCircle.setBrush(QBrush(Qt.red))
#self.borderCircle.setZ(z-1)
self.descriptor = Descriptor(canvas, attribute, z+1)
def setPos(self, x, y):
self.setX(x)
self.setY(y)
#self.borderCircle.setX(x)
#self.borderCircle.setY(y)
if not self.descriptor.drawAll(x,y):
brush = QBrush(self.brush().color(), Qt.Dense4Pattern)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -