Reportlab drawString is being overwritten by Story - python

I'm using reportlab to create a PDF document.
I'm using drawString to realize a footer on the last page and page numbers on every page. Like that:
if self._pageNumber < page_count:
self.drawRightString(19.3 * cm, 1 * cm, "page %d of %d" % (self._pageNumber, page_count))
if self._pageNumber == page_count:
self.drawRightString(19.3 * cm, 2 * cm, "page %d of %d" % (self._pageNumber, page_count))
self.drawString(9.25 * cm, 2 * cm, "www.company.com")
self.drawString(1.2 * cm, 2 * cm, "company name")
self.drawString(1.2 * cm, 1.6 * cm, "company address")
I then append tables to the story, which are supposed to be written to the document and should automatically break onto a new page if they are longer than one page, which all works fine.
My problem now is, that I always print the footer on the last page only, and if the table is not long enough to break onto another page, it overwrites the footer, so the table sits above the company details. This is of course not what I want to happen.
My idea now was that I could include a bottom margin or something like that to put some kind of border around the footer, but every attempt so far failed.
Thanks in advance!

Related

Display ellipsis when Rich Layout scrolls past end of screen/console

Is it possible to configure rich.Layout() to indicate when the number of items in the layout has exceeded the display size of the current layout?
I would like to be able to tell programmatically when the code is attempting to display too many items for the current Table/Layout to display in order to display ellipsis or a message such as "...and 200 further items". This would allow the program to alert the user that some items are not being displayed.
There is a size attribute in Layout, but that value appears to be an input to constrain the layout to a fixed size rather than an indicator of the current Layout size.
In my current application, I would rather not constrain the size of the Layout in order to use the full available layout size, whatever that value may be.
#!/usr/bin/env python3
from rich.console import Console
from rich.layout import Layout
from rich.table import Table
from rich.pretty import Pretty
# Make too many lines for the Layout/Table to display on the current screen size
MAX_LINES = 1000
def fill_table():
table = Table()
for li in range(MAX_LINES):
table.add_row(Pretty(li))
return table
console = Console()
layout = Layout(name="root")
layout["root"].update(fill_table())
console.print(layout)
I'm a bit late to the party, but I've just had to solve a similar issue. The trick is to use Layout's .render() function like so:
#!/usr/bin/env python3
from rich.console import Console
from rich.layout import Layout
from rich.table import Table
from rich.pretty import Pretty
from rich import print as pprint
# Make too many lines for the Layout/Table to display on the current screen size
MAX_LINES = 1000
def fill_table(max_height):
rows = [Pretty(li) for li in range(MAX_LINES)]
# Subtract 4 lines - that's how many the table's header and footer takes
n_rows = max_height - 4
if len(rows) > n_rows:
rows = rows[: n_rows - 1] + [f"...and {len(rows) - n_rows + 1} further items"]
table = Table()
for row in rows:
table.add_row(row)
return table
console = Console()
layout = Layout(name="root")
render_map = layout.render(console, console.options)
pprint("Region of the layout:", render_map[layout].region)
table = fill_table(render_map[layout].region.height)
layout["root"].update(table)
console.print(layout)
I didn't figure out how to measure the Table directly, but measuring Layout does the trick. I still needed to manually input the number of rows that the table's header+footer takes, which might fail if there is a multi-line column name.
EDIT: I've posted an answer to a similar problem which also takes into account multi-line rows.

Hollow square function with two inputs?

