class SMinuteTimeChart(): removeXCordList = None removePriceYCordList = None removeVolumeYCordList = None maxVolume = None crossLineIndex = None infoLayout = None def __init__(self, paramDict): self.paramDict = paramDict self.layout = self.paramDict.get("Layout") #繪圖之layout self.canvas = self.paramDict.get("Canvas") #繪圖之canvas self.shift_left = self.paramDict.get("SHIFT_LEFT") #圖形左邊位移距離 self.shift_gapheight = self.paramDict.get("SHIFT_GAPHEIGHT") #價圖及量圖的間距 self.shift_bottom = self.paramDict.get("SHIFT_BOTTOM") #圖形底部位移距離 self.shift_top = self.paramDict.get("SHIFT_TOP") #圖形上方位移距離 self.price_height_per = self.paramDict.get("PRICE_HEIGHT_PER") #價圖高度佔比 self.volume_height_per = self.paramDict.get( "VOLUME_HEIGHT_PER") #量圖高度佔比 self.CORD_INFO_COLOR = self.paramDict.get( "CORD_INFO_COLOR") #座標資訊的文字顏色 self.DATA_INFO_COLOR = self.paramDict.get("DATA_INFO_COLOR") #資訊的文字顏色 self.UP_COLOR = self.paramDict.get("UP_COLOR") #上漲時線條顏色 self.DOWN_COLOR = self.paramDict.get("DOWN_COLOR") #下跌時線條顏色 self.EQUAL_COLOR = self.paramDict.get("EQUAL_COLOR") #持平時線條顏色 self.VOLUME_COLOR = self.paramDict.get("VOLUME_COLOR") #量的線條顏色 self.FRAME_COLOR = self.paramDict.get("FRAME_COLOR") #邊框的線條顏色 self.GRID_COLOR = self.paramDict.get("GRID_COLOR") #格線的線條顏色 self.CROSS_LINE_COLOR = self.paramDict.get( "CROSS_LINE_COLOR") #十字線線條顏色 self.UP_DOWN_PER = self.paramDict.get("UP_DOWN_PER") #漲跌幅度 self.chartNum = self.paramDict.get("ChartNum") #時間總筆數 self.yesPrice = self.paramDict.get("YesPrice") #昨收價 self.startTime = self.paramDict.get("StartTime") #起始時間 self.endTime = self.paramDict.get("EndTime") #截止時間 self.infoFunc = self.paramDict.get("InfoFunc") #顯示訊息之函式 self.instGroup = self.paramDict.get("InstGroup", "") #InstructionGroup所使用之group值 self.minTimeList = [] #分時資料List self.volumeList = [] #成交量的List self.lastMinTime = None #最後一筆之時間 self.info_time = SLabel(text="") #時間 self.info_time.color = self.DATA_INFO_COLOR self.info_price = SLabel(text="") #成交價 self.info_vol = SLabel(text="") #成交量 self.info_vol.color = self.DATA_INFO_COLOR def _calcChartInfo(self): self.price_width = self.chart_size[0] - self.shift_left drawHeight = self.chart_size[ 1] - self.shift_bottom - self.shift_gapheight - self.shift_top self.price_height = drawHeight * self.price_height_per / ( self.price_height_per + self.volume_height_per) self.volume_height = drawHeight * self.volume_height_per / ( self.price_height_per + self.volume_height_per) self.xscale = 1.0 * self.price_width / self.chartNum #走勢圖x軸縮放比例 maxPrice = self.yesPrice * (1 + self.UP_DOWN_PER) #最高價 self.lowestPrice = self.yesPrice * (1 - self.UP_DOWN_PER) #最低價 self.price_yscale = 1.0 * self.price_height / ( maxPrice - self.lowestPrice) #價圖y軸縮放比例 self._calcVolumeYScale() self.start_xaxis = self.chart_start_pos[ 0] + self.shift_left #價圖及量圖之原點的x座標值 self.price_yaxis = self.chart_start_pos[ 1] + self.shift_bottom + self.volume_height + self.shift_gapheight #價圖之原點的y座標值 self.volume_yaxis = self.chart_start_pos[ 1] + self.shift_bottom #量圖之原點的y座標值 self.crossLine_height = self.chart_size[ 1] - self.shift_bottom - self.shift_top #十字線的高度 def _calcVolumeYScale(self): if self.maxVolume == None: self.volume_yscale = 1.0 * self.volume_height / 100 else: self.volume_yscale = 1.0 * self.volume_height / self.maxVolume def _drawNoDataGraph(self): """ 繪製一個沒有數據之圖形 """ self._calcChartInfo() self._drawFrameGrid() #繪製一矩形 self._drawPriceCord() #繪製價圖y軸資訊及線條 self._drawVolumeCord() #繪製量圖y軸資訊及線條 def _drawFrameGrid(self): # Start-01: 產生一繪製外框,線條及座標之物件 rectParamDict = {} rectParamDict["layout"] = self.layout rectParamDict["canvas"] = self.canvas rectParamDict["width"] = self.price_width rectParamDict["height"] = self.crossLine_height rectParamDict["x_start_pos"] = self.start_xaxis rectParamDict["y_start_pos"] = self.volume_yaxis rectParamDict["xscale"] = self.xscale rectParamDict["yscale"] = self.price_yscale rectParamDict["rectColor"] = self.FRAME_COLOR rectParamDict["rectWidth"] = 1 rectParamDict["gridColor"] = self.GRID_COLOR rectParamDict["gridWidth"] = 1 rectParamDict["cordColor"] = self.CORD_INFO_COLOR rectParamDict["instGroup"] = self.instGroup + "_SMTC_Rect" rectParamDict["shift_left"] = self.shift_left rectParamDict["drawFlagList"] = [False, False] rectParamDict["formatFlag"] = True smintimeCordUtil = SMinTimeCordUtil(rectParamDict) # End-01. smintimeCordUtil.drawRectGrid() #繪製一矩形框 xCordDict = {} infoList = [] aNum = -1 for aTime in range(int(self.startTime), int(self.endTime) + 1): aRoundNum = aTime % 100 if aRoundNum <= 59: aNum += 1 if aRoundNum == 0: infoList.append([aNum, int(aTime / 100)]) xCordDict["infoList"] = infoList xCordDict["removeLabelList"] = self.removeXCordList self.removeXCordList = smintimeCordUtil.drawXTimeCord( xCordDict) #繪製x軸資訊 def _drawPriceCord(self): """ """ rectParamDict = {} rectParamDict["layout"] = self.layout rectParamDict["canvas"] = self.canvas rectParamDict["width"] = self.price_width rectParamDict["height"] = self.price_height rectParamDict["x_start_pos"] = self.start_xaxis rectParamDict[ "y_start_pos"] = self.volume_yaxis + self.volume_height + self.shift_gapheight rectParamDict["xscale"] = self.xscale rectParamDict["yscale"] = self.price_yscale rectParamDict["rectColor"] = self.FRAME_COLOR rectParamDict["rectWidth"] = 1 rectParamDict["gridColor"] = self.GRID_COLOR rectParamDict["gridWidth"] = 1 rectParamDict["cordColor"] = self.CORD_INFO_COLOR rectParamDict["instGroup"] = "SMTC_Price" rectParamDict["shift_left"] = self.shift_left rectParamDict["drawFlagList"] = [True, False] rectParamDict["formatFlag"] = True smintimeCordUtil = SMinTimeCordUtil(rectParamDict) # 繪製y軸資訊 price_yCordDict = {} infoList = [] priceStep = 1.0 * (self.yesPrice - self.lowestPrice) / 4 for idx in range(0, 9): if idx == 4: infoList.append(self.yesPrice) else: aPrice = self.lowestPrice + priceStep * idx * 1.0 infoList.append(aPrice) price_yCordDict["infoList"] = infoList price_yCordDict["lowestValue"] = self.lowestPrice price_yCordDict["removeLabelList"] = self.removePriceYCordList self.removePriceYCordList = smintimeCordUtil.drawYGridCord( price_yCordDict) def _drawVolumeCord(self): """ """ rectParamDict = {} rectParamDict["layout"] = self.layout rectParamDict["canvas"] = self.canvas rectParamDict["width"] = self.price_width rectParamDict["height"] = self.volume_height rectParamDict["x_start_pos"] = self.start_xaxis rectParamDict["y_start_pos"] = self.volume_yaxis rectParamDict["xscale"] = self.xscale rectParamDict["yscale"] = self.volume_yscale rectParamDict["rectColor"] = self.FRAME_COLOR rectParamDict["rectWidth"] = 1 rectParamDict["gridColor"] = self.GRID_COLOR rectParamDict["gridWidth"] = 1 rectParamDict["cordColor"] = self.CORD_INFO_COLOR rectParamDict["instGroup"] = "SMTC_Volume" rectParamDict["shift_left"] = self.shift_left rectParamDict["drawFlagList"] = [False, True] rectParamDict["formatFlag"] = False smintimeCordUtil = SMinTimeCordUtil(rectParamDict) # 繪製y軸資訊 volume_yCordDict = {} infoList = [] infoList.append(0) if self.maxVolume == None: infoList.append(50) infoList.append(100) else: infoList.append(int(self.maxVolume / 2)) infoList.append(self.maxVolume) volume_yCordDict["infoList"] = infoList volume_yCordDict["lowestValue"] = 0 volume_yCordDict["removeLabelList"] = self.removeVolumeYCordList self.removeVolumeYCordList = smintimeCordUtil.drawYGridCord( volume_yCordDict) def charting(self, systemInfoList): """ 繪圖 """ #self.canvas.clear() 改由呼叫charting的那一層控制 self.chart_start_pos = systemInfoList[ 0] #走勢圖的原點座標,為一list物件,[0]為x座標,[1]為y座標 self.chart_size = systemInfoList[1] #走勢圖的size,為一list物件,[0]為寬度,[1]為高度 if self.minTimeList == None or len(self.minTimeList) == 0: self._drawNoDataGraph() return self._calcChartInfo() self._drawFrameGrid() #繪製一矩形 self._drawPriceCord() #繪製價圖y軸資訊及線條 isFirst = True for aIdx in range(0, len(self.minTimeList)): aDict = self.minTimeList[aIdx] if isFirst == True: isFirst = False preDict = {} preDict["TD"] = "0900" preDict["CloseP"] = self.yesPrice if aIdx == (len(self.minTimeList) - 1): #最後一筆資料 self._drawPriceLine(preDict, aDict, aIdx, True) self._drawVolumeLine(aDict, aIdx, True) else: #其它筆資料 self._drawPriceLine(preDict, aDict, aIdx, False) self._drawVolumeLine(aDict, aIdx, False) preDict = aDict self.lastMinTime = aDict.get("TD") if self.crossLineIndex != None: self.drawCrossLine(self.crossLineIndex) def _reChartVolume(self): self.canvas.remove_group(self.instGroup + "volume_curvData") for aIdx in range(0, len(self.minTimeList)): aDict = self.minTimeList[aIdx] self._drawVolumeLine(aDict, aIdx, False) def _getPriceColor(self, prePrice, aPrice): if aPrice > prePrice: return self.UP_COLOR elif aPrice < prePrice: return self.DOWN_COLOR else: return self.EQUAL_COLOR def _drawPriceLine(self, preDict, aDict, aIdx, isLastFlag): groupStr = self.instGroup if isLastFlag == True: groupStr += "price_lastData" else: groupStr += "price_curvData" instg = InstructionGroup(group=groupStr) color = Color() color.rgba = self._getPriceColor(preDict.get("CloseP"), aDict.get("CloseP")) instg.add(color) if aIdx == 0: x1 = self.start_xaxis else: x1 = self.start_xaxis + (aIdx - 1) * self.xscale y1 = self.price_yaxis + (preDict.get("CloseP") - self.lowestPrice) * self.price_yscale x2 = self.start_xaxis + aIdx * self.xscale y2 = self.price_yaxis + (aDict.get("CloseP") - self.lowestPrice) * self.price_yscale instg.add(Line(points=(x1, y1, x2, y2), width=1)) self.canvas.add(instg) def _drawVolumeLine(self, aDict, aIdx, isLastFlag): groupStr = self.instGroup if isLastFlag == True: groupStr += "volume_lastData" else: groupStr += "volume_curvData" instg = InstructionGroup(group=groupStr) color = Color() color.rgba = self.VOLUME_COLOR instg.add(color) x1 = self.start_xaxis + aIdx * self.xscale y1 = self.volume_yaxis x2 = x1 y2 = self.volume_yaxis + aDict.get("Vol") * self.volume_yscale instg.add(Line(points=(x1, y1, x2, y2), width=1)) self.canvas.add(instg) def addData(self, paramMinTimeList): """ 添加匯圖之資料,list中之資料為一dict,資料內容如下所示: 1.'TD': 時間 2.'OpenP': 開盤價 3.'HighP': 最高價 4.'LowP': 最低價 5.'CloseP': 收盤價 6.'OpenRefP': 開盤參考價 7.'Vol': 成交量 8.'Amt': 成交金額 9.'PriceFlag': 漲跌停符號 10.'VolCRate': 成交量換手率 11.'TtVolume': 成交總量(此欄位暫不使用) 12.'TtAmount': 成交總金額(此欄位暫不使用) """ preDict = None aDict = None if self.lastMinTime == None: #第一次添加資料時 for aDict in paramMinTimeList: aVol = aDict.get("Vol") if self.maxVolume == None: self.maxVolume = aVol else: if self.maxVolume < aVol: self.maxVolume = aVol self._calcVolumeYScale() self._drawVolumeCord() isFirst = True for aIdx in range(0, len(paramMinTimeList)): aDict = paramMinTimeList[aIdx] aVol = aDict.get("Vol") if isFirst == True: isFirst = False preDict = {} preDict["TD"] = "0900" preDict["CloseP"] = self.yesPrice self.maxVolume = aVol if aIdx == (len(paramMinTimeList) - 1): #最後一筆資料 self._drawPriceLine(preDict, aDict, aIdx, True) self._drawVolumeLine(aDict, aIdx, True) else: #其它筆資料 self._drawPriceLine(preDict, aDict, aIdx, False) self._drawVolumeLine(aDict, aIdx, False) preDict = aDict self.lastMinTime = aDict.get("TD") self.minTimeList.append(aDict) if self.maxVolume < aVol: self.maxVolume = aVol else: #非第一次添加資料 self.canvas.remove_group(self.instGroup + "price_lastData") self.canvas.remove_group(self.instGroup + "volume_lastData") isFirst = True for aIdx in range(0, len(paramMinTimeList)): aDict = paramMinTimeList[aIdx] aVol = aDict.get("Vol") if isFirst == True: isFirst = False if self.lastMinTime == aDict.get("TD"): self.minTimeList.pop(len(self.minTimeList) - 1) dataLength = len(self.minTimeList) if dataLength == 0: preDict = {} preDict["TD"] = "0900" preDict["CloseP"] = self.yesPrice else: preDict = self.minTimeList[dataLength - 1] else: dataLength = len(self.minTimeList) if dataLength == 1: preDict = {} preDict["TD"] = "0900" preDict["CloseP"] = self.yesPrice tmpDict = self.minTimeList[dataLength - 1] else: preDict = self.minTimeList[dataLength - 2] tmpDict = self.minTimeList[dataLength - 1] self._drawPriceLine( preDict, tmpDict, dataLength - 1, False) #將price_lastData轉為price_curvData self._drawVolumeLine( tmpDict, dataLength - 1, False) #將volume_lastData轉為volume_curvData if self.maxVolume < aVol: self.maxVolume = aVol self._calcVolumeYScale() self._drawVolumeCord() self._reChartVolume() dataLength = len(self.minTimeList) if aIdx == (len(paramMinTimeList) - 1): #最後一筆資料 self._drawPriceLine(preDict, aDict, dataLength, True) self._drawVolumeLine(aDict, dataLength, True) else: #其它筆資料 self._drawPriceLine(preDict, aDict, dataLength, False) self._drawVolumeLine(aDict, dataLength, False) preDict = aDict self.lastMinTime = aDict.get("TD") if self.crossLineIndex != None and self.crossLineIndex == ( len(self.minTimeList) - 1): self.crossLineIndex += 1 self.minTimeList.append(aDict) if self.crossLineIndex != None: if self.crossLineIndex == (len(self.minTimeList) - 1): self.drawCrossLine(len(self.minTimeList) - 1) def _showInfo(self, index): if self.infoLayout == None: self.infoLayout = SInfoLayout(cols=1) self.infoLayout.row_force_default = True self.infoLayout.row_default_height = 20 self.infoLayout.col_force_default = True self.infoLayout.col_default_width = 90 self.infoLayout.size = [90, 64] self.infoLayout.padding = [2, 1, 2, 1] self.infoLayout.size_hint = (None, None) else: self.infoLayout.clear_widgets() if self.infoLayout.parent == None: self.layout.add_widget(self.infoLayout) x1 = self.start_xaxis + index * self.xscale halfNum = int(self.chartNum / 2) if index > halfNum: pos_x = x1 - self.infoLayout.width else: pos_x = x1 + 5 pos_y = self.volume_yaxis + self.crossLine_height - self.infoLayout.height self.infoLayout.pos = (pos_x, pos_y) dataDict = self.minTimeList[index] self.infoLayout.add_widget(self.info_time) self.info_time.text = "時:" + sutil.formatTime(dataDict.get("TD")) price = dataDict.get("CloseP") self.infoLayout.add_widget(self.info_price) self.info_price.text = "價:" + ("{:7.2f}".format(price)).strip() self.info_price.color = self._getPriceColor(self.yesPrice, price) self.infoLayout.add_widget(self.info_vol) self.info_vol.text = "量:" + ("{:13.0f}".format( dataDict.get("Vol"))).strip() def drawCrossLine(self, aIndex): """ """ dataNum = len(self.minTimeList) if dataNum == 0: return if aIndex >= dataNum: index = dataNum - 1 elif aIndex < 0: index = 0 else: index = aIndex self.crossLineIndex = index if self.infoFunc == None: self._showInfo(index) else: self.infoFunc(self.minTimeList[index]) groupStr = self.instGroup + "cross_line" self.canvas.remove_group(groupStr) instg = InstructionGroup(group=groupStr) color = Color() color.rgba = self.CROSS_LINE_COLOR instg.add(color) x1 = self.start_xaxis + index * self.xscale y1 = self.volume_yaxis x2 = self.start_xaxis + index * self.xscale y2 = self.volume_yaxis + self.crossLine_height instg.add(Line(points=(x1, y1, x2, y2), width=1)) self.canvas.add(instg)
class SCurvGraph(SBaseTech): def __init__(self, paramDict): super().__init__(paramDict) self.CORD_INFO_COLOR = paramDict.get("CORD_INFO_COLOR") #座標資訊的文字顏色 self.DATA_INFO_COLOR = paramDict.get("DATA_INFO_COLOR") #資訊的文字顏色 self.CURV_COLOR = paramDict.get("CURV_COLOR") #線條顏色 self.CROSS_LINE_COLOR = paramDict.get("CROSS_LINE_COLOR") #十字線顏色 self.techType = paramDict.get("TechType") #技術分析類型 self.isPriceMA = paramDict.get("IsPriceMA") #是否為價格MA self.info_time = SLabel(text="") #時間 self.info_time.color = self.DATA_INFO_COLOR self.info_data = SLabel(text="") #訊息的值 self.info_data.color = self.DATA_INFO_COLOR def _calcDrawInfo(self): """ 計算繪圖所需之資訊 """ if self.extremeValue[0] > 0: self.highestValue = self.extremeValue[0] * 1.01 elif self.extremeValue[0] < 0: self.highestValue = self.extremeValue[0] * 0.99 else: self.highestValue = self.extremeValue[0] if self.extremeValue[1] > 0: self.lowestValue = self.extremeValue[1] * 0.99 elif self.extremeValue[0] < 0: self.lowestValue = self.extremeValue[1] * 1.01 else: self.lowestValue = self.extremeValue[1] diffValue = self.highestValue - self.lowestValue if diffValue != 0: self.yscale = 1.0 * self.chartSize[1] / diffValue #線圖y軸縮放比例 else: self.yscale = 1 if self.dispNum <= len(self.dataDict): self.dispMax = self.dispNum else: self.dispMax = len(self.dataDict) def getExtremeValue(self): """ 取得在某一頁次之最大值及最小值,回傳一list物件,格式如下所示: [最大值,最小值] """ if len(self.keyList) == 0: return [0, 0] maxValue = sys.maxsize * -1 - 1 minValue = sys.maxsize try: for aIdx in range(self.scopeIdx[0], self.scopeIdx[1] + 1): aKey = self.keyList[aIdx] aValue = self.dataDict.get(aKey) if aValue > maxValue: maxValue = aValue if aValue < minValue: minValue = aValue except: pass return [maxValue, minValue] def charting(self): """ 繪圖 """ super().charting() self.canvas.remove_group(self.instGroup + "_lastData") self.canvas.remove_group(self.instGroup + "_curvData") if self.keyList == None or len(self.keyList) == 0: #無資料時,不作任何動作 return isFirst = True dispIdx = -1 for aIdx in range(self.scopeIdx[0], self.scopeIdx[1] + 1): dispIdx += 1 aKey = self.keyList[aIdx] aValue = self.dataDict.get(aKey) if isFirst == True: isFirst = False if aIdx == 0: preValue = (self.highestValue - self.lowestValue) / 2 + self.lowestValue else: tmpKey = self.keyList[aIdx - 1] preValue = self.dataDict.get(tmpKey) if dispIdx != 0: if self.isPriceMA == True: if preValue != 0: self._drawLine(preValue, aValue, dispIdx, False) else: self._drawLine(preValue, aValue, dispIdx, False) preValue = aValue self.lastKey = aKey def _drawLine(self, preValue, aValue, dispIdx, isLastFlag): """ 繪製曲線 """ groupStr = self.instGroup if isLastFlag == True: groupStr += "_lastData" else: groupStr += "_curvData" instg = InstructionGroup(group=groupStr) color = Color() color.rgba = self.CURV_COLOR instg.add(color) #(self.tickWide + self.tickGap)代表顯示一筆資料,在x軸方向所需的總點數 x1 = self.chartPos[0] + dispIdx * (self.tickWide + self.tickGap) - self.backGap y1 = self.chartPos[1] + (preValue - self.lowestValue) * self.yscale x2 = x1 + (self.tickWide + self.tickGap) y2 = self.chartPos[1] + (aValue - self.lowestValue) * self.yscale instg.add(Line(points=(x1, y1, x2, y2), width=1)) self.canvas.add(instg) def addData(self, paramList): """ 添加匯圖之資料,paramList為一list物件,list中為一dict物件,格式如下所示: {'TD':'時間','Value':'值'} """ for aDict in paramList: aKey = aDict.get("TD") self.dataDict[aKey] = aDict.get("Value") self.keyList = sorted(list(self.dataDict.keys())) def _showInfo(self, index): if self.infoLayout == None: self.infoLayout = SInfoLayout(cols=1) self.infoLayout.row_force_default = True self.infoLayout.row_default_height = 20 self.infoLayout.col_force_default = True self.infoLayout.padding = [2, 1, 2, 1] self.infoLayout.size_hint = (None, None) else: self.infoLayout.clear_widgets() if self.infoLayout.parent == None: self.layout.add_widget(self.infoLayout) aKey = self.keyList[self.scopeIdx[0] + index] aValue = self.dataDict.get(aKey) self.infoLayout.add_widget(self.info_time) self.info_time.text = sutil.formatDateTime(aKey) self.infoLayout.add_widget(self.info_data) valueStr = None if self.formatType == 1: valueStr = ("{:7.2f}".format(aValue)).strip() elif self.formatType == 2: valueStr = "${:,}".format(aValue) else: valueStr = str(aValue) self.info_data.text = valueStr maxLength = len(self.info_time.text) if maxLength < len(self.info_data.text): maxLength = len(self.info_data.text) layoutWidth = schartutil.getInfoLayoutWidth(maxLength) self.infoLayout.col_default_width = layoutWidth self.infoLayout.size = [layoutWidth, 44] x1 = self.chartPos[0] + (index + 1) * (self.tickWide + self.tickGap) - self.backGap halfNum = int(self.dispNum / 2) if index > halfNum: pos_x = x1 - self.infoLayout.width else: pos_x = x1 + 5 pos_y = self.chartPos[1] + self.chartSize[1] - self.infoLayout.height self.infoLayout.pos = (pos_x, pos_y) def drawCrossLine(self, aIndex): """ 繪製十字線 """ dataNum = len(self.dataDict) if dataNum == 0: return if aIndex >= self.dispMax: index = self.dispMax - 1 elif aIndex >= self.dispNum: index = self.dispNum - 1 elif aIndex < 0: index = 0 else: index = aIndex self.crossLineIndex = index if self.infoFunc == None: self._showInfo(index) else: aKey = self.keyList[self.scopeIdx[0] + index] aValue = self.dataDict.get(aKey) self.infoFunc({"TechType":self.techType, "TD":aKey, "Value":aValue}) if self.isDrawCrossLine == False: return groupStr = self.instGroup + "cross_line" self.canvas.remove_group(groupStr) color = Color() color.rgba = self.CROSS_LINE_COLOR instg = InstructionGroup(group=groupStr) instg.add(color) x1 = self.chartPos[0] + (index + 1) * (self.tickWide + self.tickGap) - self.backGap y1 = self.chartPos[1] x2 = x1 y2 = self.chartPos[1] + self.chartSize[1] instg.add(Line(points=(x1, y1, x2, y2), width=1)) self.canvas.add(instg) aKey = self.keyList[self.scopeIdx[0] + index] aValue = self.dataDict.get(aKey) instg = InstructionGroup(group=groupStr) instg.add(color) x1 = self.chartPos[0] y1 = self.chartPos[1] + (aValue - self.lowestValue) * self.yscale x2 = self.chartPos[0] + self.chartSize[0] y2 = y1 instg.add(Line(points=(x1, y1, x2, y2), width=1)) self.canvas.add(instg)
class SKLineGraph(SBaseTech): def __init__(self, paramDict): super().__init__(paramDict) self.CORD_INFO_COLOR = paramDict.get("CORD_INFO_COLOR") #座標資訊的文字顏色 self.DATA_INFO_COLOR = paramDict.get("DATA_INFO_COLOR") #資訊的文字顏色 self.KLINE_COLOR = paramDict.get("KLINE_COLOR") #線條顏色 self.SOLID_COLOR = paramDict.get("SOLID_COLOR") #K線中陽線的顏色 self.VIRTUAL_COLOR = paramDict.get("VIRTUAL_COLOR") #K線中陰線的顏色 self.CROSS_LINE_COLOR = paramDict.get("CROSS_LINE_COLOR") #十字線顏色 self.info_time = SLabel(text="") #時間 self.info_time.color = self.DATA_INFO_COLOR self.info_openPrice = SLabel(text="") #開盤價 self.info_openPrice.color = self.DATA_INFO_COLOR self.info_highPrice = SLabel(text="") #最高價 self.info_highPrice.color = self.DATA_INFO_COLOR self.info_lowPrice = SLabel(text="") #最低價 self.info_lowPrice.color = self.DATA_INFO_COLOR self.info_closePrice = SLabel(text="") #收盤價 self.info_closePrice.color = self.DATA_INFO_COLOR def _calcDrawInfo(self): """ 計算繪圖所需之資訊 """ self.highestValue = self.extremeValue[0] * 1.01 #畫面顯示之最大值 self.lowestValue = self.extremeValue[1] * .99 #畫面顯示之最小值 diffValue = self.highestValue - self.lowestValue if diffValue != 0: self.yscale = 1.0 * self.chartSize[1] / diffValue #K線圖y軸縮放比例 else: self.yscale = 1 if self.dispNum <= len(self.dataDict): self.dispMax = self.dispNum else: self.dispMax = len(self.dataDict) def getExtremeValue(self): """ 取得當前頁次之最大值及最小值,回傳一list物件,格式如下所示: [最大值,最小值] """ if len(self.keyList) == 0: return [0, 0] maxValue = sys.maxsize * -1 - 1 minValue = sys.maxsize try: for aIdx in range(self.scopeIdx[0], self.scopeIdx[1] + 1): aKey = self.keyList[aIdx] aDict = self.dataDict.get(aKey) if aDict.get("HP") > maxValue: maxValue = aDict.get("HP") if aDict.get("LP") < minValue: minValue = aDict.get("LP") except: pass return [maxValue, minValue] def charting(self): """ 繪圖 """ super().charting() self.canvas.remove_group(self.instGroup + "_lastData") self.canvas.remove_group(self.instGroup + "_curvData") if self.keyList == None or len(self.keyList) == 0: #無資料時,不作任何動作 return dispIdx = -1 for aIdx in range(self.scopeIdx[0], self.scopeIdx[1] + 1): dispIdx += 1 aKey = self.keyList[aIdx] aDict = self.dataDict.get(aKey) self._drawLine(aDict, dispIdx, False) self.lastKey = aKey def _drawLine(self, aDict, dispIdx, isLastFlag): """ """ groupStr = self.instGroup if isLastFlag == True: groupStr += "_lastData" else: groupStr += "_curvData" instg = InstructionGroup(group=groupStr) color = Color() color.rgba = self.KLINE_COLOR instg.add(color) #(self.tickWide + self.tickGap)代表顯示一筆資料,在x軸方向所需的總點數 x1 = self.chartPos[0] + (dispIdx + 1) * (self.tickWide + self.tickGap) - self.backGap y1 = self.chartPos[1] + (aDict.get("HP") - self.lowestValue) * self.yscale x2 = x1 y2 = self.chartPos[1] + (aDict.get("LP") - self.lowestValue) * self.yscale instg.add(Line(points=(x1, y1, x2, y2), width=1)) self.canvas.add(instg) instg = InstructionGroup(group=groupStr) color = Color() openPrice = aDict.get("OP") closePrice = aDict.get("CP") if closePrice > openPrice: color.rgba = self.SOLID_COLOR instg.add(color) x1 = self.chartPos[0] + dispIdx * (self.tickWide + self.tickGap) + self.tickGap y1 = self.chartPos[1] + (openPrice - self.lowestValue) * self.yscale instg.add( Rectangle(pos=(x1, y1), size=(self.tickWide, (closePrice - openPrice) * self.yscale))) elif closePrice < openPrice: color.rgba = self.VIRTUAL_COLOR instg.add(color) x1 = self.chartPos[0] + dispIdx * (self.tickWide + self.tickGap) + self.tickGap y1 = self.chartPos[1] + (closePrice - self.lowestValue) * self.yscale instg.add( Rectangle(pos=(x1, y1), size=(self.tickWide, (openPrice - closePrice) * self.yscale))) else: color.rgba = self.KLINE_COLOR instg.add(color) x1 = self.chartPos[0] + dispIdx * (self.tickWide + self.tickGap) + self.tickGap y1 = self.chartPos[1] + (closePrice - self.lowestValue) * self.yscale x2 = self.chartPos[0] + (dispIdx + 1) * (self.tickWide + self.tickGap) y2 = y1 instg.add(Line(points=(x1, y1, x2, y2), width=1)) self.canvas.add(instg) def addData(self, paramList): """ 添加匯圖之資料,paramList為一list物件,list中為一dict物件,格式如下所示: {'TD':'時間','OP':'開盤價','HP':'最高價','LP':'最低價','CP':'收盤價','RP':'開盤參考價', 'VOL':'成交量','AMT':'成交金額','UDF':'漲停跌符號','RT':'換手率'} """ for aDict in paramList: aKey = aDict.get("TD") self.dataDict[aKey] = aDict self.keyList = sorted(list(self.dataDict.keys())) def clearData(self): super().clearData() self.canvas.remove_group(self.instGroup + "_lastData") self.canvas.remove_group(self.instGroup + "_curvData") def _showInfo(self, index): if self.infoLayout == None: self.infoLayout = SInfoLayout(cols=1) self.infoLayout.row_force_default = True self.infoLayout.row_default_height = 20 self.infoLayout.col_force_default = True self.infoLayout.padding = [2, 1, 2, 1] self.infoLayout.size_hint = (None, None) else: self.infoLayout.clear_widgets() if self.infoLayout.parent == None: self.layout.add_widget(self.infoLayout) aKey = self.keyList[self.scopeIdx[0] + index] aDict = self.dataDict.get(aKey) self.infoLayout.add_widget(self.info_time) self.info_time.text = sutil.formatDateTime(aKey) self.infoLayout.add_widget(self.info_openPrice) openPrice = aDict.get("OP") valueStr = None if self.formatType == 1: valueStr = ("{:7.2f}".format(openPrice)).strip() elif self.formatType == 2: valueStr = "${:,}".format(openPrice) else: valueStr = str(openPrice) self.info_openPrice.text = "開:" + valueStr self.infoLayout.add_widget(self.info_highPrice) highPrice = aDict.get("HP") valueStr = None if self.formatType == 1: valueStr = ("{:7.2f}".format(highPrice)).strip() elif self.formatType == 2: valueStr = "${:,}".format(highPrice) else: valueStr = str(highPrice) self.info_highPrice.text = "高:" + valueStr self.infoLayout.add_widget(self.info_lowPrice) lowPrice = aDict.get("LP") valueStr = None if self.formatType == 1: valueStr = ("{:7.2f}".format(lowPrice)).strip() elif self.formatType == 2: valueStr = "${:,}".format(lowPrice) else: valueStr = str(lowPrice) self.info_lowPrice.text = "低:" + valueStr self.infoLayout.add_widget(self.info_closePrice) closePrice = aDict.get("CP") valueStr = None if self.formatType == 1: valueStr = ("{:7.2f}".format(closePrice)).strip() elif self.formatType == 2: valueStr = "${:,}".format(closePrice) else: valueStr = str(closePrice) self.info_closePrice.text = "收:" + valueStr maxLength = len(self.info_time.text) if maxLength < schartutil.calcCharNum(self.info_openPrice.text): maxLength = schartutil.calcCharNum(self.info_openPrice.text) if maxLength < schartutil.calcCharNum(self.info_highPrice.text): maxLength = schartutil.calcCharNum(self.info_highPrice.text) if maxLength < schartutil.calcCharNum(self.info_lowPrice.text): maxLength = schartutil.calcCharNum(self.info_lowPrice.text) if maxLength < schartutil.calcCharNum(self.info_closePrice.text): maxLength = schartutil.calcCharNum(self.info_closePrice.text) layoutWidth = schartutil.getInfoLayoutWidth(maxLength) self.infoLayout.col_default_width = layoutWidth self.infoLayout.size = [layoutWidth, 104] x1 = self.chartPos[0] + (index + 1) * (self.tickWide + self.tickGap) - self.backGap halfNum = int(self.dispNum / 2) if index > halfNum: pos_x = x1 - self.infoLayout.width else: pos_x = x1 + 5 pos_y = self.chartPos[1] + self.chartSize[1] - self.infoLayout.height self.infoLayout.pos = (pos_x, pos_y) def drawCrossLine(self, aIndex): """ """ dataNum = len(self.dataDict) if dataNum == 0: return if aIndex >= self.dispMax: index = self.dispMax - 1 elif aIndex >= self.dispNum: index = self.dispNum - 1 elif aIndex < 0: index = 0 else: index = aIndex self.crossLineIndex = index if self.infoFunc == None: self._showInfo(index) else: aKey = self.keyList[self.scopeIdx[0] + index] aDict = self.dataDict.get(aKey) refDict = {} for aKey in aDict.keys(): refDict[aKey] = aDict.get(aKey) preIndex = self.scopeIdx[0] + index - 1 if preIndex < 0: refDict["UD"] = 0 else: aKey = self.keyList[preIndex] preDict = self.dataDict.get(aKey) refDict["UD"] = aDict.get("CP") - preDict.get("CP") self.infoFunc(refDict) if self.isDrawCrossLine == False: return groupStr = self.instGroup + "cross_line" self.canvas.remove_group(groupStr) color = Color() color.rgba = self.CROSS_LINE_COLOR instg = InstructionGroup(group=groupStr) instg.add(color) x1 = self.chartPos[0] + (index + 1) * (self.tickWide + self.tickGap) - self.backGap y1 = self.chartPos[1] x2 = x1 y2 = self.chartPos[1] + self.chartSize[1] instg.add(Line(points=(x1, y1, x2, y2), width=1)) self.canvas.add(instg) aKey = self.keyList[self.scopeIdx[0] + index] aDict = self.dataDict.get(aKey) instg = InstructionGroup(group=groupStr) instg.add(color) x1 = self.chartPos[0] y1 = self.chartPos[1] + (aDict.get("CP") - self.lowestValue) * self.yscale x2 = self.chartPos[0] + self.chartSize[0] y2 = y1 instg.add(Line(points=(x1, y1, x2, y2), width=1)) self.canvas.add(instg)
class SVolBarGraph(SBaseTech): def __init__(self, paramDict): super().__init__(paramDict) self.CORD_INFO_COLOR = paramDict.get("CORD_INFO_COLOR") #座標資訊的文字顏色 self.DATA_INFO_COLOR = paramDict.get("DATA_INFO_COLOR") #資訊的文字顏色 self.UP_COLOR = paramDict.get("UP_COLOR") #上漲時條狀圖的顏色 self.DOWN_COLOR = paramDict.get("DOWN_COLOR") #下跌時條狀圖的顏色 self.EQUAL_COLOR = paramDict.get("EQUAL_COLOR") #持平時條狀圖的顏色 self.CROSS_LINE_COLOR = paramDict.get("CROSS_LINE_COLOR") #十字線顏色 self.techType = paramDict.get("TechType") #技術分析類型 self.info_time = SLabel(text="") #時間 self.info_time.color = self.DATA_INFO_COLOR self.info_vol = SLabel(text="") #成交量 self.info_vol.color = self.DATA_INFO_COLOR def _calcDrawInfo(self): self.highestValue = self.extremeValue[0] * 1.01 #畫面顯示之最大值 self.lowestValue = 0 #畫面顯示之最小值 diffValue = self.highestValue - self.lowestValue if diffValue != 0: self.yscale = 1.0 * self.chartSize[1] / ( self.highestValue - self.lowestValue) #線圖y軸縮放比例 else: self.yscale = 1 if self.dispNum <= len(self.dataDict): self.dispMax = self.dispNum else: self.dispMax = len(self.dataDict) def getExtremeValue(self): """ 取得當前頁次之最大值及最小值,回傳一list物件,格式如下所示: [最大值,最小值] """ if len(self.keyList) == 0: return [0, 0] maxValue = sys.maxsize * -1 - 1 minValue = 0 try: for aIdx in range(self.scopeIdx[0], self.scopeIdx[1] + 1): aKey = self.keyList[aIdx] aDict = self.dataDict.get(aKey) if aDict.get("VOL") > maxValue: maxValue = aDict.get("VOL") except: pass return [maxValue, minValue] def charting(self): """ 繪圖 """ super().charting() self.canvas.remove_group(self.instGroup + "_lastData") self.canvas.remove_group(self.instGroup + "_curvData") if self.keyList == None or len(self.keyList) == 0: #無資料時,不作任何動作 return isFirst = True dispIdx = -1 for aIdx in range(self.scopeIdx[0], self.scopeIdx[1] + 1): dispIdx += 1 aKey = self.keyList[aIdx] aDict = self.dataDict.get(aKey) if isFirst == True: isFirst = False if aIdx == 0: preClosePrice = 0 else: tmpKey = self.keyList[aIdx - 1] preClosePrice = self.dataDict.get(tmpKey).get("CP") self._drawLine(preClosePrice, aDict, dispIdx, False) preClosePrice = aDict.get("CP") self.lastKey = aKey def _drawLine(self, preClose, aDict, dispIdx, isLastFlag): """ """ groupStr = self.instGroup if isLastFlag == True: groupStr += "_lastData" else: groupStr += "_curvData" instg = InstructionGroup(group=groupStr) color = Color() volume = aDict.get("VOL") closePrice = aDict.get("CP") if closePrice > preClose: color.rgba = self.UP_COLOR instg.add(color) x1 = self.chartPos[0] + dispIdx * (self.tickWide + self.tickGap) + self.tickGap y1 = self.chartPos[1] instg.add( Rectangle(pos=(x1, y1), size=(self.tickWide, (volume - self.lowestValue) * self.yscale))) elif closePrice < preClose: color.rgba = self.DOWN_COLOR instg.add(color) x1 = self.chartPos[0] + dispIdx * (self.tickWide + self.tickGap) + self.tickGap y1 = self.chartPos[1] instg.add( Rectangle(pos=(x1, y1), size=(self.tickWide, (volume - self.lowestValue) * self.yscale))) else: color.rgba = self.EQUAL_COLOR instg.add(color) x1 = self.chartPos[0] + dispIdx * (self.tickWide + self.tickGap) + self.tickGap y1 = self.chartPos[1] instg.add( Rectangle(pos=(x1, y1), size=(self.tickWide, (volume - self.lowestValue) * self.yscale))) self.canvas.add(instg) def addData(self, paramList): """ 添加匯圖之資料,paramList為一list物件,list中為一dict物件,格式如下所示: {'TD':'時間','CP':'收盤價','VOL':'成交量'} """ for aDict in paramList: aKey = aDict.get("TD") self.dataDict[aKey] = aDict self.keyList = sorted(list(self.dataDict.keys())) def _showInfo(self, index): if self.infoLayout == None: self.infoLayout = SInfoLayout(cols=1) self.infoLayout.row_force_default = True self.infoLayout.row_default_height = 20 self.infoLayout.col_force_default = True self.infoLayout.padding = [2, 1, 2, 1] self.infoLayout.size_hint = (None, None) else: self.infoLayout.clear_widgets() if self.infoLayout.parent == None: self.layout.add_widget(self.infoLayout) aKey = self.keyList[self.scopeIdx[0] + index] aDict = self.dataDict.get(aKey) self.infoLayout.add_widget(self.info_time) self.info_time.text = sutil.formatDateTime(aKey) self.infoLayout.add_widget(self.info_vol) volume = aDict.get("VOL") valueStr = None if self.formatType == 1: valueStr = ("{:7.2f}".format(volume)).strip() elif self.formatType == 2: valueStr = "${:,}".format(volume) else: valueStr = str(volume) self.info_vol.text = valueStr maxLength = len(self.info_time.text) if maxLength < schartutil.calcCharNum(self.info_vol.text): maxLength = schartutil.calcCharNum(self.info_vol.text) layoutWidth = schartutil.getInfoLayoutWidth(maxLength) self.infoLayout.col_default_width = layoutWidth self.infoLayout.size = [layoutWidth, 44] x1 = self.chartPos[0] + (index + 1) * (self.tickWide + self.tickGap) - self.backGap halfNum = int(self.dispNum / 2) if index > halfNum: pos_x = x1 - self.infoLayout.width else: pos_x = x1 + 5 pos_y = self.chartPos[1] + self.chartSize[1] - self.infoLayout.height self.infoLayout.pos = (pos_x, pos_y) def drawCrossLine(self, aIndex): """ """ dataNum = len(self.dataDict) if dataNum == 0: return if aIndex >= self.dispMax: index = self.dispMax - 1 elif aIndex >= self.dispNum: index = self.dispNum - 1 elif aIndex < 0: index = 0 else: index = aIndex self.crossLineIndex = index if self.infoFunc == None: self._showInfo(index) else: aKey = self.keyList[self.scopeIdx[0] + index] aDict = self.dataDict.get(aKey) refDict = {} for aKey in aDict.keys(): refDict[aKey] = aDict.get(aKey) refDict["TechType"] = self.techType self.infoFunc(refDict) if self.isDrawCrossLine == False: return groupStr = self.instGroup + "cross_line" self.canvas.remove_group(groupStr) color = Color() color.rgba = self.CROSS_LINE_COLOR instg = InstructionGroup(group=groupStr) instg.add(color) x1 = self.chartPos[0] + (index + 1) * (self.tickWide + self.tickGap) - self.backGap y1 = self.chartPos[1] x2 = x1 y2 = self.chartPos[1] + self.chartSize[1] instg.add(Line(points=(x1, y1, x2, y2), width=1)) self.canvas.add(instg) aKey = self.keyList[self.scopeIdx[0] + index] aDict = self.dataDict.get(aKey) instg = InstructionGroup(group=groupStr) instg.add(color) x1 = self.chartPos[0] y1 = self.chartPos[1] + (aDict.get("VOL") - self.lowestValue) * self.yscale x2 = self.chartPos[0] + self.chartSize[0] y2 = y1 instg.add(Line(points=(x1, y1, x2, y2), width=1)) self.canvas.add(instg)
class STradeGraph(FloatLayout): isFocus = True infoLayout = None profitPerNumDict = {} xcordDict = {} def __init__(self, paramDict, **kwargs): super(STradeGraph, self).__init__(**kwargs) self.paramDict = paramDict self.btmenu = self.paramDict.get(CONSTS.S_BTMENU) self.dataList = self.paramDict.get("dataList") # 1001-Start: 計算最大及最小獲利率 self.min_profitPer = 0 self.max_profitPer = 0 tmpProfitPer = None if self.dataList != None and len(self.dataList) != 0: for profitPer in self.dataList: if profitPer < 0: tmpProfitPer = math.floor(profitPer) else: tmpProfitPer = math.ceil(profitPer) if tmpProfitPer > self.max_profitPer: self.max_profitPer = tmpProfitPer if tmpProfitPer < self.min_profitPer: self.min_profitPer = tmpProfitPer self.min_profitPer = int(self.min_profitPer) self.max_profitPer = int(self.max_profitPer) # 1001-End. self.maxNum = 5 self.yscale = 1 tradeGraphDict = sutil.getDictFromFile(os.path.join(os.path.dirname(__file__), ".." + os.sep + "conf" + os.sep + "trade_graph.ini")) self.shift_left = int(tradeGraphDict.get("SHIFT_LEFT")) self.shift_right = int(tradeGraphDict.get("SHIFT_RIGHT")) self.shift_bottom = int(tradeGraphDict.get("SHIFT_BOTTOM")) self.shift_top = int(tradeGraphDict.get("SHIFT_TOP")) self.ycordWidth = int(tradeGraphDict.get("YCORD_WIDTH")) self.pillarPoint = int(tradeGraphDict.get("PILLAR_POINT")) self.pillarGapPoint = int(tradeGraphDict.get("PILLAR_GAP_POINT")) self.pillarSumPoint = self.pillarPoint + self.pillarGapPoint self.FRAME_COLOR = colorHex(tradeGraphDict.get("FRAME_COLOR")) self.GRID_COLOR = colorHex(tradeGraphDict.get("GRID_COLOR")) self.CORD_INFO_COLOR = colorHex(tradeGraphDict.get("CORD_INFO_COLOR")) self.CROSS_LINE_COLOR = colorHex(tradeGraphDict.get("CROSS_LINE_COLOR")) self.TRADE_INFO_FGCOLOR = colorHex(tradeGraphDict.get("TRADE_INFO_FGCOLOR")) self.info_tradeNum = SLabel(text="") #交易次數 self.info_tradeNum.color = self.TRADE_INFO_FGCOLOR self.info_profitPer = SLabel(text="") #獲利率 self.info_profitPer.color = self.TRADE_INFO_FGCOLOR self.bind(pos=self.charting) self.bind(size=self.charting) Window.bind(mouse_pos=self.mousePos) def calcMaxTick(self): """ 依據容器的寬度及傳入之數據,計算圖形可畫出之最大及最小的tick值 """ all_tickNum = (int)((self.width - self.ycordWidth * 2 - self.shift_left - self.shift_right) / self.pillarSumPoint) half_tickNum = (int)((all_tickNum - 1) / 2) all_tickNum = half_tickNum * 2 self.min_tickNum = half_tickNum * -1 self.max_tickNum = half_tickNum if self.min_profitPer < self.min_tickNum and self.max_profitPer < self.max_tickNum: diff = self.max_profitPer - self.min_profitPer if diff >= all_tickNum: self.max_tickNum = self.max_profitPer self.min_tickNum = (half_tickNum * 2 - self.max_tickNum) * -1 else: shift_num = self.min_tickNum - self.min_profitPer self.max_tickNum = self.max_tickNum - shift_num self.min_tickNum = self.min_tickNum - shift_num elif self.min_profitPer > self.min_tickNum and self.max_profitPer > self.max_tickNum: diff = self.max_profitPer - self.min_profitPer if diff >= all_tickNum: self.min_tickNum = self.min_profitPer self.max_tickNum = half_tickNum * 2 + self.min_profitPer else: shift_num = self.max_profitPer - self.max_tickNum self.max_tickNum = self.max_tickNum + shift_num self.min_tickNum = self.min_tickNum + shift_num self.max_tickNum = int(self.max_tickNum) self.min_tickNum = int(self.min_tickNum) def drawNoDataGraph(self): """ 繪製一個沒有數據之圖形 """ self.calcMaxTick() self.drawCordFrame() self.yscale = (self.height - self.shift_bottom - self.shift_top) / self.maxNum self.drawCordInfo() def charting(self, *args): """ 繪圖 """ self.canvas.clear() if self.dataList == None or len(self.dataList) == 0: self.drawNoDataGraph() return self.calcMaxTick() self.drawCordFrame() self.profitPerNumDict.clear() self.maxNum = 5 tmpProfitPer = None aNum = None for profitPer in self.dataList: if profitPer < 0: tmpProfitPer = math.floor(profitPer) if tmpProfitPer < self.min_tickNum: tmpProfitPer = self.min_tickNum else: tmpProfitPer = math.ceil(profitPer) if tmpProfitPer > self.max_tickNum: tmpProfitPer = self.max_tickNum tmpProfitPer = int(tmpProfitPer) aNum = self.profitPerNumDict.get(str(tmpProfitPer)) if aNum == None: aNum = 0 aNum += 1 self.profitPerNumDict[str(tmpProfitPer)] = aNum if aNum > self.maxNum: self.maxNum = aNum self.yscale = (self.height - self.shift_bottom - self.shift_top) / self.maxNum self.drawCordInfo() index = None shift_xpos = None center_pos = int(self.pillarPoint / 2) for i in range(self.min_tickNum, self.max_tickNum + 1): if i == 0: shift_xpos = self.ycordWidth elif i > 0: shift_xpos = self.ycordWidth * 2 else: shift_xpos = 0 index = i - self.min_tickNum aNum = self.profitPerNumDict.get(str(i)) if aNum != None: lineColor = Color() lineColor.rgba = self.GRID_COLOR instg = InstructionGroup(group="data") instg.add(lineColor) x1 = int(self.pos[0] + self.shift_left + index * self.pillarSumPoint + shift_xpos) y1 = int(self.pos[1] + self.shift_bottom) x2 = int(self.pos[0] + self.shift_left + index * self.pillarSumPoint + shift_xpos) y2 = int(self.pos[1] + self.shift_bottom + aNum * self.yscale) instg.add(Line(points=(x1, y1, x2, y2), width=1)) self.canvas.add(instg) instg = InstructionGroup(group="data") instg.add(lineColor) x1 = int(self.pos[0] + self.shift_left + index * self.pillarSumPoint + shift_xpos) y1 = int(self.pos[1] + self.shift_bottom + aNum * self.yscale) x2 = int(self.pos[0] + self.shift_left + index * self.pillarSumPoint + self.pillarPoint - 1 + shift_xpos) y2 = int(self.pos[1] + self.shift_bottom + aNum * self.yscale) instg.add(Line(points=(x1, y1, x2, y2), width=1)) self.canvas.add(instg) instg = InstructionGroup(group="data") instg.add(lineColor) x1 = int(self.pos[0] + self.shift_left + index * self.pillarSumPoint + self.pillarPoint - 1 + shift_xpos) y1 = int(self.pos[1] + self.shift_bottom) x2 = int(self.pos[0] + self.shift_left + index * self.pillarSumPoint + self.pillarPoint - 1 + shift_xpos) y2 = int(self.pos[1] + self.shift_bottom + aNum * self.yscale) instg.add(Line(points=(x1, y1, x2, y2), width=1)) self.canvas.add(instg) x1 = int(self.pos[0] + self.shift_left + index * self.pillarSumPoint + shift_xpos + center_pos) self.xcordDict[str(i)] = x1 def drawCordFrame(self): # 畫一條橫線 instg = InstructionGroup(group="frame") frameColor = Color() frameColor.rgba = self.FRAME_COLOR instg.add(frameColor) x1 = int(self.pos[0] + self.shift_left) y1 = int(self.pos[1] + self.shift_bottom) x2 = int(self.pos[0] + self.width - self.shift_right) y2 = int(self.pos[1] + self.shift_bottom) instg.add(Line(points=(x1, y1, x2, y2), width=1)) self.canvas.add(instg) # 畫一條豎線 instg = InstructionGroup(group="frame") instg.add(frameColor) center_pos = int(self.pillarPoint / 2) self.center_xpos = int(self.pos[0] + self.shift_left + (self.min_tickNum * -1) * self.pillarSumPoint + self.ycordWidth + center_pos) x1 = self.center_xpos y1 = int(self.pos[1] + self.shift_bottom) x2 = self.center_xpos y2 = int(self.pos[1] + self.height - self.shift_top) instg.add(Line(points=(x1, y1, x2, y2), width=1)) self.canvas.add(instg) def drawCordInfo(self): # 100-Start: 標示y軸資訊 initStepUp = 1 if self.maxNum < 8: initStepUp = 1 elif self.maxNum < 10: initStepUp = 2 else: initStepUp = int(self.maxNum / 5) x0 = self.pos[0] + ((self.width - self.shift_left - self.shift_right) / 2) + self.shift_left - 30 y0 = self.pos[1] + self.height - self.shift_top + 5 alabel = SCordLabel(pos=(x0,y0),size_hint=(None,None)) alabel.width = 60 alabel.height = 20 alabel.text = "(X軸:獲利率,Y軸:交易次數)" alabel.color = self.CORD_INFO_COLOR self.add_widget(alabel) frameColor = Color() frameColor.rgba = self.FRAME_COLOR stepUp = initStepUp while(stepUp <= self.maxNum): instg = InstructionGroup(group="frame") instg.add(frameColor) x1 = self.center_xpos - 5 y1 = int(self.pos[1] + self.shift_bottom + stepUp * self.yscale) x2 = self.center_xpos y2 = int(self.pos[1] + self.shift_bottom + stepUp * self.yscale) instg.add(Line(points=(x1, y1, x2, y2), width=1)) self.canvas.add(instg) x0 = self.center_xpos - self.ycordWidth y0 = int(self.pos[1] + self.shift_bottom + stepUp * self.yscale) - 10 alabel = SCordLabel(pos=(x0,y0),size_hint=(None,None)) alabel.width = 36 alabel.height = 20 alabel.text = str(stepUp) alabel.color = self.CORD_INFO_COLOR self.add_widget(alabel) stepUp += initStepUp # 100-End. # 200-Start: 標示x軸資訊 center_pos = int(self.pillarPoint / 2) remainder = None for i in range(self.min_tickNum, self.max_tickNum + 1): remainder = i % 10 if remainder != 0 and i != -1 and i != 0 and i != 1 and i != self.min_tickNum and i != self.max_tickNum: continue index = i - self.min_tickNum if i >= 0: tmp = self.max_tickNum - i if tmp <= 8 and i != self.max_tickNum: continue if i == 0: shift_xpos = self.ycordWidth else: shift_xpos = self.ycordWidth * 2 else: if index <= 8 and i != self.min_tickNum: continue shift_xpos = 0 instg = InstructionGroup(group="frame") instg.add(frameColor) x1 = int(self.pos[0] + self.shift_left + index * self.pillarSumPoint + center_pos + shift_xpos) y1 = int(self.pos[1] + self.shift_bottom) x2 = int(self.pos[0] + self.shift_left + index * self.pillarSumPoint + center_pos + shift_xpos) y2 = int(self.pos[1] + self.shift_bottom - 5) instg.add(Line(points=(x1, y1, x2, y2), width=1)) self.canvas.add(instg) x0 = int(self.pos[0] + self.shift_left + index * self.pillarSumPoint + center_pos + shift_xpos - 18) y0 = int(self.pos[1] + self.shift_bottom) - 25 alabel = SCordLabel(pos=(x0,y0),size_hint=(None,None)) alabel.width = 36 alabel.height = 20 if i == self.min_tickNum: alabel.text = "<=" + str(i) + "%" elif i == self.max_tickNum: alabel.text = ">=" + str(i) + "%" else: alabel.text = str(i) + "%" alabel.color = self.CORD_INFO_COLOR self.add_widget(alabel) # 200-End. def drawCrossLine(self, key): if len(self.profitPerNumDict) == 0: return aNum = self.profitPerNumDict.get(key) if aNum == None: return keyInt = int(key) index = keyInt - self.min_tickNum if keyInt == 0: shift_xpos = self.ycordWidth elif keyInt > 0: shift_xpos = self.ycordWidth * 2 else: shift_xpos = 0 color = Color() color.rgba = self.CROSS_LINE_COLOR self.canvas.remove_group("cross_line") center_pos = int(self.pillarPoint / 2) instg = InstructionGroup(group="cross_line") instg.add(color) x1 = int(self.pos[0] + self.shift_left + index * self.pillarSumPoint + shift_xpos + center_pos) y1 = int(self.pos[1] + self.shift_bottom) x2 = int(self.pos[0] + self.shift_left + index * self.pillarSumPoint + shift_xpos + center_pos) y2 = int(self.pos[1] + self.shift_bottom + self.maxNum * self.yscale) instg.add(Line(points=(x1, y1, x2, y2), width=1)) self.canvas.add(instg) instg = InstructionGroup(group="cross_line") instg.add(color) x1 = self.pos[0] + self.shift_left y1 = int(self.pos[1] + self.shift_bottom + aNum * self.yscale) x2 = self.pos[0] + self.width - self.shift_right y2 = int(self.pos[1] + self.shift_bottom + aNum * self.yscale) instg.add(Line(points=(x1, y1, x2, y2), width=1)) self.canvas.add(instg) def clearCrossLineAndInfo(self): self.canvas.remove_group("cross_line") if self.infoLayout != None: if self.infoLayout.parent != None: self.remove_widget(self.infoLayout) def mousePos(self, *args): if len(args) >= 2: key = None xcord = None if len(self.profitPerNumDict) != 0 and len(self.xcordDict) != 0: for xkey in self.xcordDict.keys(): xcord = self.xcordDict.get(xkey) if args[1][0] < (xcord + 0.5) and args[1][0] < (xcord - 0.5): key = int(xkey) break if key == None: self.clearCrossLineAndInfo() return aNum = self.profitPerNumDict.get(str(key)) if aNum == None: self.clearCrossLineAndInfo() return self.drawCrossLine(str(key)) if self.infoLayout == None: self.infoLayout = SInfoLayout(cols=1) self.infoLayout.row_force_default = True self.infoLayout.row_default_height = 20 self.infoLayout.col_force_default = True self.infoLayout.col_default_width = 110 self.infoLayout.padding = [2, 1, 2, 1] self.infoLayout.size_hint = (None, None) else: self.infoLayout.clear_widgets() if self.infoLayout.parent == None: self.add_widget(self.infoLayout) index = key - self.min_tickNum center_pos = int(self.pillarPoint / 2) if key == 0: shift_xpos = self.ycordWidth elif key > 0: shift_xpos = self.ycordWidth * 2 else: shift_xpos = 0 x1 = int(self.pos[0] + self.shift_left + index * self.pillarSumPoint + shift_xpos + center_pos) if (self.width - x1) < (self.infoLayout.width + self.shift_right): pos_x = x1 - self.infoLayout.width else: pos_x = x1 + 5 y1 = int(self.pos[1] + self.shift_bottom + aNum * self.yscale) if (self.height - y1) < (self.infoLayout.height + self.shift_top): pos_y = y1 - self.infoLayout.height else: pos_y = y1 + 5 self.infoLayout.pos = (pos_x, pos_y) self.info_tradeNum = SLabel(text="") #交易次數 self.info_tradeNum.color = self.TRADE_INFO_FGCOLOR self.info_profitPer = SLabel(text="") #獲利率 self.info_profitPer.color = self.TRADE_INFO_FGCOLOR rowNum = 1 self.infoLayout.add_widget(self.info_profitPer) self.info_profitPer.text = "獲利率:" + str(key) + "%" rowNum += 1 self.infoLayout.add_widget(self.info_tradeNum) self.info_tradeNum.text = "交易次數:" + str(aNum) self.infoLayout.size = (110, rowNum * 20)
class STrendGraph(FloatLayout): isFocus = True infoLayout = None rectGridList = None def __init__(self, paramDict, **kwargs): super(STrendGraph, self).__init__(**kwargs) with self.canvas: Color(0, 20 / 255, 45 / 255) Rectangle(pos=self.pos, size=self.size) self.paramDict = paramDict self.btmenu = self.paramDict.get(CONSTS.S_BTMENU) self.tickNum = self.paramDict.get("ticknum") self.dataList = self.paramDict.get("datalist") if self.tickNum == 600: self.xgrid_num = 100 elif self.tickNum == 500: self.xgrid_num = 90 elif self.tickNum == 400: self.xgrid_num = 70 elif self.tickNum == 300: self.xgrid_num = 50 elif self.tickNum == 200: self.xgrid_num = 40 else: self.xgrid_num = 30 self.crossLineXIndex = -1 self.crossLineYIndex = -1 trendGraphDict = sutil.getDictFromFile( os.path.join(os.path.dirname(__file__), ".." + os.sep + "conf" + os.sep + "trend_graph.ini")) self.shift_left = int(trendGraphDict.get("SHIFT_LEFT")) self.shift_right = int(trendGraphDict.get("SHIFT_RIGHT")) self.shift_bottom = int(trendGraphDict.get("SHIFT_BOTTOM")) self.shift_top = int(trendGraphDict.get("SHIFT_TOP")) self.CORD_INFO_COLOR = colorHex(trendGraphDict.get("CORD_INFO_COLOR")) self.UP_COLOR = colorHex(trendGraphDict.get("UP_COLOR")) self.DOWN_COLOR = colorHex(trendGraphDict.get("DOWN_COLOR")) self.EQUAL_COLOR = colorHex(trendGraphDict.get("EQUAL_COLOR")) self.VOLUME_COLOR = colorHex(trendGraphDict.get("VOLUME_COLOR")) self.FRAME_COLOR = colorHex(trendGraphDict.get("FRAME_COLOR")) self.GRID_COLOR = colorHex(trendGraphDict.get("GRID_COLOR")) self.CROSS_LINE_COLOR = colorHex( trendGraphDict.get("CROSS_LINE_COLOR")) self.TRADEB_SIG_COLOR = colorHex( trendGraphDict.get("TRADEB_SIG_COLOR")) self.TRADES_SIG_COLOR = colorHex( trendGraphDict.get("TRADES_SIG_COLOR")) self.TRADE_INFO_FGCOLOR = colorHex( trendGraphDict.get("TRADE_INFO_FGCOLOR")) self.info_date = SLabel(text="") #日期 self.info_date.color = self.TRADE_INFO_FGCOLOR self.info_time = SLabel(text="") #時間 self.info_time.color = self.TRADE_INFO_FGCOLOR self.info_price = SLabel(text="") #成交價 self.info_price.color = self.TRADE_INFO_FGCOLOR self.info_vol = SLabel(text="") #成交量 self.info_vol.color = self.TRADE_INFO_FGCOLOR self.info_amt = SLabel(text="") #金額 self.info_amt.color = self.TRADE_INFO_FGCOLOR self.info_bs = SLabel(text="") #買賣訊號及買(賣)價 self.info_bs.color = self.TRADE_INFO_FGCOLOR self.calcMaxTick() self.bind(pos=self.charting) self.bind(size=self.charting) Window.bind(mouse_pos=self.mousePos) def calcMaxTick(self): self.max_tickNum = 0 if self.dataList == None: self.max_tickNum = self.tickNum elif self.tickNum > len(self.dataList): self.max_tickNum = self.tickNum else: self.max_tickNum = len(self.dataList) def charting(self, *args): self.canvas.clear() if self.dataList == None or len(self.dataList) == 0: return self.max_price_s = -1 self.min_price_s = sys.maxsize self.max_volume = 0 for data in self.dataList: price = data.get("price") if price > self.max_price_s: self.max_price_s = price if price < self.min_price_s: self.min_price_s = price vol = data.get("vol") if vol > self.max_volume: self.max_volume = vol diffMaxMin = (self.max_price_s - self.min_price_s) / 100.0 self.max_price = self.max_price_s + diffMaxMin * 6 self.min_price = self.min_price_s - diffMaxMin * 14 self.xscale = 1.0 * (self.width - self.shift_left - self.shift_right) / self.max_tickNum self.yscale = 1.0 * (self.height - self.shift_bottom - self.shift_top ) / (self.max_price - self.min_price) self.vscale = self.yscale * (self.min_price_s - self.min_price) * .8 / self.max_volume self.drawFrameGrid() curIndex = preIndex = -1 prePrice = 0 x1 = 0 y1 = 0 x2 = 0 y2 = 0 for data in self.dataList: curIndex += 1 if curIndex == 0: self.drawXCoordinateInfo(0) preIndex = curIndex prePrice = data.get("price") self.drawVolumeGraph(data, 0) self.addBSPoint(0) continue instg = InstructionGroup(group="data") curPrice = data.get("price") color = self.getLineColor(prePrice, curPrice) instg.add(color) x1 = int(self.pos[0] + self.shift_left + preIndex * self.xscale) y1 = int(self.pos[1] + self.shift_bottom + (prePrice - self.min_price) * self.yscale) x2 = int(self.pos[0] + self.shift_left + curIndex * self.xscale) y2 = int(self.pos[1] + self.shift_bottom + (curPrice - self.min_price) * self.yscale) instg.add(Line(points=(x1, y1, x2, y2), width=1)) self.canvas.add(instg) self.drawVolumeGraph(data, curIndex) if (curIndex % self.xgrid_num) == 0: self.drawXCoordinateInfo(curIndex) self.addBSPoint(curIndex) preIndex = curIndex prePrice = curPrice self.drawCrossLine(curIndex) def drawVolumeGraph(self, data, curIndex): instg = InstructionGroup(group="data") volume = data.get("vol") color = Color() color.rgba = self.VOLUME_COLOR instg.add(color) x1 = int(self.pos[0] + self.shift_left + curIndex * self.xscale) y1 = int(self.pos[1] + self.shift_bottom) x2 = int(self.pos[0] + self.shift_left + curIndex * self.xscale) y2 = int(self.pos[1] + self.shift_bottom + volume * self.vscale) instg.add(Line(points=(x1, y1, x2, y2), width=1)) self.canvas.add(instg) def addBSPoint(self, index): data = self.dataList[index] bstype = data.get("bs") if bstype != "": price = data.get("price") x1 = int(self.pos[0] + self.shift_left + index * self.xscale) - 5 alabel = Label(text="", size_hint=(None, None), size=(5, 10), markup=True) if bstype == "B": y1 = int(self.pos[1] + self.shift_bottom + (price - self.min_price) * self.yscale) - 20 alabel.pos = (x1, y1) alabel.color = self.TRADEB_SIG_COLOR alabel.text = "[b]" + "B" + "[/b]" elif bstype == "S": y1 = int(self.pos[1] + self.shift_bottom + (price - self.min_price) * self.yscale) + 10 alabel.pos = (x1, y1) alabel.color = self.TRADES_SIG_COLOR alabel.text = "[b]" + "S" + "[/b]" self.add_widget(alabel) def drawXCoordinateInfo(self, index): x0 = int(self.pos[0] + self.shift_left + index * self.xscale - 30) y0 = int(self.pos[1] + self.shift_bottom - 20) dateStr = self.dataList[index].get("date") alabel = SCordLabel(pos=(x0, y0), size_hint=(None, None)) alabel.width = 60 alabel.height = 20 alabel.text = sutil.formatDate(dateStr) alabel.color = self.CORD_INFO_COLOR self.add_widget(alabel) timeStr = self.dataList[index].get("time") if timeStr != "0": x0 = int(self.pos[0] + self.shift_left + index * self.xscale - 30) y0 = int(self.pos[1] + self.shift_bottom - 40) alabel = SCordLabel(pos=(x0, y0), size_hint=(None, None)) alabel.width = 60 alabel.height = 20 alabel.text = sutil.formatTime(timeStr) alabel.color = self.CORD_INFO_COLOR self.add_widget(alabel) def drawFrameGrid(self): # Start-01: 產生一繪製外框,線條及座標之物件 rectParamDict = {} rectParamDict["canvas"] = self.canvas rectParamDict[ "width"] = self.width - self.shift_left - self.shift_right rectParamDict[ "height"] = self.height - self.shift_bottom - self.shift_top rectParamDict["x_start_pos"] = self.pos[0] + self.shift_left rectParamDict["y_start_pos"] = self.pos[1] + self.shift_bottom rectParamDict["rectColor"] = self.FRAME_COLOR rectParamDict["rectWidth"] = 1 rectParamDict["gridColor"] = self.GRID_COLOR rectParamDict["gridWidth"] = 1 rectParamDict["instGroup"] = "StrendGraph" smintimeChartUtil = SMinTimeCordUtil(rectParamDict) # End-01. smintimeChartUtil.drawRectGrid() #繪製一矩形框 gridColor = Color() gridColor.rgba = self.GRID_COLOR tmpNum = self.xgrid_num while (tmpNum < self.max_tickNum): instg = InstructionGroup(group="grid") x1 = int(self.pos[0] + self.shift_left + tmpNum * self.xscale) y1 = self.pos[1] + self.shift_bottom x2 = int(self.pos[0] + self.shift_left + tmpNum * self.xscale) y2 = self.pos[1] + self.height - self.shift_top instg.add(gridColor) instg.add(Line(points=(x1, y1, x2, y2), width=1)) self.canvas.add(instg) tmpNum += self.xgrid_num pscale = (self.max_price - self.min_price) / 4.0 for i in range(0, 5): y1 = int(self.pos[1] + self.shift_bottom + (i * pscale * self.yscale)) y2 = int(self.pos[1] + self.shift_bottom + (i * pscale * self.yscale)) if i != 0 and i != 4: instg = InstructionGroup(group="grid") x1 = self.pos[0] + self.shift_left x2 = self.pos[0] + self.width - self.shift_right instg.add(gridColor) instg.add(Line(points=(x1, y1, x2, y2), width=1)) self.canvas.add(instg) cordPrice = i * pscale + self.min_price cordPriceStr = "{:7.2f}".format(cordPrice) x0 = self.pos[0] + 2 #y0 = y1 - 8 y0 = y1 alabel = SCordLabel(text=cordPriceStr, pos=(x0, y0), size_hint=(None, None)) alabel.width = self.shift_left - 2 alabel.height = 16 alabel.halign = "right" alabel.color = self.CORD_INFO_COLOR self.add_widget(alabel) smintimeChartUtil = None def getLineColor(self, price1, price2): color = Color() if price1 > price2: color.rgba = self.DOWN_COLOR elif price1 < price2: color.rgba = self.UP_COLOR else: color.rgba = self.EQUAL_COLOR return color def drawCrossLine(self, index): if self.dataList == None or index >= len(self.dataList): return data = self.dataList[index] price = data.get("price") color = Color() color.rgba = self.CROSS_LINE_COLOR self.canvas.remove_group("cross_line") instg = InstructionGroup(group="cross_line") instg.add(color) x1 = int(self.pos[0] + self.shift_left + index * self.xscale) y1 = self.pos[1] + self.shift_bottom x2 = int(self.pos[0] + self.shift_left + index * self.xscale) y2 = self.pos[1] + self.height - self.shift_top instg.add(Line(points=(x1, y1, x2, y2), width=1)) self.canvas.add(instg) instg = InstructionGroup(group="cross_line") instg.add(color) x1 = self.pos[0] + self.shift_left y1 = int(self.pos[1] + self.shift_bottom + (price - self.min_price) * self.yscale) x2 = self.pos[0] + self.width - self.shift_right y2 = int(self.pos[1] + self.shift_bottom + (price - self.min_price) * self.yscale) instg.add(Line(points=(x1, y1, x2, y2), width=1)) self.canvas.add(instg) def mousePos(self, *args): if len(args) >= 2: if self.dataList != None and len(self.dataList) != 0: index = int( (args[1][0] - self.pos[0] - self.shift_left) / self.xscale) if index < len(self.dataList) and index >= 0: data = self.dataList[index] dateStr = str(data.get("date")) timeStr = str(data.get("time")) price = data.get("price") self.drawCrossLine(index) if self.infoLayout == None: self.infoLayout = SInfoLayout(cols=1) self.infoLayout.row_force_default = True self.infoLayout.row_default_height = 20 self.infoLayout.col_force_default = True self.infoLayout.col_default_width = 110 self.infoLayout.padding = [2, 1, 2, 1] self.infoLayout.size_hint = (None, None) else: self.infoLayout.clear_widgets() if self.infoLayout.parent == None: self.add_widget(self.infoLayout) x1 = int(self.pos[0] + self.shift_left + index * self.xscale) if (self.width - x1) < (self.infoLayout.width + self.shift_right): pos_x = x1 - self.infoLayout.width else: pos_x = x1 + 5 y1 = int(self.pos[1] + self.shift_bottom + (price - self.min_price) * self.yscale) if (self.height - y1) < (self.infoLayout.height + self.shift_top): pos_y = y1 - self.infoLayout.height else: pos_y = y1 + 5 self.infoLayout.pos = (pos_x, pos_y) rowNum = 1 self.infoLayout.add_widget(self.info_date) self.info_date.text = sutil.formatDate(dateStr) if timeStr != "" and timeStr != "0": rowNum += 1 self.infoLayout.add_widget(self.info_time) self.info_time.text = sutil.formatTime(timeStr) rowNum += 1 self.infoLayout.add_widget(self.info_price) self.info_price.text = "價:" + ( "{:7.2f}".format(price)).strip() rowNum += 1 self.infoLayout.add_widget(self.info_vol) self.info_vol.text = "量:" + ("{:13.0f}".format( data.get("vol"))).strip() rowNum += 1 self.infoLayout.add_widget(self.info_amt) self.info_amt.text = "金:" + ("{:13.0f}".format( data.get("amt"))).strip() bs = data.get("bs") bsprice = data.get("bsprice") if bs != "": rowNum += 1 self.infoLayout.add_widget(self.info_bs) self.info_bs.text = bs + ":" + ( "{:7.2f}".format(bsprice)).strip() self.infoLayout.size = (110, rowNum * 20) else: self.canvas.remove_group("cross_line") if self.infoLayout != None: if self.infoLayout.parent != None: self.remove_widget(self.infoLayout)