Python 3.x Cross Platform Console Library
modified: 2015-03-22 04:22:15

        Leonard Kevin McGuire Jr 2014 (kmcg3413@gmail.com)
        This gives an implementation similar to ncurses and the python curses module. The
        API is totally different, but it does try to provide a minimal cross-platform
        support for positional printing and colors.
        It also supports cross-process access. This allows you to query information from
        another process through a TCP server.
        * colors still being implemented
    import random
    import struct
    import sys
    import math
    import sys
    from ctypes import *
        import curses
        hasCurses = True
        hasCurses = False
    class BufferInfo:
    class CrossTerm:
        def __init__(self, win):
            global hasCurses
            self.win = win
            self.hasCurses = hasCurses
            # if on win32 platform use kernel32 dynamic link library
            if not hasCurses:
                self.hdll = windll.LoadLibrary('kernel32.dll')
                #self.hSetConsoleCursorPosition = CFUNCTYPE(c_int)(('SetConsoleCursorPosition', self.hdll))
                self.hSetConsoleCursorPosition = self.hdll['SetConsoleCursorPosition']
                #self.hGetStdHandle = CFUNCTYPE(c_uint)(('GetStdHandle', self.hdll))
                self.hGetStdHandle = self.hdll['GetStdHandle']
                #self.hWriteConsoleOutputCharacter = CFUNCTYPE(c_uint)(('WriteConsoleOutputCharacterA', self.hdll))
                self.hWriteConsoleOutputCharacter = self.hdll['WriteConsoleOutputCharacterA']
                #self.hGetConsoleScreenBufferInfo = CFUNCTYPE(c_uint)(('GetConsoleScreenBufferInfo', self.hdll))
                self.hGetConsoleScreenBufferInfo = self.hdll['GetConsoleScreenBufferInfo']
                sw, sh = self.getScreenSize()
                # scroll the buffer so we get a new blank space
                # similar to how curses initializes the screen
                for i in range(0, sh):
                # get the row index of the top line of our current
                # screen
                binfo = self._getConsoleScreenBufferInfo()
                self.topy = binfo.winY
            This will set the absolute cursor position.
            * curses did not implement this.. so me either but
              just wanted the code to stay for future reference
              if needed (for the win32 function call)
        #def setCursorPosition(self, x, y):
        #    if self.hasCurses:
        #    else:
        #        ch = self.hGetStdHandle(c_int(-11))
        #        self.hSetConsoleCursorPosition(c_uint(ch), c_short(x), c_short(y))
            Will return current cursor position.
        def getCursorPosition(self):
            if self.hasCurses:
                return self.win.getyx()
                ch = self.hGetStdHandle(c_int(-11))
                buf = struct.pack('<hhhhhhhhhhh', 0,="" 0)="" self.hgetconsolescreenbufferinfo(c_uint(ch),="" c_char_p(buf))="" info="struct.unpack_from('<HHHH'," buf)="" cx="info[2]" cy="info[3]" return="" (cx,="" cy)="" '''="" should="" be="" caled="" after="" you="" make="" any="" changes="" to="" have="" them="" displayed="" the="" screen.="" this="" actually="" does="" nothin="" for="" windows="" but="" it="" is="" required="" curses.="" so="" if="" want="" cross-platform="" operation="" then="" just="" call="" as="" needed.="" def="" update(self):="" self.hascurses:="" self.win.refresh()="" will="" clear="" screen="" with="" all="" spaces.="" clear(self):="" self.win.clear()="" #="" win32="" api="" has="" no="" function="" i="" implement="" in="" w,="" h="self.getScreenSize()" y="" range(0,="" h):="" create="" new="" lines="" height="" of="" current="" window="" (to="" keep="" scrollable="" history)="" self.writestringat(0,="" self.topy="" +="" h,="" '="" *="" w)="" internal="" winnt="" only="" _getconsolescreenbufferinfo(self):="" ch="self.hGetStdHandle(c_int(-11))" buf="struct.pack('<HHHHHHHHHHH'," http:="" msdn.microsoft.com="" en-us="" library="" ms682093(v="vs.85).aspx" binfo="BufferInfo()" binfo.sizew="info[0]" binfo.sizeh="info[1]" binfo.cursorposx="info[2]" binfo.cursorposy="info[3]" binfo.attributes="info[4]" binfo.winx="info[5]" binfo.winy="info[6]" binfo.winw="info[7]" binfo.winh="info[8]" get="" size="" visible="" (does="" not="" include="" buffer="" any.)="" getscreensize(self):="" self.win.getyx()="" else:="" #w="info[0]" -="" width="" entire="" #h="info[1]" #print(info[5])="" #print(info[6])="" #print(info[7])="" #print(info[8])="" w="binfo.winW" (w,="" h)="" write="" string="" at="" position.="" writestringat(self,="" x,="" y,="" s,="" attr="0," maxpad="0):"> 0:
                if len(s) < maxpad:
                    s = '%s%s' % (s, ' ' * (maxpad - len(s)))
                    s = s[0:maxpad]
            if self.hasCurses:
                self.win.addstr(y, x, s, attr)
                y = self.topy + y
                ch = self.hGetStdHandle(c_int(-11))
                s = bytes(s, 'utf8')
                wrote = struct.pack('<q', 0)="" cord="(y" <<="" 16)="" |="" x="" err="self.hWriteConsoleOutputCharacter(c_uint(ch)," c_char_p(s),="" c_uint(len(s)),="" cord,="" c_char_p(wrote))="" '''="" this="" version="" mainly="" adds="" support="" to="" help="" with="" boxing.="" it="" serves="" prevent="" you="" from="" overwriting="" your="" allocated="" area.="" i="" may="" add="" for="" automatic="" allocation="" of="" boxes="" somewhere="" on="" the="" screen="" at="" a="" later="" time.="" but,="" now="" must="" specify="" box="" dimensions.="" class="" boxoverlapexception(exception):="" pass="" object="" used="" write="" when="" using="" boxes.="" crossterm2box:="" def="" __init__(self,="" ct,="" x,="" y,="" w,="" h,="" prefix="" ):="" self.ct="ct" self.x="x" self.y="y" self.w="w" self.h="h" self.prefix="prefix" setprefix(self,="" prefix):="" getrect(self):="" return="" (self.x,="" self.y,="" self.w,="" self.h)="" write(self,="" text):="" text="%s%s" %="" (self.prefix,="" text)="" if="" len(text)=""> self.w * self.h:
                # truncate it
                text = text[0:self.w * self.h]
            rowcnt = int(math.ceil(len(text) / self.w))
            # write it over entire box
            for row in range(0, rowcnt):
                off = row * self.w
                line = text[off:self.w]
                if len(line) < self.w:
                    line = '%s%s' % (line, ' ' * (self.w - len(line)))
                self.ct.writeStringAt(self.x, self.y + row, line)
        This provides enhanced and extra features.
        [X] box support
        [ ] tcp server support
    class CrossTerm2(CrossTerm):
        def __init__(self, *args):
            self.w, self.h = self.getScreenSize()
            self.boxes = []
            You specify the box width and height and it will
            find a spot to put it.
        def getBoxAuto(self, w, h, prefix = ''):
            for x in range(0, self.w):
                for y in range(0, self.h):
                    box = self.getBox(x, y, w, h, prefix = prefix)
                    if box is not None:
                        return box
            # no space big enough for the box
            return None
            This will create a new box.
        def getBox(self, x, y, w, h, prefix = ''):
            # look through list of boxes and find place to put
            nx1 = x
            ny1 = y
            nx2 = (x + w) - 1
            ny2 = (y + h) - 1
            # check for overlap
            for box in self.boxes:
                bx1, by1, bx2, by2 = box.getRect()
                bx2 += bx1
                by2 += by1
                if nx1 >= bx1 and nx1 < bx2 and ny1 >= by1 and ny1 < by2:
                    if nx2 >= bx1 and nx2 < bx2 and ny2 >= by1 and ny2 < by2:
                        return None
            nbox = CrossTerm2Box(self, x, y, w, h, prefix = prefix)
            # trigger prefix if any
            if len(prefix) > 0:
            return nbox
        If using curses it wraps the code path so the terminal
        can be restored when program exits. If not using curses
        it either does an alternative initialization or nothing.
    def wrapper(f, *args):
        win = None
        if hasCurses:
            # initial screen
            win = curses.initscr()
            # turn on cbreak
            # turn off echo
            # turn on terminal keypad
            # initialize colors
        ct = CrossTerm2(win)
        if True or not hasCurses:
            #            WINDOWS ONLY
            # to keep the programming from accidentally
            # messing the window up by print something
            # to the screen we will direct all output
            # from print statements to a file; curses on
            # linux can handle print statements and do
            # not effect the buffer but we have no good
            # way to do it on windows platform unless
            # we created a brand new console window just
            # for our output
            stdout = open('.stdout', 'w')
            ct.stdout = sys.stdout         # save it
            sys.stdout = stdout            # protect it
            ct.stderr = sys.stderr
            sys.stderr = stdout
            f(ct, *args)
            if hasCurses:
                # restore cooked mode
                # turns on echo
                # disales terminal keypad
                # restore terminal mode
            sys.stdout = ct.stdout
            sys.stderr = ct.stderr
        My testing function.
    def main(xc):
        #print('\033[%sA' % y)   # move cursor up
        #print('\033[%sD' % x)   # move cursor left
        #print('\033[%s;%sH%s' % (y + 1, x + 1, s))   # set cursor position
        #writeStringAtPosition(0, 0, 'a world')
        #writeStringAtPosition(0, 1, 'b apple')
        #writeStringAtPosition(0, 2, 'c grape')
        #win = curses.initscr()
        for a in range(0, 15):
            xc.writeStringAt(10, 2 + a, 'HELLO WORLD', 253)
        #xc.writeStringAt(10, 10, 'HELLO WORLD')
        while True:
    if __name__ == '__main__':