from math import floor
from time import time
from qt import QWidget, QPainter, Qt, QSize, QColor, QFontMetricsF
from retype.ui.painting import rectPixmap, textPixmap, linePixmap, Font
[docs]class StatsDock(QWidget):
[docs] def __init__(self, parent=None):
super().__init__(parent)
self.connected = False
self.prev_cursor_pos = 0
self.prev_seconds = 0
self.prev_ts = 0
self.c = 0
self.cpm = 0
self.wpm = 0
self.wpm_pb = 0
self.wpms = []
self.background_colour = QColor('#CDCDC1')
self.foreground_colour = QColor('white')
self.text_colour = QColor('black')
self.grid_colour = QColor('gray')
[docs] def connectConsole(self, console):
self._hs = console.highlighting_service
console.textEdited.connect(self.onUpdate)
self.connected = True
[docs] def onUpdate(self, text):
v = self._hs.book_view
if not v.isVisible:
return
ts = round(time())
# Start “timer”
if not self.prev_ts:
self.prev_ts = ts
seconds = (ts - self.prev_ts) or 1
# Reset if been inactive
if seconds - self.prev_seconds > 2:
self.prev_ts = ts
self.c = 0
graphShouldUpdate = False
# FIXME: This is probably not very robust; it’s an attempt to make
# things work for both character-by-character typing and stenography
# where whole words can be inputted at once, while preventing the
# cursor-maniplation commands from affecting the count undesirably.
if len(text) >= v.cursor_pos - self.prev_cursor_pos > 0:
self.c += v.cursor_pos - self.prev_cursor_pos
graphShouldUpdate = True
# CPM and WPM calculation
self.cpm = floor((self.c / seconds) * 60)
self.wpm = floor(self.cpm / 5)
# Personal best
if self.wpm > self.wpm_pb:
self.wpm_pb = self.wpm
# Periodic refreshment
if seconds > 20:
self.prev_ts = ts - 5
self.c = self.cpm / 12
# Graph update
if graphShouldUpdate:
self.rect_w = 15
w = self.size().width()
amount = floor(w / self.rect_w)
if len(self.wpms) > amount:
length = len(self.wpms)
self.wpms = self.wpms[length-amount:length]
self.wpms.append(self.wpm)
self.update()
self.prev_seconds = seconds
self.prev_cursor_pos = v.cursor_pos
[docs] def paintEvent(self, e):
w = self.size().width()
h = self.size().height()
factor = 1 if not self.wpm_pb else h/self.wpm_pb
qp = QPainter()
qp.begin(self)
draw = qp.drawPixmap
# Background
qp.fillRect(0, 0, w, h, self.background_colour)
# WPM rects
i = 0
for wpm in self.wpms:
rect_h = floor(wpm * factor)
draw(i, h - rect_h,
rectPixmap(self.rect_w, wpm * factor,
self.background_colour, self.foreground_colour))
i += self.rect_w
# Gridlines
i = 50
while i < self.wpm_pb:
y = h - (i * factor)
qp.drawPixmap(0, y,
linePixmap(w, 0, self.grid_colour, 1,
style=Qt.PenStyle.DashLine))
i += 50
# Text
font = Font.GENERAL.toQFont()
fm = QFontMetricsF(font)
font_h = fm.height()
pb_txt = "PB: {}".format(self.wpm_pb)
cur_txt = "Current: {} WPM".format(self.wpm)
draw(2, 2,
textPixmap(pb_txt, fm.horizontalAdvance(pb_txt), font_h,
font, self.text_colour))
cur_w = fm.horizontalAdvance(cur_txt)
draw(w - cur_w - 2, 2,
textPixmap(cur_txt, cur_w, font_h, font, self.text_colour))
qp.end()
[docs] def sizeHint(self):
return QSize(50, 25)