I've been struggling to create a function that can put out a default hollow square of 5x5 but then also take in 2 inputs.
Here's the question:
create a charsqr.py function that when imported will result in the following:
Program:
def main():
charsqr()
charsqr(4)
charsqr(3,'#')
main()
Result:
*****
* *
* *
* *
*****
****
* *
* *
****
###
# #
###
Here's my code:
def charsqr(chars,sym):
if type(chars) = int and type(sym)=str:
print(str(sym)*chars)
for i in range(chars-2):
print(str(sym)+" "*(chars-2)+str(sym))
print(str(sym)*chars)
else:
print("*"*5)
for i in range(5):
print("*"+" "*3+"*")
print("*"*5)
I've been fiddling around with the if statement as I've been getting "missing 2 positional arguments" error. I know how to create a hollow square, just not sure how to get the program to print the default square and then a square with the given "chars" without needing a "sym".
Any feedback is appreciated ! :)
You're complicating this way too much, a simple:
def draw_square(size=5, edge="*", fill=" "):
hollow = size - 2 # just so we don't calculate the hollow part each time
print(edge * size) # top part
for _ in range(hollow): # iterate until the last line
print(edge + fill * hollow + edge) # middle part
print(edge * size) # bottom part
Should do the trick. You can even change the 'hollow' part to some character.
You apparently want a function that uses an asterisk if no character is stated, and uses size 5 if no size is stated. That is what default parameters are for in Python:
def charsqr(chars=5, sym='*'):
Then calling charsqr() is the same as charsqr(5, '*'), and calling charsqr(4) is the same as charsqr(4, '*'). Calling charsqr(3, '#') uses no defaults.
Note that the code you show has bad indentation and will give a syntax error. Indent the lines of your function (everything but the def line). Also replace the = symbols in your if line with ==. With those three changes, your code works and gives the desired output.

Make text in a tkinter Listbox fit inside?

I've looked everywhere for a fix to this. I stumbled across this:
How to fit Tkinter listbox to contents
But this question is asking the reverse of what I want. I want the box to remain the size I've set it to, but the text runs off of the side like in the screenshot from the above linked question. Is there anyway to force a \n to be added to the string once its character count reaches the length of the listbox?
Also, I apologize if something is wrong with the format of my question, I've never posted here before.
class Stars(Tk):
def __init__(self):
Tk.__init__(self)
self.feed = Listbox(self, width = 55 , height = 31, relief = SUNKEN, borderwidth = 3)
self.feed.grid(row = 1, column = 2, columnspan = 2)
def simulate(self):
self.mass = eval(self.massEntry.get())
self.feed.insert(END, 'Star to be created with mass of {} * 10^30 kg; {} solar masses.'.format(1.98855 * self.mass, self.mass))
self.feed.insert(END, '0 years: Protostar formed in an interstellar gas cloud, and begins to compress due to gravity.')
This is all of the relevant code (trying to make a stellar evolution simulation). This is what it looks like when run, with the problem circled in red:
http://imgur.com/dZCYe6s
No, there is no way to have a Listbox wrap the text. If you want to support wrapping, use a Text widget. If you want to select items like in a listbox, you can add some custom bindings to do that.

Generating Radiobutton grid menu from an Array in Tkinter, Python

Edit: Sorry I can't answer my own post since I'm new but I figured it out: If you remove the line "tki.Button(master,..." (2nd to last code line), then the code runs perfectly fine. I guess the grid and the button don't work the way I put it.
sorry to bother but I'm having a little trouble figuring out what's off here. Basically I have an array that I want to loop through and set each of the values as a radiobutton IN A GRID. Later I'm going to loop through several arrays to generate a larger grid menu, but I can probably figure that out once I get this first loop working.
Here is my code:
import Tkinter as tki
master = tki.Tk()
frm = tki.Frame(master, bd = 16, relief = "sunken")
frm.grid()
tType = tki.StringVar()
tColumn = tki.IntVar()
tRow = tki.IntVar()
compType = ["iMac ", "Mac Mini ", "Mac Pro ", "Macbook ", "Macbook Air ", "Macbook Pro "]
tColumn.set(0)
tRow.set(0)
def radioCreate(typeArray):
for t in typeArray:
b = tki.Radiobutton(frm, text = t, variable = tType)
b.config(indicatoron = 0, bd = 4, width = 16, value = t)
b.grid(row = tRow.get(), column = tColumn.get())
tRow.set((tRow.get() + 1)) #increment tRow for next run-through
def p():
print tType.get()
radioCreate(compType)
tki.Button(master, command = p, text = "Display").pack()
master.mainloop()
Now remember, I'm trying to get this working in a grid, because I'm going to populate other columns with other data from different arrays.
The problem is i. These two lines:
frm.grid()
...
tki.Button(...).pack()
While it's perfectly acceptable to use pack and grid in the same application, you can't use them on two widgets that share the same master.
Grid will potentially change the size of a widget or its master depending on its options. Pack will notice the change, and may itself try to resize one or more widgets and/or the master based on its options. Grid will notice the change, and may resize... Pack will notice the change and resize, ...

