11# Builtin modules
22import curses
33from time import sleep , time
4- from threading import Thread , Event , Lock
54from typing import Dict , List , Any , TYPE_CHECKING
5+ from threading import Lock
66from dataclasses import dataclass
77# Third party modules
88# Local modules
@@ -39,81 +39,112 @@ class MainModule:
3939 prices :List [float ]
4040 priceCaches :List [float ]
4141 lastTimestamp :float
42- closeEvent : Event
42+ tickerCounter : int
4343 pricesLock :Lock
4444 terminalSize :YX
4545 screen :Any
4646 currency :str
47- thr :Thread
4847 def __init__ (self , currency :str = "btc" ) -> None :
4948 self .terminalSize = YX (0 , 0 )
50- self .closeEvent = Event ()
5149 self .pricesLock = Lock ()
5250 self .lastTimestamp = 0
51+ self .tickerCounter = 0
5352 self .prices = []
5453 self .priceCaches = []
5554 def _write (self , text :str , color :str = "WHITE" ) -> None :
5655 self .screen .addstr (text , curses .color_pair (COLORS [color ]))
5756 def _writeN (self , text :str , length :int , color :str = "WHITE" ) -> None :
5857 self .screen .addnstr (text , length , curses .color_pair (COLORS [color ]))
59- def _fetchFirst (self ) -> None : ...
58+ def _fetchHistory (self ) -> None : ...
6059 def _fetchThreadLoop (self ) -> None : ...
60+ def _start (self ) -> None : ...
61+ def _close (self ) -> None : ...
6162 def _loop (self , screen :Any ) -> None :
6263 try :
6364 try :
64- curses .start_color ()
6565 curses .use_default_colors ()
6666 for i in range (curses .COLORS ):
6767 curses .init_pair (i + 1 , i , - 1 )
6868 screen .attron ( curses .color_pair (COLORS ["WHITE" ]) )
6969 curses .curs_set (0 )
7070 except :
71- curses . initscr ()
71+ pass
7272 screen .timeout (0 )
7373 screen .clear ()
7474 screen .addstr ("Loading prices..." )
7575 screen .refresh ()
7676 self .screen = screen
77+ doRefresh = False
78+ lastData = 0.0 , 0
79+ lastTS = 0
7780 while True :
78- sleep (1 )
79- screen .clear ()
81+ sleep (0.1 )
82+ c = self .screen .getch ()
83+ if c == ord ('q' ):
84+ break
85+ doRefresh = False
8086 tmpTerminalSize = YX (* screen .getmaxyx ())
8187 if tmpTerminalSize != self .terminalSize :
8288 self .terminalSize = tmpTerminalSize
89+ doRefresh = True
8390 if self .terminalSize .x <= 30 or self .terminalSize .y <= 15 :
91+ screen .clear ()
8492 self ._writeN ("No place to print" , 17 , "RED" )
8593 screen .refresh ()
8694 continue
8795 with self .pricesLock :
8896 if time ()- self .lastTimestamp > 60 :
89- if not self .priceCaches :
97+ if self .tickerCounter % 5 == 0 :
98+ self .prices .clear ()
99+ self ._fetchHistory ()
90100 self .prices .append (self .prices [- 1 ])
91101 else :
92- self .prices .append (sum (self .priceCaches ) / len (self .priceCaches ))
93- self .priceCaches .clear ()
102+ if not self .priceCaches :
103+ self .prices .append (self .prices [- 1 ])
104+ else :
105+ self .prices .append (sum (self .priceCaches ) / len (self .priceCaches ))
106+ self .priceCaches .clear ()
107+ self .tickerCounter += 1
94108 self .lastTimestamp = int (time () // 60 * 60 )
95- else :
96- if self .priceCaches :
97- self .prices [- 1 ] = sum (self .priceCaches ) / len (self .priceCaches )
109+ doRefresh = True
110+ elif self .priceCaches :
111+ pcs = sum (self .priceCaches )
112+ pcl = len (self .priceCaches )
113+ if lastData != (pcs , pcl ):
114+ self .prices [- 1 ] = pcs / pcl
98115 self .priceCaches .clear ()
116+ lastData = pcs , pcl
117+ doRefresh = True
118+ if lastTS != int (time ()% 60 ):
119+ lastTS = int (time ()% 60 )
120+ doRefresh = True
121+ if not doRefresh :
122+ continue
123+ screen .clear ()
99124 # Parsing cache
100125 screen .move (0 , 0 )
101126 self ._write ("BTC price: " )
102127 self ._write ("{:.2F}" .format (self .prices [- 1 ]), "RED" if self .prices [- 1 ] < self .prices [- 2 ] else "GREEN" )
103- self ._write (" USD " )
128+ self ._write (" USD | " )
104129 self ._write ("{}" .format (60 - int (time ()- self .lastTimestamp )), "YELLOW" )
105130 self ._write (" seconds left for the next tick. [{} tickers in cache]\n " .format (len (self .prices )))
106131 self ._write ("Changes: " )
107- for i , tDiff in enumerate ([
108- TickerDiff (self .prices [- 1 ], self .prices [- 2 ]),
109- TickerDiff (self .prices [- 1 ], self .prices [- 16 ]),
110- TickerDiff (self .prices [- 1 ], self .prices [- 31 ]),
111- TickerDiff (self .prices [- 1 ], self .prices [- 61 ]),
132+ for i , (d , tDiff ) in enumerate ([
133+ ("1 min: " , TickerDiff (self .prices [- 1 ], self .prices [- 2 ])),
134+ ("15 min: " , TickerDiff (self .prices [- 1 ], self .prices [- 16 ])),
135+ ("30 min: " , TickerDiff (self .prices [- 1 ], self .prices [- 31 ])),
136+ ("1 hour: " , TickerDiff (self .prices [- 1 ], self .prices [- 61 ])),
137+ ("12 hour: " , TickerDiff (self .prices [- 1 ], self .prices [- 721 ])),
112138 ]):
113139 if i > 0 :
114- self ._write (" : " )
115- self ._write ("{sign}{val:.2F} ({sign}{perc:.2F}%)" .format (** tDiff .__dict__ ), tDiff .color )
116- self ._write (" (1min:15min:30min:1hour)" )
140+ d = " | " + d
141+ text = "{sign}{val:.2F} ({sign}{perc:.2F}%)" .format (** tDiff .__dict__ )
142+ cy , cx = screen .getyx ()
143+ if len (text )+ len (d ) >= (self .terminalSize .x - cx ):
144+ self ._write ("\n " )
145+ d = " " + d [3 :]
146+ self ._write (d )
147+ self ._write (text , tDiff .color )
117148 cy , cx = screen .getyx ()
118149 writeChart (
119150 screen ,
@@ -128,11 +159,8 @@ def _loop(self, screen:Any) -> None:
128159 except KeyboardInterrupt :
129160 return
130161 finally :
131- self .closeEvent .set ()
132- self .thr .join (5 )
162+ self ._close ()
133163 curses .endwin ()
134164 def start (self ) -> None :
135- self ._fetchFirst ()
136- self .thr = Thread (target = self ._fetchThreadLoop , daemon = True )
137- self .thr .start ()
165+ self ._start ()
138166 curses .wrapper (self ._loop )
0 commit comments