From 044130bcea2533e2a242e7bd5281bcd1c4cc4b11 Mon Sep 17 00:00:00 2001 From: Eugenio Parodi Date: Thu, 4 Mar 2021 00:57:17 +0000 Subject: [PATCH] multiple values in the graph widget, visibility is checked among the parents --- TermTk/TTkCore/canvas.py | 67 +++++++++++++++++++----------- TermTk/TTkCore/constant.py | 5 +++ TermTk/TTkCore/helper.py | 2 + TermTk/TTkGui/theme.py | 46 +++++++++++++++------ TermTk/TTkWidgets/graph.py | 28 ++++++++----- TermTk/TTkWidgets/widget.py | 5 ++- tests/showcase/graph.py | 82 ++++++++++++++++++++++--------------- tests/utf-8/test.braille.py | 52 +++++++++++++++++++++++ 8 files changed, 208 insertions(+), 79 deletions(-) mode change 100644 => 100755 tests/showcase/graph.py create mode 100755 tests/utf-8/test.braille.py diff --git a/TermTk/TTkCore/canvas.py b/TermTk/TTkCore/canvas.py index 813049b1..845b92ba 100644 --- a/TermTk/TTkCore/canvas.py +++ b/TermTk/TTkCore/canvas.py @@ -343,14 +343,8 @@ class TTkCanvas: def drawHChart(self, pos, values, zoom=1.0, color=TTkColor.RST): x,y=pos v1,v2 = values - t1,t2,b1,b2=0,0,0,0 - gu=TTkCfg.theme.graph_up - gd=TTkCfg.theme.graph_down - - if v1>0: t1 = v1*zoom - else: b1 = v1*zoom - if v2>0: t2 = v2*zoom - else: b2 = v2*zoom + gb=TTkCfg.theme.braille + ''' loop 0 1 2 3 = range(0,1+maxt//4) v1 13 |---|---|---|--| @@ -359,21 +353,48 @@ class TTkCanvas: out 4,4 4,4 4,2 3,0 0,0 o1 = 4 if v1-4 > i*4 else v1-i*4 ''' - # Draw Top Chart - maxt = max(t1,t2) - for i in range(0,int(maxt//4)+1): - o1 = 4 if t1-4 > i*4 else max(0,int(t1-i*4)) - o2 = 4 if t2-4 > i*4 else max(0,int(t2-i*4)) - #TTkLog.debug(f"{v1,v2},{(t1//4),(t2//4)}, {(t1%4),(t2%4)}, {o1,o2}") - self._set(y-i,x, gu[o1][o2], color) - # Draw Bottom Chart - minb = min(b1,b2) - for i in range(int(minb//4),0): - o1 = 4 if -b1-4 > -i*4 else max(0,int(-b1+i*4)) - o2 = 4 if -b2-4 > -i*4 else max(0,int(-b2+i*4)) - ##TTkLog.debug(f"{v1,v2},{(t1//(t2//4)}, {(t1%4),(t2%4)}, {o1,o2}") - self._set(y-i,x, gd[o1][o2], color) - + # TTkLog.debug(f"{(v1,v2)} z{zoom}") + zl1 = [ int(i*zoom) for i in v1 ] + zl2 = [ int(i*zoom) for i in v2 ] + maxz = max(max(zl1),max(zl2),0) + minz = min(min(zl1),min(zl2),0) + filled = True + for i in range(int(minz//4),int(maxz//4)+2): + ts1 = i*4 + ts2 = i*4+4 + ''' + Braille bits: + o2 o1 = 4 bits each + + 1 5 Braille dots + 2 6 + 3 7 + 4 8 + + TTkTheme.braille[( o1<<4 | o2 )] = Braille UTF-8 char + ''' + braille = 0x00 + for ii in range(len(zl1)): + z1 = zl1[ii] + z2 = zl2[ii] + o1,o2 = 0,0 + #TTkLog.debug + if not filled or ii>0: + if ts1 <= z1 < ts2: o1 = 0x80>>max(0,int(z1-ts1)) + if ts1 <= z2 < ts2: o2 = 0x08>>max(0,int(z2-ts1)) + else: + if (0<=ts1ts1>z1): o1 = 0xf0 + if (0<=ts1ts1>z2): o2 = 0x0f + k1 = 0x0f80 if z1>=0 else 0x00f0 + k2 = 0x00f8 if z2>=0 else 0x000f + if ts1 <= z1 < ts2: o1 = 0xf0&(k1>>max(0,int(z1-ts1))) + if ts1 <= z2 < ts2: o2 = 0x0f&(k2>>max(0,int(z2-ts1))) + braille ^= (o1|o2) + # braille &= 0xff + #TTkLog.debug(f"z:{zl1,zl2}, ts:{ts1,ts2},{o1,o2}") + #if braille<0 or braille>0xff: + # TTkLog.debug(f"z:{zl1,zl2},t:{t1,t2},i:{i} {t1-i*4} {t2-i*4} o:{o1,o2}, {hex(braille)}") + self._set(y-i-1,x, gb[braille], color) def execPaint(self, winw, winh): diff --git a/TermTk/TTkCore/constant.py b/TermTk/TTkCore/constant.py index ffae4a09..48377e7b 100644 --- a/TermTk/TTkCore/constant.py +++ b/TermTk/TTkCore/constant.py @@ -41,6 +41,11 @@ class TTkConstant: BOTTOM = 0x0002 LEFT = 0x0004 RIGHT = 0x0008 + CENTER = 0x0010 + + # Graph types + FILLED = 0x0001 + LINE = 0x0002 # Mouse Events MOUSE_EVENT = 0x01 diff --git a/TermTk/TTkCore/helper.py b/TermTk/TTkCore/helper.py index e8ad7f2b..b8a3cacd 100644 --- a/TermTk/TTkCore/helper.py +++ b/TermTk/TTkCore/helper.py @@ -46,6 +46,7 @@ class TTkHelper: @staticmethod def addUpdateWidget(widget): + if not widget.isVisible(): return if widget not in TTkHelper._updateWidget: TTkHelper._updateWidget.append(widget) @@ -104,6 +105,7 @@ class TTkHelper: # TTkLog.debug(f"{len(TTkHelper._updateBuffer)} {len(TTkHelper._updateWidget)}") for widget in TTkHelper._updateWidget: + if not widget.isVisible(): continue parent = widget.parentWidget() while parent is not None: if parent not in updateBuffers: diff --git a/TermTk/TTkGui/theme.py b/TermTk/TTkGui/theme.py index 49a1ea2c..00c372a3 100644 --- a/TermTk/TTkGui/theme.py +++ b/TermTk/TTkGui/theme.py @@ -24,6 +24,7 @@ from TermTk.TTkCore.color import TTkColor + class TTkTheme(): ''' from: https://en.wikipedia.org/wiki/Box-drawing_character ┌─┬┐ ╔═╦╗ ╓─╥╖ ╒═╤╕ @@ -149,19 +150,38 @@ class TTkTheme(): ⢸⠉⠉⠉⢸⢹⠁ ⠃⠹ ⠁ ⠈⡿⠁ ⠈⠸⠉⣿⡏ ⠈⢹⠁⠏⣿⠏⠃⢻⠉ ⠸⠈⠁ ⠈⠈ ⠇ ⠋⠇ ⠈ ⠘ ⠈ ''' - graph_up = ( - (' ','⢀','⢠','⢰','⢸'), - ('⡀','⣀','⣠','⣰','⣸'), - ('⡄','⣄','⣤','⣴','⣼'), - ('⡆','⣆','⣦','⣶','⣾'), - ('⡇','⣇','⣧','⣷','⣿')) - graph_down=( - (' ','⠈','⠘','⠸','⢸'), - ('⠁','⠉','⠙','⠹','⢹'), - ('⠃','⠋','⠛','⠻','⢻'), - ('⠇','⠏','⠟','⠿','⢿'), - ('⡇','⡏','⡟','⡿','⣿')) - + # graph_up = ( + # (' ','⢀','⢠','⢰','⢸'), + # ('⡀','⣀','⣠','⣰','⣸'), + # ('⡄','⣄','⣤','⣴','⣼'), + # ('⡆','⣆','⣦','⣶','⣾'), + # ('⡇','⣇','⣧','⣷','⣿')) + # graph_down=( + # (' ','⠈','⠘','⠸','⢸'), + # ('⠁','⠉','⠙','⠹','⢹'), + # ('⠃','⠋','⠛','⠻','⢻'), + # ('⠇','⠏','⠟','⠿','⢿'), + # ('⡇','⡏','⡟','⡿','⣿')) + + # Generated by: + # tests/utf-8/test.braille.py + braille=( + '⠀','⠈','⠐','⠘','⠠','⠨','⠰','⠸','⢀','⢈','⢐','⢘','⢠','⢨','⢰','⢸', + '⠁','⠉','⠑','⠙','⠡','⠩','⠱','⠹','⢁','⢉','⢑','⢙','⢡','⢩','⢱','⢹', + '⠂','⠊','⠒','⠚','⠢','⠪','⠲','⠺','⢂','⢊','⢒','⢚','⢢','⢪','⢲','⢺', + '⠃','⠋','⠓','⠛','⠣','⠫','⠳','⠻','⢃','⢋','⢓','⢛','⢣','⢫','⢳','⢻', + '⠄','⠌','⠔','⠜','⠤','⠬','⠴','⠼','⢄','⢌','⢔','⢜','⢤','⢬','⢴','⢼', + '⠅','⠍','⠕','⠝','⠥','⠭','⠵','⠽','⢅','⢍','⢕','⢝','⢥','⢭','⢵','⢽', + '⠆','⠎','⠖','⠞','⠦','⠮','⠶','⠾','⢆','⢎','⢖','⢞','⢦','⢮','⢶','⢾', + '⠇','⠏','⠗','⠟','⠧','⠯','⠷','⠿','⢇','⢏','⢗','⢟','⢧','⢯','⢷','⢿', + '⡀','⡈','⡐','⡘','⡠','⡨','⡰','⡸','⣀','⣈','⣐','⣘','⣠','⣨','⣰','⣸', + '⡁','⡉','⡑','⡙','⡡','⡩','⡱','⡹','⣁','⣉','⣑','⣙','⣡','⣩','⣱','⣹', + '⡂','⡊','⡒','⡚','⡢','⡪','⡲','⡺','⣂','⣊','⣒','⣚','⣢','⣪','⣲','⣺', + '⡃','⡋','⡓','⡛','⡣','⡫','⡳','⡻','⣃','⣋','⣓','⣛','⣣','⣫','⣳','⣻', + '⡄','⡌','⡔','⡜','⡤','⡬','⡴','⡼','⣄','⣌','⣔','⣜','⣤','⣬','⣴','⣼', + '⡅','⡍','⡕','⡝','⡥','⡭','⡵','⡽','⣅','⣍','⣕','⣝','⣥','⣭','⣵','⣽', + '⡆','⡎','⡖','⡞','⡦','⡮','⡶','⡾','⣆','⣎','⣖','⣞','⣦','⣮','⣶','⣾', + '⡇','⡏','⡗','⡟','⡧','⡯','⡷','⡿','⣇','⣏','⣗','⣟','⣧','⣯','⣷','⣿') frameBorderColor = TTkColor.RST frameTitleColor = TTkColor.fg("#dddddd")+TTkColor.bg("#222222") diff --git a/TermTk/TTkWidgets/graph.py b/TermTk/TTkWidgets/graph.py index 31295651..296922a8 100644 --- a/TermTk/TTkWidgets/graph.py +++ b/TermTk/TTkWidgets/graph.py @@ -34,29 +34,37 @@ from TermTk.TTkWidgets.widget import TTkWidget from TermTk.TTkTemplates.color import TColor class TTkGraph(TTkWidget, TColor): - __slots__ = ('_data', '_maxData', '_offset', '_direction') + __slots__ = ('_data', '_maxData', '_offset', '_direction', '_align') def __init__(self, *args, **kwargs): - self._data = [] + self._data = [[0]] self._offset = 0 TTkWidget.__init__(self, *args, **kwargs) TColor.__init__(self, *args, **kwargs) self._name = kwargs.get('name' , 'TTkGraph' ) self._maxData = kwargs.get('maxData', 0x1000) self._direction = kwargs.get('direction', TTkK.RIGHT) + self._align = kwargs.get('align', TTkK.CENTER) - def addValue(self, value): - self._data.append(value) + def addValue(self, values): + self._data.append(values) self.update() def paintEvent(self): if not self._data: return - x=0 - y = self.height()//2 w,h = self.size() - v1,v2 = 0,0 + x=0 + if self._align == TTkK.CENTER: + y = h//2 + elif self._align == TTkK.TOP: + y = 0 + else: + y = h + v1,v2 = [0],[0] i=0 data = self._data[-w*2:] - zoom = 2*h/max(max(data),-min(data)) + # TTkLog.debug(data) + mv = max(max(map(max,data)),-min(map(min,data))) + zoom = 2*h/mv if mv>0 else 1.0 for i in range(len(data)): v2 = v1 v1 = data[i] @@ -67,6 +75,6 @@ class TTkGraph(TTkWidget, TColor): self._canvas.drawHChart(pos=(w-(x+i//2),y),values=(v1,v2), zoom=zoom, color=self.color.modParam(val=-y)) if i%2==1: if self._direction == TTkK.RIGHT: - self._canvas.drawHChart(pos=(x+i//2+1,y),values=(v1,0), zoom=zoom, color=self.color.modParam(val=-y)) + self._canvas.drawHChart(pos=(x+i//2+1,y),values=(v1,v1), zoom=zoom, color=self.color.modParam(val=-y)) else: - self._canvas.drawHChart(pos=(w-(x+i//2+1),y),values=(0,v1), zoom=zoom, color=self.color.modParam(val=-y)) + self._canvas.drawHChart(pos=(w-(x+i//2+1),y),values=(v1,v1), zoom=zoom, color=self.color.modParam(val=-y)) diff --git a/TermTk/TTkWidgets/widget.py b/TermTk/TTkWidgets/widget.py index f813ca85..b3c64c2b 100644 --- a/TermTk/TTkWidgets/widget.py +++ b/TermTk/TTkWidgets/widget.py @@ -420,7 +420,10 @@ class TTkWidget: def close(self): pass def isVisible(self): - return self._visible + if self._parent is None: + return self._visible + else: + return self._visible & self._parent.isVisible() # Event to be sent # TODO: Remove This diff --git a/tests/showcase/graph.py b/tests/showcase/graph.py old mode 100644 new mode 100755 index 933c7882..62b39b5e --- a/tests/showcase/graph.py +++ b/tests/showcase/graph.py @@ -27,48 +27,66 @@ import sys, os, argparse, math, random sys.path.append(os.path.join(sys.path[0],'../..')) import TermTk as ttk + +class graphTimerEvent(): + def __init__(self, w, type, delay): + self.timer = ttk.TTkTimer() + self.val = 10 + self.delay = delay + self.switch = False + self.w = w + self.type = type + self.timer.timeout.connect(self.timerEvent) + self.timer.start(1) + @ttk.pyTTkSlot() + def timerEvent(self): + self.switch = not self.switch + if self.type == 1: # Triple sin + offset1 = 15 + offset2 = 20 + val = [ math.sin( self.val *math.pi/40)*4*10 , + math.sin((self.val+offset1)*math.pi/40)*4*7, + math.sin((self.val+offset2)*math.pi/30)*4*5,] + if self.type == 2: # Double sin alternated + offset = 15 + if self.switch: val = [math.sin( self.val *math.pi/40)*4*10] + else: val = [math.sin((self.val+offset)*math.pi/40)*4*7 ] + if self.type == 3: # random + sin + val = [ random.uniform(15,+40), + math.sin((self.val)*math.pi/30)*15+20, + ] + if self.type == 5: # random + val = [random.uniform(-40,-10)] + if self.type == 6: # random + val = [random.uniform(-40,+40)] + if self.type == 4: # mix rand and sin + if self.switch: val = [math.sin(self.val*math.pi/40)*4*10] + else: val = [random.uniform(-40,+40)] + self.val+=1 + self.w.addValue(val) + self.timer.start(self.delay) + def demoGraph(root= None): frame = ttk.TTkFrame(parent=root, border=False, layout=ttk.TTkGridLayout()) graphWidget1 = ttk.TTkGraph(color=ttk.TTkColor.fg('#00dddd', modifier=ttk.TTkColorGradient(increment=-25))) graphWidget2 = ttk.TTkGraph(direction=ttk.TTkK.LEFT, color=ttk.TTkColor.fg('#ff8800', modifier=ttk.TTkColorGradient(increment= 40))) - graphWidget3 = ttk.TTkGraph(color=ttk.TTkColor.fg('#dd00dd', modifier=ttk.TTkColorGradient(increment=-20))) + graphWidget3 = ttk.TTkGraph(align=ttk.TTkK.BOTTOM, color=ttk.TTkColor.fg('#dd00dd', modifier=ttk.TTkColorGradient(increment=-20))) graphWidget4 = ttk.TTkGraph(color=ttk.TTkColor.fg('#00dd44', modifier=ttk.TTkColorGradient(increment=-30))) + graphWidget5 = ttk.TTkGraph(align=ttk.TTkK.TOP, color=ttk.TTkColor.fg('#dd44dd', modifier=ttk.TTkColorGradient(increment=-20))) + graphWidget6 = ttk.TTkGraph(color=ttk.TTkColor.fg('#00dd44', modifier=ttk.TTkColorGradient(increment=-30))) frame.layout().addWidget(graphWidget1, 0,0) frame.layout().addWidget(graphWidget2, 0,1) frame.layout().addWidget(graphWidget3, 1,0) frame.layout().addWidget(graphWidget4, 1,1) - class timerEvent(): - def __init__(self, w, type): - self.timer = ttk.TTkTimer() - self.val = 10 - self.switch = False - self.w = w - self.type = type - self.timer.timeout.connect(self.timerEvent) - self.timer.start(1) - @ttk.pyTTkSlot() - def timerEvent(self): - self.switch = not self.switch - if self.type == 1: # Simple sin - val = math.sin(self.val*math.pi/40)*4*10 - if self.type == 2: # Double sin - offset = 15 - if self.switch: val = math.sin(self.val*math.pi/40)*4*10 - else: val = math.sin((self.val+offset)*math.pi/40)*4*7 - if self.type == 3: # random - val = random.uniform(-40,+40) - if self.type == 4: # mix rand and sin - if self.switch: val = math.sin(self.val*math.pi/40)*4*10 - else: val = random.uniform(-40,+40) - self.val+=1 - self.w.addValue(val) - self.timer.start(0.1) - timerEvent(graphWidget1, 1) - timerEvent(graphWidget2, 2) - timerEvent(graphWidget3, 3) - timerEvent(graphWidget4, 4) + frame.layout().addWidget(graphWidget5, 2,0) + frame.layout().addWidget(graphWidget6, 2,1) + graphTimerEvent(graphWidget1, 1, 0.1) + graphTimerEvent(graphWidget2, 2, 0.1) + graphTimerEvent(graphWidget3, 3, 0.1) + graphTimerEvent(graphWidget4, 4, 0.05) + graphTimerEvent(graphWidget5, 5, 0.1) + graphTimerEvent(graphWidget6, 6, 0.1) return frame - def main(): parser = argparse.ArgumentParser() parser.add_argument('-f', help='Full Screen', action='store_true') diff --git a/tests/utf-8/test.braille.py b/tests/utf-8/test.braille.py new file mode 100755 index 00000000..5d974391 --- /dev/null +++ b/tests/utf-8/test.braille.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python3 + +# MIT License +# +# Copyright (c) 2021 Eugenio Parodi +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + + +''' + bits utf-8 diff with utf8 bits + 0 4 0,1,2 -> 0x00->0x07 ch012 + 1 5 4,5,6 -> 0x00->0x07 << 3 ch456>>1 + 2 6 + 3 7 + | \---> 7 -> 0x80 ch7 + \-----> 3 -> 0x40 ch3<<3 +''' +def ch2braille(ch): + ch012 = ch & 0x07 + ch456 = ch & 0x70 + ch3 = ch & 0x08 + ch7 = ch & 0x80 + return 0x2800 | ch012 | (ch456>>1) | (ch3<<3) | ch7 + + +codes = [ chr(ch2braille(ch)) for ch in range(0,0x100)] + +print("braille=(") +for a in range(0x10): + for b in range(0x10): + val = a | (b<<4) + print(f"'{codes[val]}',",end='') + print() +print(")") +