#!/usr/bin/env python3 from textwrap import TextWrapper as wrap from threading import Thread import signal from time import sleep, time import os.path as path from os.path import getmtime as gt import curses from curses import wrapper import curses.textpad as textpad import sys import re class Viewport: def __init__(self, screen): curses.curs_set(0) self.user_path = path.expanduser('~') self.username = path.split(self.user_path)[-1] self.screen = screen self.width = curses.COLS - 1 self.height = curses.LINES - 1 self.last_update = 0.0 self.last_msg = '' self.update = False self.path = '/home/sloum/Documents/code/python/colorchat/chatlog' self.screen.nodelay(True) self.screen.clear() self.c_out = self.screen.subwin(self.height - 3, self.width,0,0) self.c_in = self.screen.subwin(3,self.width,self.height - 3,0) self.text_buffer = '' self.watcher = Thread(target=self.watch, args=(self.path,), daemon=True) self.main() def main(self): self.watcher.start() while True: self.text_input() if self.update: self.flash_log() self.update = False sleep(0.05) def watch(self,filepath): while True: filetime = gt(filepath) elapsedtime = self.elapsed_time(filetime) if elapsedtime: self.last_msg = elapsedtime if filetime > self.last_update: self.last_update = filetime self.update = True else: self.update = False sleep(0.5) def flash_log(self): with open(self.path, 'r') as f: temp = f.read() temp_list = self.wrap_text(temp) rows = len(temp_list) max_rows = self.height - 2 self.c_out.erase() self.c_out.hline(0,0,'.',self.width) title = '_colorchat_' position = self.width // 2 - len(title) // 2 self.c_out.addstr(0,position,title) if rows < max_rows: for x in range(rows - 1): self.c_out.addstr(max_rows - rows + x, 0, temp_list[x]) else: counter = 1 for x in temp_list[-(max_rows-2):]: self.c_out.addstr(counter, 0, x) counter += 1 self.c_out.refresh() # takes in a string and outputs a list of strings def wrap_text(self, string): ob = wrap(width=self.width - 2, tabsize=4, replace_whitespace=False) text = [ob.fill(x) for x in string.split('\n')] text = '\n'.join(text).split('\n') return text def text_input(self): self.c_in.erase() c = self.screen.getch() if c in (curses.KEY_ENTER, 10, 13): self.send_msg() elif c == curses.KEY_BACKSPACE: if len(self.text_buffer) > 0: self.text_buffer = self.text_buffer[:-1] elif c != -1 and not c in [curses.KEY_UP, curses.KEY_LEFT, curses.KEY_DOWN, curses.KEY_RIGHT]: character = chr(c) if re.match(r'^[\S ]+$',character): self.text_buffer += chr(c) self.c_in.hline(0,0,'-',self.width) self.c_in.addstr(1,1,self.text_buffer[-(self.width * 2 - 10):] + '_') self.c_in.addstr(2,self.width - 1 - len(self.last_msg),self.last_msg) self.c_in.refresh() def send_msg(self): if self.text_buffer in ['q','quit','exit']: sys.exit(0) with open(self.path, 'a') as f: f.write('{} > {}\n'.format(self.username,self.text_buffer)) self.text_buffer = '' def elapsed_time(self,last_time): if not last_time: return False current = time() elapsed = int(current - last_time) unit = 's' if elapsed >= 60: elapsed = elapsed // 60 unit = 'm' if elapsed >= 60: elapsed = elapsed // 60 unit = 'h' if elapsed >= 24: elapsed = elapsed // 24 unit = 'd' return 'Time since last post: {}{}'.format(elapsed,unit) if __name__ == '__main__': signal.signal(signal.SIGINT,signal.SIG_IGN) try: wrapper(Viewport) except KeyboardInterrupt: curses.echo() curses.nocbreak() curses.endwin() finally: sys.exit(1)