Python Forum
Thread Rating:
  • 1 Vote(s) - 1 Average
  • 1
  • 2
  • 3
  • 4
  • 5
colpage.py
#1
colpage.py:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from __future__ import absolute_import, division, print_function, unicode_literals
"""Organize strings into columns and columns into pages using separate coroutines.

file          colpage.py
purpose       organize strings into columns and columns into pages
email         10054452614123394844460370234029112340408691

The intent is that this command works correctly under both Python 2 and
Python 3.  Please report failures or code improvement to the author.
"""

__license__ = """
Copyright (C) 2017, by Phil D. Howard - all other rights reserved

Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA, OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

The author may be contacted by decoding the number
10054452614123394844460370234029112340408691
(provu igi la numeron al duuma)
"""

import fcntl, os, struct, sys, termios


def get_terminal_geometry(fd=1):
    """Get terminal geometry (width,heght) for a specified fd."""
    import fcntl,os,struct,termios
    try:
        fd = os.open('/dev/tty',os.O_WRONLY)
        r = struct.unpack('4H',fcntl.ioctl(fd,termios.TIOCGWINSZ,struct.pack('4H',0,0,0,0)))[1::-1]
        os.close(fd)
    except:
        r = None
    return r


class colpage:
    """class representing column formatted pages"""


    def __init__(self,gutter=None,width=None,height=None):
        """initialize class colpage"""

        self.page_num = 0

        return self.geometry( gutter=gutter, width=width, height=height )


    def flush(self):
        """flush pending columns to a completed page"""

        gutter = self.gutter
        page = ['']*self.height
        mc = len(self.columns)-1
        for n,column in enumerate(self.columns):    # add each column to the page on the right side
            width = self.widths[n]
            for m,line in enumerate(column):        # append each line to the page being built
                page[m] += (line+' '*width)[:width]
                if n < mc:                          # append a gutter except after the last column
                    page[m] += ' '*gutter
        self.pages.append(page)                     # add the new page to the list of completed pages
        self.columns = []
        self.widths = []
        return len(self.pages)


    def geometry(self,gutter=None,width=None,height=None):
        """set or get colpage geometry (gutter,width,height)"""

        if 'gutter' in dir(self) and gutter is None and width is None and height is None:
            self_gutter=self.gutter
            return (self.gutter,self.width,self.height)

        if width is None or height is None:
            term = get_terminal_geometry()

        if isinstance(gutter,int):
            self.gutter = gutter
        elif gutter is None:
            gutter = 1
            if 'gutter' in dir(self):
                self.gutter = gutter

        if isinstance(width,int):
            self.width = width
        if width is None:
            width = term[0]
            if 'width' in dir(self):
                self.width = width

        if isinstance(height,int):
            self.height = height
        if height is None:
            height = term[1]
            if 'height' in dir(self):
                self.height = height

        if gutter < 0:
            raise ValueError

        if width < 2:
            raise ValueError

        if height < 2:
            raise ValueError

        self.gutter = gutter
        self.height = height
        self.width = width

        self.columns = []
        self.lines = []
        self.pages = []
        self.widths = []

        self.page_starter = ['']*self.height
    
        return


    def destruct(self):
        del self
        return


    def get(self):
        """get the next completed page (list of list of str) or None."""
        
        if len(self.pages) > 0:
            return self.pages.pop(0)
        return None


    def print_ready_pages(self,**opts):
        """print the pages that are ready."""

        while True:
            page = self.get()
            if page == None:
                break
            for line in page:
                print(line,**opts)
        return



    def put(self,*args):
        """put a new string as a line into a page column."""

        if len(args) > 0:

            line = ' '.join(args)
            self.lines.append(line)

            # if this new line does not fill the height, then we are done
            if len(self.lines) < self.height:
                return

        # the working list is full or being flushed by put() with no arguments

        width = max(len(l) for l in self.lines)
        total_width = sum(this_width+self.gutter for this_width in self.widths)+width
        if total_width >= self.width:
            self.flush()

        self.columns.append(self.lines)
        self.widths.append(width)

        if len(args) > 0:
            self.lines = []
        else:
            self.flush()

        return


def main(args):
    """main: command line reads stdin, forms it into columns, outputs to stdout."""

    if len(args) > 3:
        gutter, width, height = [int(arg) for arg in args[1:]]
        colz = colpage( gutter=gutter, width=width, height=height )
    else:
        # let colpage try to get the geometry from the terminal size
        colz = colpage( gutter=1 )

    # feed stdin to colpage, send its pages to stdout
    for line in sys.stdin:
        colz.put(line[:-1])
        colz.print_ready_pages()

    # finish up at EOF
    colz.put()
    colz.print_ready_pages()

    return


if __name__ == '__main__':
    try:
        result=main(sys.argv)
        sys.stdout.flush()
    except BrokenPipeError:
        result=99
    except KeyboardInterrupt:
        print('')
        result=98
    if result is 0 or result is None or result is True:
        exit(0)
    if result is 1 or result is False:
        exit(1)
    if isinstance(result,str):
        print(result,file=sys.stderr)
        exit(2)
    try:
        exit(int(result))
    except ValueError:
        print(str(result),file=sys.stderr)
        exit(3)
    except TypeError:
        exit(4)
# EOF
this is a script that implements a class and command to format a one-at-a-time sequence (the caller put()s each line one at a time with an empty put() call to indicate the end of the sequence of lines) into column oriented pages (the caller checks for a finished page via get() or print_ready_pages()).  the command works on stdin and stdout.  the geometry (3 numbers: gutter, width, height) is optional and it tries to use the controlling terminal geometry if not given. the order of forming the columns is top to bottom, on the left column first, adding each column on the right side until no more space is available for the width.

my thoughts for the future:
1. a generator version.
2. a means to have it print each finished page
3. headers/footers/sidings on each page, maybe with page numbers.
Tradition is peer pressure from dead people

What do you call someone who speaks three languages? Trilingual. Two languages? Bilingual. One language? American.
Reply


Messages In This Thread
colpage.py - by Skaperen - Apr-29-2017, 06:12 AM
RE: colpage.py - by volcano63 - Apr-29-2017, 09:04 AM
RE: colpage.py - by nilamo - Apr-29-2017, 04:57 PM
RE: colpage.py - by Skaperen - Apr-30-2017, 02:41 AM
RE: colpage.py - by Skaperen - Apr-30-2017, 04:10 AM
RE: colpage.py - by nilamo - Apr-30-2017, 04:31 AM
RE: colpage.py - by Mekire - Apr-30-2017, 04:42 AM
RE: colpage.py - by Skaperen - Apr-30-2017, 05:24 AM
RE: colpage.py - by Skaperen - Apr-30-2017, 05:10 AM
RE: colpage.py - by Mekire - Apr-30-2017, 05:14 AM
RE: colpage.py - by Skaperen - Apr-30-2017, 05:36 AM
RE: colpage.py - by Mekire - Apr-30-2017, 05:28 AM
RE: colpage.py - by Skaperen - May-02-2017, 04:31 AM
RE: colpage.py - by Mekire - Apr-30-2017, 05:46 AM
RE: colpage.py - by Skaperen - Apr-30-2017, 06:15 AM
RE: colpage.py - by Mekire - Apr-30-2017, 07:17 AM
RE: colpage.py - by Skaperen - Apr-30-2017, 09:42 AM
RE: colpage.py - by volcano63 - May-01-2017, 05:00 PM
RE: colpage.py - by Mekire - May-01-2017, 10:39 PM
RE: colpage.py - by volcano63 - May-01-2017, 10:47 PM

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020