Add "frames per second" to the CursesReporter.
You can now set the FPS goal with `--fps=10` (or whatever) on the command line; and the current (measured) FPS is displayed in the lower right corner. During the run, you can bump the FPS goal up and down with `F` and `f` respectively!
This commit is contained in:
@ -10,8 +10,9 @@ if __name__ == '__main__':
|
|||||||
logging.basicConfig(level=logging.INFO, format='%(message)s', filename='./copycat.log', filemode='w')
|
logging.basicConfig(level=logging.INFO, format='%(message)s', filename='./copycat.log', filemode='w')
|
||||||
|
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
parser.add_argument('--seed', type=int, default=None, help='Provide a deterministic seed for the RNG.')
|
|
||||||
parser.add_argument('--focus-on-slipnet', action='store_true', help='Show the slipnet and coderack, instead of the workspace.')
|
parser.add_argument('--focus-on-slipnet', action='store_true', help='Show the slipnet and coderack, instead of the workspace.')
|
||||||
|
parser.add_argument('--fps', type=float, default=None, help='Aim for this many frames per second.')
|
||||||
|
parser.add_argument('--seed', type=int, default=None, help='Provide a deterministic seed for the RNG.')
|
||||||
parser.add_argument('initial', type=str, help='A...')
|
parser.add_argument('initial', type=str, help='A...')
|
||||||
parser.add_argument('modified', type=str, help='...is to B...')
|
parser.add_argument('modified', type=str, help='...is to B...')
|
||||||
parser.add_argument('target', type=str, help='...as C is to... what?')
|
parser.add_argument('target', type=str, help='...as C is to... what?')
|
||||||
@ -23,6 +24,7 @@ if __name__ == '__main__':
|
|||||||
reporter=CursesReporter(
|
reporter=CursesReporter(
|
||||||
window,
|
window,
|
||||||
focus_on_slipnet=options.focus_on_slipnet,
|
focus_on_slipnet=options.focus_on_slipnet,
|
||||||
|
fps_goal=options.fps,
|
||||||
),
|
),
|
||||||
rng_seed=options.seed,
|
rng_seed=options.seed,
|
||||||
)
|
)
|
||||||
|
|||||||
@ -27,6 +27,9 @@ class SafeSubwindow(object):
|
|||||||
def border(self):
|
def border(self):
|
||||||
self.w.border()
|
self.w.border()
|
||||||
|
|
||||||
|
def derwin(self, h, w, y, x):
|
||||||
|
return self.w.derwin(h, w, y, x)
|
||||||
|
|
||||||
def erase(self):
|
def erase(self):
|
||||||
self.w.erase()
|
self.w.erase()
|
||||||
|
|
||||||
@ -48,7 +51,7 @@ class SafeSubwindow(object):
|
|||||||
|
|
||||||
|
|
||||||
class CursesReporter(Reporter):
|
class CursesReporter(Reporter):
|
||||||
def __init__(self, window, focus_on_slipnet=False):
|
def __init__(self, window, focus_on_slipnet=False, fps_goal=None):
|
||||||
curses.curs_set(0) # hide the cursor
|
curses.curs_set(0) # hide the cursor
|
||||||
curses.noecho() # hide keypresses
|
curses.noecho() # hide keypresses
|
||||||
height, width = window.getmaxyx()
|
height, width = window.getmaxyx()
|
||||||
@ -59,15 +62,21 @@ class CursesReporter(Reporter):
|
|||||||
answersHeight = 5
|
answersHeight = 5
|
||||||
coderackHeight = height - upperHeight - answersHeight
|
coderackHeight = height - upperHeight - answersHeight
|
||||||
self.focusOnSlipnet = focus_on_slipnet
|
self.focusOnSlipnet = focus_on_slipnet
|
||||||
|
self.fpsGoal = fps_goal
|
||||||
self.temperatureWindow = SafeSubwindow(window, height, 5, 0, 0)
|
self.temperatureWindow = SafeSubwindow(window, height, 5, 0, 0)
|
||||||
self.upperWindow = SafeSubwindow(window, upperHeight, width-5, 0, 5)
|
self.upperWindow = SafeSubwindow(window, upperHeight, width-5, 0, 5)
|
||||||
self.coderackWindow = SafeSubwindow(window, coderackHeight, width-5, upperHeight, 5)
|
self.coderackWindow = SafeSubwindow(window, coderackHeight, width-5, upperHeight, 5)
|
||||||
self.answersWindow = SafeSubwindow(window, answersHeight, width-5, upperHeight + coderackHeight, 5)
|
self.answersWindow = SafeSubwindow(window, answersHeight, width-5, upperHeight + coderackHeight, 5)
|
||||||
for w in [self.temperatureWindow, self.upperWindow, self.answersWindow]:
|
self.fpsWindow = SafeSubwindow(self.answersWindow, 3, 9, answersHeight - 3, width - 14)
|
||||||
|
for w in [self.temperatureWindow, self.upperWindow, self.answersWindow, self.fpsWindow]:
|
||||||
w.erase()
|
w.erase()
|
||||||
w.border()
|
w.border()
|
||||||
w.refresh()
|
w.refresh()
|
||||||
self.answers = {}
|
self.answers = {}
|
||||||
|
self.fpsTicks = 0
|
||||||
|
self.fpsSince = time.time()
|
||||||
|
self.fpsMeasured = 100 # just a made-up number at first
|
||||||
|
self.fpsDelay = 0
|
||||||
|
|
||||||
def do_keyboard_shortcuts(self):
|
def do_keyboard_shortcuts(self):
|
||||||
w = self.temperatureWindow # just a random window
|
w = self.temperatureWindow # just a random window
|
||||||
@ -79,11 +88,18 @@ class CursesReporter(Reporter):
|
|||||||
while ordch not in [ord('P'), ord('p'), 27, ord('Q'), ord('q')]:
|
while ordch not in [ord('P'), ord('p'), 27, ord('Q'), ord('q')]:
|
||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
ordch = w.getch()
|
ordch = w.getch()
|
||||||
|
self.fpsTicks = 0
|
||||||
|
self.fpsSince = time.time()
|
||||||
w.erase()
|
w.erase()
|
||||||
w.border()
|
w.border()
|
||||||
w.refresh()
|
w.refresh()
|
||||||
if ordch in [27, ord('Q'), ord('q')]:
|
if ordch in [27, ord('Q'), ord('q')]:
|
||||||
raise KeyboardInterrupt()
|
raise KeyboardInterrupt()
|
||||||
|
if ordch in [ord('F')]:
|
||||||
|
self.fpsGoal = (self.fpsGoal or self.fpsMeasured) * 1.25
|
||||||
|
if ordch in [ord('f')]:
|
||||||
|
self.fpsGoal = (self.fpsGoal or self.fpsMeasured) * 0.8
|
||||||
|
|
||||||
|
|
||||||
def report_answer(self, answer):
|
def report_answer(self, answer):
|
||||||
d = self.answers.setdefault(answer['answer'], {
|
d = self.answers.setdefault(answer['answer'], {
|
||||||
@ -122,7 +138,31 @@ class CursesReporter(Reporter):
|
|||||||
w.addnstr(i+1, 2, represent(d), columnWidth)
|
w.addnstr(i+1, 2, represent(d), columnWidth)
|
||||||
w.refresh()
|
w.refresh()
|
||||||
|
|
||||||
|
def depict_fps(self):
|
||||||
|
w = self.fpsWindow
|
||||||
|
now = time.time()
|
||||||
|
elapsed = now - self.fpsSince
|
||||||
|
fps = self.fpsTicks / elapsed
|
||||||
|
if self.fpsGoal is not None:
|
||||||
|
seconds_of_work_per_frame = (elapsed / self.fpsTicks) - self.fpsDelay
|
||||||
|
desired_time_working_per_second = self.fpsGoal * seconds_of_work_per_frame
|
||||||
|
if desired_time_working_per_second < 1.0:
|
||||||
|
self.fpsDelay = (1.0 - desired_time_working_per_second) / fps
|
||||||
|
else:
|
||||||
|
self.fpsDelay = 0
|
||||||
|
w.addstr(1, 1, 'FPS:%3d' % fps, curses.A_NORMAL)
|
||||||
|
w.refresh()
|
||||||
|
self.fpsSince = now
|
||||||
|
self.fpsTicks = 0
|
||||||
|
self.fpsMeasured = fps
|
||||||
|
|
||||||
def report_coderack(self, coderack):
|
def report_coderack(self, coderack):
|
||||||
|
self.fpsTicks += 1 # for the purposes of FPS calculation
|
||||||
|
if self.fpsDelay:
|
||||||
|
time.sleep(self.fpsDelay)
|
||||||
|
if time.time() > self.fpsSince + 1.200:
|
||||||
|
self.depict_fps()
|
||||||
|
|
||||||
NUMBER_OF_BINS = 7
|
NUMBER_OF_BINS = 7
|
||||||
|
|
||||||
# Combine duplicate codelets for printing.
|
# Combine duplicate codelets for printing.
|
||||||
|
|||||||
Reference in New Issue
Block a user