How to print out status bar and percentage?

To implement a status bar like below:
[========== ] 45%
[================ ] 60%
[==========================] 100%
I want to this to be printed out to stdout, and keep refreshing it, not print to another line. How to do this?
The '\r' character (carriage return) resets the cursor to the beginning of the line and allows you to write over what was previously on the line.
from time import sleep
import sys
for i in range(21):
sys.stdout.write('\r')
# the exact output you're looking for:
sys.stdout.write("[%-20s] %d%%" % ('='*i, 5*i))
sys.stdout.flush()
sleep(0.25)
I'm not 100% sure if this is completely portable across all systems, but it works on Linux and OSX at the least.
There's a Python module that you can get from PyPI called progressbar that implements such functionality. If you don't mind adding a dependency, it's a good solution. Otherwise, go with one of the other answers.
A simple example of how to use it:
import progressbar
from time import sleep
bar = progressbar.ProgressBar(maxval=20, \
widgets=[progressbar.Bar('=', '[', ']'), ' ', progressbar.Percentage()])
bar.start()
for i in xrange(20):
bar.update(i+1)
sleep(0.1)
bar.finish()
To install it, you can use easy_install progressbar, or pip install progressbar if you prefer pip.
I found useful library tqdm (https://github.com/tqdm/tqdm/, previously: https://github.com/noamraph/tqdm). It automatically estimates time of completion and can be used as iterator.
Usage:
import tqdm
import time
for i in tqdm.tqdm(range(1000)):
time.sleep(0.01)
# or other long operations
Results in:
|####------| 450/1000 45% [elapsed: 00:04 left: 00:05, 99.15 iters/sec]
tqdm can wrap any iterable.
You can use \r (carriage return). Demo:
import sys
total = 10000000
point = total / 100
increment = total / 20
for i in xrange(total):
if(i % (5 * point) == 0):
sys.stdout.write("\r[" + "=" * (i / increment) + " " * ((total - i)/ increment) + "]" + str(i / point) + "%")
sys.stdout.flush()
Here you can use following code as a function:
def drawProgressBar(percent, barLen = 20):
sys.stdout.write("\r")
progress = ""
for i in range(barLen):
if i < int(barLen * percent):
progress += "="
else:
progress += " "
sys.stdout.write("[ %s ] %.2f%%" % (progress, percent * 100))
sys.stdout.flush()
With use of .format:
def drawProgressBar(percent, barLen = 20):
# percent float from 0 to 1.
sys.stdout.write("\r")
sys.stdout.write("[{:<{}}] {:.0f}%".format("=" * int(barLen * percent), barLen, percent * 100))
sys.stdout.flush()
Try this function using only the built-in sys:
import sys
def print_progress_bar(index, total, label):
n_bar = 50 # Progress bar width
progress = index / total
sys.stdout.write('\r')
sys.stdout.write(f"[{'=' * int(n_bar * progress):{n_bar}s}] {int(100 * progress)}% {label}")
sys.stdout.flush()
Usage:
foo_list = ["a", "b", "c", "d"]
total = len(foo_list)
for index, item in enumerate(foo_list):
print_progress_bar(index, total, "foo bar")
sleep(0.5)
enumerate(foo_list) gives you access to the index value during a loop.
Output:
[================================================ ] 96% foo bar
None of the answers posted completely addressed my needs. So I wrote my own as shown above. The features I needed:
Pass only the step number and total number of steps and it does the difficult job of calculating percentage complete.
Using 60 characters, divide them into 480 "ticks" to yield 0.21 % per tick. Without ticks, each character would only be 1.67 %.
Support for prepending a title.
Optional percentage complete at end of line.
Variable length progress bar that defaults to 60 characters or 480 "ticks".
Set progress bar color, the default is green.
How to Call the Progress Display
Calling the progress display is pretty straight forward. For the above sample .gif the function was called using:
percent_complete(step, total_steps, title="Convert Markdown")
The total_steps was about 2,500 for len(rows) in Stack Exchange Data Dump in CSV format. The step was the current row number as each Stack Exchange Markdown Q&A was converted to Kramdown (for GitHub Pages).
Python Code
The code is straight forward, but a bit longer than the other answers:
def percent_complete(step, total_steps, bar_width=60, title="", print_perc=True):
import sys
# UTF-8 left blocks: 1, 1/8, 1/4, 3/8, 1/2, 5/8, 3/4, 7/8
utf_8s = ["█", "▏", "▎", "▍", "▌", "▋", "▊", "█"]
perc = 100 * float(step) / float(total_steps)
max_ticks = bar_width * 8
num_ticks = int(round(perc / 100 * max_ticks))
full_ticks = num_ticks / 8 # Number of full blocks
part_ticks = num_ticks % 8 # Size of partial block (array index)
disp = bar = "" # Blank out variables
bar += utf_8s[0] * int(full_ticks) # Add full blocks into Progress Bar
# If part_ticks is zero, then no partial block, else append part char
if part_ticks > 0:
bar += utf_8s[part_ticks]
# Pad Progress Bar with fill character
bar += "▒" * int((max_ticks/8 - float(num_ticks)/8.0))
if len(title) > 0:
disp = title + ": " # Optional title to progress display
# Print progress bar in green: https://stackoverflow.com/a/21786287/6929343
disp += "\x1b[0;32m" # Color Green
disp += bar # Progress bar to progress display
disp += "\x1b[0m" # Color Reset
if print_perc:
# If requested, append percentage complete to progress display
if perc > 100.0:
perc = 100.0 # Fix "100.04 %" rounding error
disp += " {:6.2f}".format(perc) + " %"
# Output to terminal repetitively over the same line using '\r'.
sys.stdout.write("\r" + disp)
sys.stdout.flush()
Python Code Notes
A few points:
The [ .... ] bracket placeholders requirement in the question are not necessary because there is the fill characters that serve the same purpose. This saves two extra characters to make the progress bar wider.
The bar_width keyword parameter can be used depending on screen width. The default of 60 seems a good fit for most purposes.
The print_perc=True keyword parameter default can be overridden by passing print_perc=False when calling the function. This would allow a longer progress bar.
The title="" keyword parameter defaults to no title. Should you desire one use title="My Title" and : will automatically be added to it.
When your program finishes remember to call sys.stdout.write("\r") followed by sys.stdout.flush() to clear the progress display line.
Summary
This answer is a bit longer than the others but it's important to note it's a full solution, not part of a solution that you need to add more code to.
Another point is this solution has no dependencies and nothing extra to install. The UTF-8 character set is supported by Python and gnome-terminal was no extra setup required. If you are using Python 2.7 you might require # -*- coding: utf-8 -*- after the shebang. IE as the second line of your program.
The function could be converted to a class with separate init, update, pause (for printing debug stuff to the screen), resume and close methods.
This function was converted from a bash script:
How to add a progress bar to a shell script?
The bash script would display Sony TV volume with libnotify-bin (pop-up bubble message) whenever TV volume was changed. If you are interested in a bash progress bar, please visit the Stack Overflow link.
Edit January 30, 2022
Change from 4 ticks to 8 ticks per character.
Remove breaks between full blocks.
Add color support.
def printProgressBar(value,label):
n_bar = 40 #size of progress bar
max = 100
j= value/max
sys.stdout.write('\r')
bar = '█' * int(n_bar * j)
bar = bar + '-' * int(n_bar * (1-j))
sys.stdout.write(f"{label.ljust(10)} | [{bar:{n_bar}s}] {int(100 * j)}% ")
sys.stdout.flush()
call:
printProgressBar(30,"IP")
IP | [████████████----------------------------] 30%
based on the above answers and other similar questions about CLI progress bar, I think I got a general common answer to all of them. Check it at https://stackoverflow.com/a/15860757/2254146
Here is a copy of the function, but modified to fit your style:
import time, sys
# update_progress() : Displays or updates a console progress bar
## Accepts a float between 0 and 1. Any int will be converted to a float.
## A value under 0 represents a 'halt'.
## A value at 1 or bigger represents 100%
def update_progress(progress):
barLength = 20 # Modify this to change the length of the progress bar
status = ""
if isinstance(progress, int):
progress = float(progress)
if not isinstance(progress, float):
progress = 0
status = "error: progress var must be float\r\n"
if progress < 0:
progress = 0
status = "Halt...\r\n"
if progress >= 1:
progress = 1
status = "Done...\r\n"
block = int(round(barLength*progress))
text = "\rPercent: [{0}] {1}% {2}".format( "="*block + " "*(barLength-block), progress*100, status)
sys.stdout.write(text)
sys.stdout.flush()
Looks like
Percent: [====================] 99.0%
If you are developing a command line interface, I suggest you to take a look at click which is very nice:
import click
import time
for filename in range(3):
with click.progressbar(range(100), fill_char='=', empty_char=' ') as bar:
for user in bar:
time.sleep(0.01)
Here the output you get:
$ python test.py
[====================================] 100%
[====================================] 100%
[========= ] 27%
As described in Mark Rushakoff's solution, you can output the carriage return character, sys.stdout.write('\r'), to reset the cursor to the beginning of the line. To generalize that solution, while also implementing Python 3's f-Strings, you could use
from time import sleep
import sys
n_bar = 50
iterable = range(33) # for demo purposes
n_iter = len(iterable)
for i, item in enumerate(iterable):
j = (i + 1) / n_iter
sys.stdout.write('\r')
sys.stdout.write(f"[{'=' * int(n_bar * j):{n_bar}s}] {int(100 * j)}%")
sys.stdout.flush()
sleep(0.05)
# do something with <item> here
To be pure python and not make system calls:
from time import sleep
for i in range(21):
spaces = " " * (20 - i)
percentage = 5*i
print(f"\r[{'='*i}{spaces}]{percentage}%", flush=True, end="")
sleep(0.25)
I came upon this thread today and after having tried out this solution from Mark Rushakoff
from time import sleep
import sys
for i in range(21):
sys.stdout.write('\r')
# the exact output you're looking for:
sys.stdout.write("[%-20s] %d%%" % ('='*i, 5*i))
sys.stdout.flush()
sleep(0.25)
I can say that this works fine on W7-64 with python 3.4.3 64-bit but only in the native console. However when using the built-in console of spyder 3.0.0dev, the line breaks are still/again present. As this took me some time to figure out, I'd like to report this observation here.
Easiest is still
import sys
total_records = 1000
for i in range (total_records):
sys.stdout.write('\rUpdated record: ' + str(i) + ' of ' + str(total_records))
sys.stdout.flush()
Key is to convert the integer type to string.
Building on some of the answers here and elsewhere, I've written this simple function which displays a progress bar and elapsed/estimated remaining time. Should work on most unix-based machines.
import time
import sys
percent = 50.0
start = time.time()
draw_progress_bar(percent, start)
def draw_progress_bar(percent, start, barLen=20):
sys.stdout.write("\r")
progress = ""
for i in range(barLen):
if i < int(barLen * percent):
progress += "="
else:
progress += " "
elapsedTime = time.time() - start;
estimatedRemaining = int(elapsedTime * (1.0/percent) - elapsedTime)
if (percent == 1.0):
sys.stdout.write("[ %s ] %.1f%% Elapsed: %im %02is ETA: Done!\n" %
(progress, percent * 100, int(elapsedTime)/60, int(elapsedTime)%60))
sys.stdout.flush()
return
else:
sys.stdout.write("[ %s ] %.1f%% Elapsed: %im %02is ETA: %im%02is " %
(progress, percent * 100, int(elapsedTime)/60, int(elapsedTime)%60,
estimatedRemaining/60, estimatedRemaining%60))
sys.stdout.flush()
return
This is quite a simple approach can be used with any loop.
#!/usr/bin/python
for i in range(100001):
s = ((i/5000)*'#')+str(i)+(' %')
print ('\r'+s),
Using #Mark-Rushakoff answer, I worked out a simpler approach, no need to call the sys library. It works with Python 3. Tested in Windows:
from time import sleep
for i in range(21):
# the exact output you're looking for:
print ("\r[%-20s] %d%%" % ('='*i, 5*i), end='')
sleep(0.25)
Try PyProg. PyProg is an open-source library for Python to create super customizable progress indicators & bars.
It is currently at version 1.0.2; it is hosted on Github and available on PyPI (Links down below). It is compatible with Python 3 & 2 and it can also be used with Qt Console.
It is really easy to use. The following code:
import pyprog
from time import sleep
# Create Object
prog = pyprog.ProgressBar(" ", " ", total=34, bar_length=26, complete_symbol="=", not_complete_symbol=" ", wrap_bar_prefix=" [", wrap_bar_suffix="] ", progress_explain="", progress_loc=pyprog.ProgressBar.PROGRESS_LOC_END)
# Update Progress Bar
prog.update()
for i in range(34):
# Do something
sleep(0.1)
# Set current status
prog.set_stat(i + 1)
# Update Progress Bar again
prog.update()
# Make the Progress Bar final
prog.end()
will produce exactly what you want (even the bar length!):
[=========== ] 45%
[=============== ] 60%
[==========================] 100%
For more options to customize the progress bar, go to the Github page of this website.
I actually made PyProg because I needed a simple but super customizable progress bar library. You can easily install it with: pip install pyprog.
PyProg Github: https://github.com/Bill13579/pyprog
PyPI: https://pypi.python.org/pypi/pyprog/
Here is something I have made using the solution by #Mark-Rushakoff. To adaptively adjust to the terminal width.
from time import sleep
import os
import sys
from math import ceil
l = list(map(int,os.popen('stty size','r').read().split()))
col = l[1]
col = col - 6
for i in range(col):
sys.stdout.write('\r')
getStr = "[%s " % ('='*i)
sys.stdout.write(getStr.ljust(col)+"]"+"%d%%" % (ceil((100/col)*i)))
sys.stdout.flush()
sleep(0.25)
print("")
Per Steven C. Howell's comment on Mark Rushakoff's answer
j = (i + 1) / n
stdout.write('\r')
stdout.write('[%-20s] %d%%' % ('='*int(20*j), 100*j))
stdout.flush()
where i is the current item and n is the total number of items
For Python 3.6 the following works for me to update the output inline:
for current_epoch in range(10):
for current_step) in range(100):
print("Train epoch %s: Step %s" % (current_epoch, current_step), end="\r")
print()
import progressbar
import time
# Function to create
def animated_marker():
widgets = ['Loading: ', progressbar.Bar('=', '[', ']', '-'), progressbar.Percentage()]
bar = progressbar.ProgressBar(max_value=200,widgets=widgets).start()
for i in range(200):
time.sleep(0.1)
bar.update(i+1)
bar.finish()
# Driver's code
animated_marker()
Here Is a simple progress bar code with 0 imports
#!/usr/bin/python3
def progressbar(current_value,total_value,bar_lengh,progress_char):
percentage = int((current_value/total_value)*100) # Percent Completed Calculation
progress = int((bar_lengh * current_value ) / total_value) # Progress Done Calculation
loadbar = "Progress: [{:{len}}]{}%".format(progress*progress_char,percentage,len = bar_lengh) # Progress Bar String
print(loadbar, end='\r') # Progress Bar Output
if __name__ == "__main__":
the_list = range(1,301)
for i in the_list:
progressbar(i,len(the_list),30,'■')
print("\n")
Progress: [■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■]100%

Categories