#-*- coding: utf-8 -*- # Copyright 2008-2010 Calculate Ltd. http://www.calculate-linux.org # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from re import search, compile, S from cl_utils import _toUNICODE import cl_overriding import cl_lang import sys tr = cl_lang.lang() tr.setLocalDomain('cl_lib') tr.setLanguage(sys.modules[__name__]) def prettyColumnStr(*cols): '''Функция преобразования строк в текстовые колонки. Если указанный текст не помещается в колонку, то строка переносится на следующую этой же колонки перенос текста идет по словам, и текст выравнивается по ширине колонки за счет дополнительных пробелов между словами. Если в строке используется перенос строки, то текст переносится не просто на следующую строку, а также на следующую строку колонки, причем если используется \r текст выравнива- ется по ширине, а если \n, то просто перевод строки. Параметры: cols множестово пар: текст, ширина колонки, причем, если у последней колонки не указывать ширину, то она будет выведена вся. Возвращаемые параметры: строка, которую можно использовать для вывода на экран Пример: columnWrite( "Some text", 10, "Next column", 20 ) ''' # шаблон поиска переводов строк wherenr = compile( '[\n\r]', S ) retstr = "" # перевести кортеж в список, т.к. изменяется cols = list(cols) # перевести текст в юникод, заодно перевести числа в строку noconvert = False space = u' ' nospace = u'' for i in xrange(0,len(cols),2): cols[i] = _toUNICODE(cols[i]) # флаг "есть еще текст для вывода" repeat = True while repeat: # сбросить итератор на первый элемент q = 0 repeat = False # пока не закончили перебирать параметры (перебираем по парам) while q < len(cols): # если это последний параметр, и для него не указана ширина if q == len(cols)-1: # выводим его полностью не смотря на ширину окна retstr += cols[q] + " " cols[q] = '' else: # вывести часть строки не больше указанной ширины колонки partstr = cols[q][:cols[q+1]] # искать перевод строки с полученной части brfind = wherenr.search(partstr) # если это не последняя колонка if q + 2 < len(cols): # добавить разделитель между колонками cellspacing = space else: # разделитель не нужен cellspacing = nospace # если перевод строки найден, то if brfind != None: # для текущего вывода в колонку # берем часть строки до перевода partstr = partstr[:brfind.start()] # остальная часть идет в остаток (без перевода) cols[q] = cols[q][brfind.start()+1:] # # если используется перевод каретки # if brfind.group() == '\r': # # то выравниваем по ширине колонки # partstr = partstr.ljust(cols[q+1], ' ') # else: # # добавить отступы чтобы закончить колонку partstr = partstr.ljust(cols[q+1], ' ') # если взята часть строки elif len(partstr) == cols[q+1] and partstr != cols[q]: # если взята часть строки (разрыв в слове) if cols[q][cols[q+1]] != ' ': # ищем ближайший пробел справа spacepos = partstr.rfind(' ') # если пробел найти не удалось if spacepos == -1: # то на вывод идет часть строки равной ширине cols[q] = cols[q][cols[q+1]:] # если пробел найден else: # обрезаем строку до найденного пробела partstr = partstr[:spacepos] cols[q] = cols[q][spacepos+1:] # если взята часть строки (разрыв на пробеле) else: # ислючить переносной пробел cols[q] = cols[q][cols[q+1]+1:] # выровнить текст по ширине колонки partstr = partstr.ljust(cols[q+1], ' ') #partstr = justify(partstr, cols[q+1]) # остатки строки else: # добавить отступы чтобы закончить колонку partstr = partstr.ljust(cols[q+1], ' ') cols[q] = '' retstr+= partstr + cellspacingS # остальную часть строки оставить на следующую итерацию # если от строки что то осаталось if len(cols[q]) > 0: # отметить запуск еще одной итерации по параметрам repeat = True # следующая пара q += 2 # колонки отображены retstr += "\n" return retstr.encode('utf8') def columnStr(*cols): '''Вывод данных по колонкам, причем, если данные не вмещаются в указнаную колонку, то они переносятся на следующую строку в нужную колонку. В строку. Параметры: cols множестово пар: текст, ширина колонки, причем, если у последней колонки не указывать ширину, то она будет выведена вся. Возвращаемые параметры: строка, которую можно использовать для вывода на экран Пример: columnWrite( "Some text", 10, "Next column", 20 ) ''' retstr = "" # перевести кортеж в список, т.к. изменяется cols = list(cols) # перевести текст в юникод, заодно перевести числа в строку for i in xrange(0,len(cols),2): cols[i] = (str(cols[i])).decode('utf8') # флаг "есть еще текст для вывода" repeat = True while repeat: # сбросить итератор на первый элемент q = 0 repeat = False # пока не закончили перебирать параметры (перебираем по парам) while q < len(cols): # если это последний параметр, и для него не указана ширина if q == len(cols)-1: # выводим его полностью не смотря на ширину окна retstr += cols[q] + " " cols[q] = '' else: # вывести часть строки не больше указанной ширины колонки retstr+=(cols[q][:cols[q+1]].ljust(cols[q+1])).encode('utf8') \ + " " # остальную часть строки оставить на следующую итерацию cols[q] = cols[q][cols[q+1]:] # если от строки что то осаталось if len(cols[q]) > 0: # отметить запуск еще одной итерации по параметрам repeat = True # следующая пара q += 2 # колонки отображены retstr += "\n" return retstr def columnWrite(*cols): '''Вывод данных по колонкам, причем, если данные не вмещаются в указнаную колонку, то они переносятся на следующую строку в нужную колонку. Параметры: cols множестово пар: текст, ширина колонки, причем, если у последней колонки не указывать ширину, то она будет выведена вся. Пример: columnWrite( "Some text", 10, "Next column", 20 ) ''' # перевести кортеж в список, т.к. изменяется cols = list(cols) # перевести текст в юникод, заодно перевести числа в строку for i in xrange(0,len(cols),2): cols[i] = (str(cols[i])).decode('utf8') # флаг "есть еще текст для вывода" repeat = True while repeat: # сбросить итератор на первый элемент q = 0 repeat = False # пока не закончили перебирать параметры (перебираем по парам) while q < len(cols): # если это последний параметр, и для него не указана ширина if q == len(cols)-1: # выводим его полностью несмотря на ширину окна cl_overriding.printSUCCESS(cols[q].encode('utf8'), printBR=False) cols[q] = '' else: # вывести часть строки не больше указанной ширины колонки cl_overriding.printSUCCESS(\ (cols[q][:cols[q+1]].ljust(cols[q+1])).encode('utf8') + " ", printBR=False) # остальную часть строки оставить на следующую итерацию cols[q] = cols[q][cols[q+1]:] # если от строки что то осаталось if len(cols[q]) > 0: # отметить запуск еще одной итерации по параметрам repeat = True # следующая пара q += 2 # колонки отображены cl_overriding.printSUCCESS('') def justify(s,width): '''Выровнить текст по ширине Параметры: s выводимая строка width ширина на которую надо выровнить строку Возвращаямые параметры: Выровненная строка ''' # если подана строка без пробелов - прекратить обработку if s.find(' ') == -1: return s pos = 0 # переводим в юникод для правильного вычисления длины try: s = s.decode( 'utf-8' ) # пропуск если это не utf-8 except UnicodeEncodeError: pass # пока длина строки меньше указанной while len(s) < width: # находим очередной пробел pos = s.find( ' ', pos ) # если не найден искать сначала if pos == -1: pos = s.find(' ') # вставить в позицию еще один пробел s = s[:pos] +' ' +s[pos:] # оставить удвоенный пробел pos += 3 # вернуть строку в utf8 если она пришла в utf8 return s.encode('utf-8') class tableReport: """Класс для выдачи данных в табличном виде""" def __init__(self, title, headerList, dataList): # Заголовок self.title = title # Cписок элементов первой строки таблицы self.headerList = headerList # Список строк (каждая строка - список элементов) self.dataList = dataList # Список ширин колонок self.columnsWidth = self.getColumsnWidth() def getColumsnWidth(self): """Находит максимальную ширину каждой колонки результат список ширин колонок """ columnsWidth = [] lenCol = 0 for s in self.headerList: lenCol += 1 columnsWidth.append(len(_toUNICODE(s))) maxLenCol = lenCol # Вычисляем максимальное количество столбцов for s in self.dataList: lenS = len(s) if maxLenCol < lenS: maxLenCol = lenS if maxLenCol > lenCol: appCol = maxLenCol - lenCol # Добавляем элементы в список ширин for i in range(appCol): columnsWidth.append(0) # Вычисляем ширину столбцов for e in self.dataList: i = 0 for s in e: lenS = len(_toUNICODE(s)) if columnsWidth[i] < lenS: columnsWidth[i] = lenS i += 1 return columnsWidth def createFormatStr(self, listStr): """Создает список (текст, ширина ...)""" strList = [] lenStr = len(listStr) for i in range(len(self.columnsWidth)): if lenStr > i: strList.append(listStr[i]) else: strList.append("") strList.append(self.columnsWidth[i]) return strList def printReport(self,printRows=True): """Напечатать данные в табличном виде""" if self.title: cl_overriding.printSUCCESS(self.title) listStrSep = [] for lenCol in self.columnsWidth: listStrSep.append("-"*lenCol) printData = "" printData += columnStr(*self.createFormatStr(listStrSep)) printData += columnStr(*self.createFormatStr(self.headerList)) printData += columnStr(*self.createFormatStr(listStrSep)) for s in self.dataList: printData += columnStr(*self.createFormatStr(s)) printData += columnStr(*self.createFormatStr(listStrSep)) if printData[-1] == "\n": printData = printData[:-1] lines = printData.splitlines() lenCols = map(lambda x: len(x), lines[0].strip().split(" ")) convLines = [] lenLines = len(lines) for i in range(lenLines): char = " | " if i == 0 or i == 2 or i == lenLines-1: char ="-+-" convLines.append(self._insertStrChar(lines[i], lenCols, char)) cl_overriding.printSUCCESS("\n".join(convLines)) if printRows: cl_overriding.printSUCCESS("(%s %s)"% (len(self.dataList), _("rows"))) def _insertStrChar(self, line, lenCols, char): """Вставляет несколько символов char в указанные позиции сначала строки будет вставлено char[1:] в конце строки char[:-1] """ lineUnicode = _toUNICODE(line) prevPos = 0 convLine = char[1:] lenLenCols = len(lenCols) for i in range(lenLenCols): pos = lenCols[i] + prevPos if i == lenLenCols-1: insertChar = char[:-1] else: insertChar = char convLine += lineUnicode[prevPos:pos] + insertChar prevPos = pos + 1 return convLine.encode("UTF-8")