How to use ImageWriter special options from barcode library in Python 3? - python

I've generated a barcode PNG image with the following code:
import barcode
from barcode.writer import ImageWriter
image_writer = ImageWriter()
ean = barcode.get('ean13', '123456789102', writer=image_writer)
filename = ean.save('ean13')
But I want to change the human readable text. I want to put it closer to the barcode and change the font size.
So. according to the library docs: https://python-barcode.readthedocs.io/en/stable/writers/index.html:
All writer take the following options (specified as keyword arguments
to Barcode.save(filename, options) or set via
Writer.set_options(options), where options is a dictionary where keys
are option names and values are option values to be set).
If I do this:
import barcode
from barcode.writer import ImageWriter
options = {
'font_size': 20,
'text_distance': 1.0,
}
image_writer = ImageWriter()
ean = barcode.get('ean13', '123456789102', writer=image_writer)
filename = ean.save('ean13', options)
It works as expected. The problem is that in the future I won't want to generate an image file stored on disk, and write method does not accept options.
So I'm trying the other way, using set_options, which theoretically works for every kind of Writer as they inherit from BaseWriter, but if I do this:
import barcode
from barcode.writer import ImageWriter
options = {
'format': 'PNG',
'font_size': 20,
'text_distance': 1.0,
}
image_writer = ImageWriter().set_options(options)
ean = barcode.get('ean13', '123456789102', writer=image_writer)
filename = ean.save('ean13')
Not only I don't get what I want but the generated image file is a SVG.
Why is this happening? Any ideas of how can I achieve my purpose?

did you try using render?
from barcode import Code128
from barcode.writer import ImageWriter
render_options = {
"module_width": 0.25,
"module_height": 6,
"write_text": False,
"module_width": 0.25,
"quiet_zone": 0.1,
}
barcode=Code128("123456789",writer=ImageWriter()).render(render_options)

Related

Reportlab alignment & width issues

I’m having some issues with reportlab and writing a PDF. When the PDF is written it only consumes a little less than 2/3 of the page width (letter). The heading, for example, wraps and never makes it past the halfway point of the document.
I’m at a loss for how to get my tables and paragraph to use the full width of the page.
Any insight is greatly appreciated.
Thank you in advance.
import io
import os
from django.core.files.base import ContentFile
from jsignature.utils import draw_signature
from reportlab.lib.pagesizes import letter
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.enums import TA_RIGHT, TA_CENTER, TA_LEFT
from reportlab.lib.units import inch
from reportlab.pdfgen import canvas
from reportlab.platypus import SimpleDocTemplate, Paragraph, Table
from PIL import Image
# create pdf with table and paragraphs
def create_pdf(participant):
# create a file-like buffer to receive PDF data
buffer = io.BytesIO()
# define styles
styles = getSampleStyleSheet()
style_general = ParagraphStyle(
name='left',
parent=styles['Normal'],
fontSize=12,
fontName='Helvetica',
alignment=TA_LEFT)
style_image = ParagraphStyle(
name='left',
fontSize=30,
parent=styles['Normal'],
alignment=TA_LEFT)
style_heading = ParagraphStyle(
name='center',
fontSize=18,
fontName='Helvetica-Bold',
parent=styles['Heading1'],
leading=18,
alignment=TA_CENTER)
# create a simple document with page size in buffer
doc = SimpleDocTemplate(buffer, pagesize=letter, author='Me')
# create a list of paragraphs
AllParagraphs = []
# convert png image to jpeg
jpeg_image = get_jpeg_image(participant)
# add rows and columns so that the data can align
table_data = [
[Paragraph("My Heading - It should span the full page width", style_heading)],
[
Paragraph('Name:', style_general),
Paragraph(
f'{participant.first_name} {participant.middle_initial} {participant.last_name}',
style_general)
],
[
Paragraph(f'Signature:', style_general),
# image height of 30 to prevent overlapping since fontSize is 30,
# image width of double to maintain aspect ratio
Paragraph(
"<img src='{0}' valign='middle' width=60 height=30 />".format(
jpeg_image),
style_image)
]
]
# set rows and columns into Table object
table_element = Table(table_data)
# add table to list of paragraphs
AllParagraphs.append(table_element)
# build document with list of paragraphs
doc.build(AllParagraphs)
# get content of buffer
buffer.seek(0)
pdf_data = buffer.getvalue()
# save buffer content to django File object
file_data = ContentFile(pdf_data)
# name pdf file
file_data.name = f'{participant.last_name}.pdf'
# delete jpeg file
os.remove(jpeg_image)
# save pdf file to parent model
participant.pdf = file_data
participant.save()
For those interested in the answer: adjusting the table style to span multiple columns was the right approach.
In this case a table is being used to best align the signature elements, so spanning the columns similar to how you would in html or css is the solution.
...
# existing code for placement reference
# set rows and columns into Table object
table_element = Table(table_data)
# add table to list of paragraphs
# new code for spanning
# style table object to make single cells span both columns
table_element.setStyle(TableStyle([
('SPAN', (0, 0), (1, 0)),
]))

Pythonic way to load image resources

I have a file called resources.py which loads images to be used in the main project.
So far the code looks like this:
import pyglet
pyglet.resource.path = ["../resources", "C:/users/____/Pictures/useful icons"]
pyglet.resource.reindex()
checkbox_unchecked = pyglet.resource.image("checkbox_unchecked.png")
checkbox_checked = pyglet.resource.image("checkbox_checked.png")
checkbox_unchecked_dark = pyglet.resource.image("checkbox_unchecked_dark.png")
checkbox_checked_dark = pyglet.resource.image("checkbox_checked_dark.png")
checkbox_unchecked_thick = pyglet.resource.image("checkbox_unchecked_thick.png")
checkbox_checked_thick = pyglet.resource.image("checkbox_checked_thick.png")
checkbox_unchecked_disabled = pyglet.resource.image("checkbox_unchecked_disabled.png")
checkbox_checked_disabled = pyglet.resource.image("checkbox_checked_disabled.png")
I thought that this is an unwieldy way to do it, so what came to my mind is something like:
import pyglet
pyglet.resource.path = ['../resources', "C:/users/____/Pictures/useful icons"]
pyglet.resource.reindex()
images = ["checkbox_unchecked.png", "checkbox_checked.png", ...]
for image in images:
exec(f'{image} = pyglet.resource.image("{image}")')
This of course uses the exec function which I know is frowned upon as there is usually a better way of doing it. The only other way I can see of doing it is creating a dictionary instead.
Like so:
import pyglet
pyglet.resource.path = ['../resources', "C:/users/____/Pictures/useful icons"]
pyglet.resource.reindex()
images = ["checkbox_unchecked.png", "checkbox_checked.png", ...]
imageDict = {}
for image in images:
imageDict[image] = pyglet.resource.image(image)
Which of these (or other methods) is the most DRY-complient and
comprehensible way to load the images?
You might consider a dictionary comprehension in combination with the pathlib module so that when you call keys from the dictionary you don't have to call them with the extension
from pathlib import Path
import pyglet
pyglet.resource.path = ['../resources', "C:/users/____/Pictures/useful icons"]
pyglet.resource.reindex()
images = ["checkbox_unchecked.png", "checkbox_checked.png", ...]
imageDict = { Path(image).stem: pyglet.resource.image(image) for image in images }
Then you would get your images out with:
imageDict['checkbox_unchecked']
You can use your dictionary solution to get what you originally wanted by using globals(), which is a dict of all the global variables.
for image in images:
globals()[image.split('.')[0]] = pyglet.resource.image(image)
Or:
globals().update((image.split('.')[0], pyglet.resource.image(image)) for image in images)

How can I constantly check if a JSON file is updated in Python?

I'm making a clock using Tkinter and Python.
What I want to achieve is having the clock run and automatically check if the JSON file (which contains the clock's visual settings) has been updated. If the JSON file has been updated, then the clock will update live.
However, this isn't the case. What I have to do is update the JSON file, close the clock program, then re-open the clock program. Only in this case will the changed JSON settings take place.
clock.py
from tkinter import *
from datetime import datetime
from settings import *
# Updates the program and clock settings
def tick():
time_string = datetime.now().strftime(time_format)
date_string = datetime.now().strftime(date_format)
root.config(bg=background_color)
container.configure(bg=background_color)
current_time.configure(text=time_string,
font=(time_font, time_size, time_weight, time_slant),
fg=time_color,
bg=background_color)
current_date.configure(text=date_string,
font=(date_font, date_size, date_weight, date_slant),
fg=date_color,
bg=background_color)
current_time.after(1, tick)
# TKInterface
root = Tk()
root.title(window_title)
# Binds 'Esc' key to exit program
root.bind('<Escape>', exit)
# Runs program in full-screen
if full_screen:
root.attributes('-fullscreen', True)
root.config(cursor='none')
# Creates container to hold time and date
container = Frame(root)
current_time = Label(container)
current_date = Label(container)
container.pack(expand=True)
current_time.pack()
current_date.pack()
tick()
root.mainloop()
settings.py
import os
import json
with open('settings.json') as json_settings:
settings = json.load(json_settings)
# Window
full_screen = settings['window']['full_screen']
window_title = settings['window']['window_title']
# Background
background_color = settings['background']['color']
# Time
time_font = settings['time']['font']
time_size = settings['time']['size']
time_weight = settings['time']['weight']
time_slant = settings['time']['slant']
time_color = settings['time']['color']
time_format = settings['time']['format']
# Date
date_font = settings['date']['font']
date_size = settings['date']['size']
date_weight = settings['date']['weight']
date_slant = settings['date']['slant']
date_color = settings['date']['color']
date_format = settings['date']['format']
settings.json
{
"window": {
"full_screen": false,
"window_title" : "chronoberry"
},
"background": {
"color": "black"
},
"time": {
"font": "arial",
"size": 70,
"weight": "bold",
"slant": "roman",
"color": "white",
"format": "%-I:%M:%S %p"
},
"date": {
"font": "arial",
"size": 20,
"weight": "normal",
"slant": "roman",
"color": "white",
"format": "%A, %B %-d %Y"
}
}
Desired Effect:
If I change the background color in my JSON file and save it, my clock should be able to update its color during run-time.
Before changing the background color
After changing the background color
What I've Tried:
Re-importing the settings.py module using importlib.reload(), but the settings.py module isn't a valid argument.
Opening settings.json, reading from it, closing it, then opening it again. However, once a file is closed, it's impossible to open it again.
Your settings module is essentially a module of constants, which makes it quite hard to reuse. While you could do something hacky to force the reload like:
def tick():
import settings, importlib
importlib.reload(settings)
from settings import *
# ... rest of tick code ...
that's going to be both inefficient and a terrible misuse of the import mechanism (now instead of reading one file, you're reading two, the module and the JSON it depends on).
Instead, I'd recommend making settingss a little more reusable with a function that can read the data and cache it, rather than a bunch of globals:
import os
import json
class Settings:
SETTINGS_FILE = 'settings.json'
def __init__(self):
self._load_settings()
def update_settings(self):
if self._last_update != os.stat(self.SETTINGS_FILE).st_mtime:
self._load_settings()
return True
return False
def _load_settings(self):
with open(self.SETTINGS_FILE) as json_settings:
settings = json.load(json_settings)
self._last_update = os.fstat(json_settings.fileno()).st_mtime
# Window
self.full_screen = settings['window']['full_screen']
self.window_title = settings['window']['window_title']
# Background
self.background_color = settings['background']['color']
# Time
self.time_font = settings['time']['font']
self.time_size = settings['time']['size']
self.time_weight = settings['time']['weight']
self.time_slant = settings['time']['slant']
self.time_color = settings['time']['color']
self.time_format = settings['time']['format']
# Date
self.date_font = settings['date']['font']
self.date_size = settings['date']['size']
self.date_weight = settings['date']['weight']
self.date_slant = settings['date']['slant']
self.date_color = settings['date']['color']
self.date_format = settings['date']['format']
Now your clock module can import settings, construct a Settings object up front, and at each tick call update_settings() on it. If update_settings returns True, it should also reapply the configuration. The code would need to qualify the various names, so instead of just saying date_color, you'd put:
mysettings = Settings()
at the top level, and refer to date_color with:
mysettings.date_color
but that's a small price to pay to improve the code.
How serious is this code? You could just check for the file's last modified time at some interval with os.stat(path_to_file).st_mtime and refresh your interface if it's later than the last time you checked. Quick and dirty.
You have a couple of options. 1 is the option benas has suggested. Every second or so check the last modified time of the json file to see if it has changed.
Another option is to use a package such as PyiNotify. This package can identify events that occur on files and folders. 1 such event is the IN_MODIFY event which triggers when a file has been modified. A list of events can be found here.
Firstly you would need to install the package using pip:
pip install pyinotify
The following is example code (almost entirely from their documentation)
import pyinotify
# The watch manager stores the watches and provides operations on watches
wm = pyinotify.WatchManager()
wm.add_watch('/path/to/file', mask, rec=True)
mask = pyinotify.IN_MODIFY # watched events
class EventHandler(pyinotify.ProcessEvent):
def process_IN_MODIFY(self, event):
print "Modifying:", event.pathname

How do I add a page background image in pylatex?

I have written the following code, and have given the latex commands for drawing background, by using the NoEscape module.
I have an image reportbg.png in the same directory as the program. Now, I want this image to appear as a background in all pages of the report.
types = ('../Faults/*.png', '../Faults/*.jpg')
imgnames = []
for files in types:
imgnames.extend(natsort.natsorted(glob.glob(files)))
geometry_options = { "head": "30pt",
"margin": "0.3in",
"top": "0.2in",
"bottom": "0.4in",
"includeheadfoot": True}
doc = Document(geometry_options=geometry_options)
first_page = PageStyle("firstpage")
doc.preamble.append(first_page)
doc.change_document_style("firstpage")
new_comm1 = NoEscape(r'\usepackage{wallpaper}')
doc.append(new_comm1)
new_comm2 = NoEscape(r'\CenterWallPaper{reportbg.png}')
doc.append(new_comm2)
with doc.create(Section('Faults identified')):
doc.append("Report")
with doc.create(Subsection('Fault pictures')):
for i,imgname in enumerate(imgnames):
with doc.create(Figure(position='h!')) as f_pic:
f_pic.add_image(imgname, width='220px')
f_pic.add_caption('Height: '+str(56)+', Angle: '+str(20))
doc.append('Some regular text')
However, I got the following error:
! LaTeX Error: Can be used only in preamble.
See the LaTeX manual or LaTeX Companion for explanation.
Type H <return> for immediate help.
...
l.23 \usepackage
{wallpaper}%
! Undefined control sequence.
l.24 \CenterWallPaper
{reportbg.png}%
<../Faults/1.jpg, id=1, 1927.2pt x 1084.05pt> <use ../Faults/1.jpg>
<../Faults/2.jpg, id=2, 1927.2pt x 1084.05pt> <use ../Faults/2.jpg>
<../Faults/3.jpg, id=3, 1927.2pt x 1084.05pt> <use ../Faults/3.jpg>
<../Faults/4.jpg, id=4, 1003.75pt x 1003.75pt> <use ../Faults/4.jpg>
LaTeX Warning: '!h' float specifier changed to '!ht'.
To implement a Background Image on all the pages of the document, you can generate first the PDF document in pylatex and then add the image as a watermark with PyPDF2. To do so, you need to have your 'reportbg.png' image into a pdf format (reportbg.pdf).
Here's a modified example based on the pylatex documentation (https://jeltef.github.io/PyLaTeX/current/examples/basic.html):
CODE
from pylatex import Document, Section, Subsection, Command
from pylatex.utils import italic, NoEscape
import PyPDF2
class Document_Watermark():
def __init__(self, doc):
self.doc = doc
self.fill_document()
self.create_document()
self.Watermark()
def fill_document(self):
"""Add a section, a subsection and some text to the document.
:param doc: the document
:type doc: :class:`pylatex.document.Document` instance
"""
with self.doc.create(Section('A section')):
self.doc.append('Some regular text and some ')
self.doc.append(italic('italic text. '))
with self.doc.create(Subsection('A subsection')):
self.doc.append('Also some crazy characters: $&#{}')
def create_document(self):
# Add stuff to the document
with self.doc.create(Section('A second section')):
self.doc.append('Some text.')
self.doc.generate_pdf('basic_maketitle2', clean_tex=False, compiler='pdflatex')
tex = self.doc.dumps() # The document as string in LaTeX syntax
def Watermark(self):
Doc = open('basic_maketitle2.pdf', 'rb')
pdfReader = PyPDF2.PdfFileReader(Doc)
pdfWatermark = PyPDF2.PdfFileReader(open('watermark3.pdf', 'rb'))
pdfWriter = PyPDF2.PdfFileWriter()
for pageNum in range(0, pdfReader.numPages):
pageObj = pdfReader.getPage(pageNum)
pageObj.mergePage(pdfWatermark.getPage(0))
pdfWriter.addPage(pageObj)
resultPdfFile = open('PDF_Watermark.pdf', 'wb')
pdfWriter.write(resultPdfFile)
Doc.close()
resultPdfFile.close()
# Basic document
doc = Document('basic')
# Document with `\maketitle` command activated
doc = Document()
doc.preamble.append(Command('title', 'Awesome Title'))
doc.preamble.append(Command('author', 'Anonymous author'))
doc.preamble.append(Command('date', NoEscape(r'\today')))
doc.append(NoEscape(r'\maketitle'))
Document_Watermark(doc)
The example watermark is this one: watermark3.pdf
The initial PDF document: basic_maketitle2.pdf
The final document: PDF_Watermark.pdf
PS: The watermark, the initial generated pdf and the .py file must be at the same directory. I couldn't upload the PDF files, because this is my first answer post and I'm not really sure how could I, but I share some images. I hope it will be helpful.
For more information, I suggest to read the following book: "Automate the boring stuff with Python", chapter 13, by Al Sweigart.

xlwings: set foreground text color from Python

I'm using xlwings on a Mac and would like to set the foreground color of text in a cell from Python. I see that range.color will change background color which I could use but it has an additional problem that the cell borders are overwritten by the new BG color.
Is there any way to change foreground text color from Python and/or prevent the cell borders being overwritten by a new BG color?
The new API is :
Range("A1").api.Font.ColorIndex = 3
xlwings is currently still a bit light on stylistic features. However, as described here, you can work around like this on Mac by accessing the underlying appscript object directly:
some_rgb_vale = (22, 22, 200)
xw.Range('A1').xl_range.font_object.color.set(some_rgb_vale)
Update:
Nowadays, this is now natively supported by xlwings including Hex color notation:
import xlwings as xw
book = xw.Book()
sheet = book.sheets[0]
sheet["A1"].value = "Some Text"
sheet["A1"].font.color = "#ff0000"
this worked for me
import xlwings as xw
import sys,os
FN = "test.xlsx"
path = os.path.abspath(os.path.dirname(sys.argv[0]))
fn = path + "/" + FN
wb = xw.Book (fn)
ws = wb.sheets["Tabelle1"]
# set background color
ws["B1"].value = "Field1"
ws["B1"].color = (255,255,204)
# set font to bold
ws["B2"].value = "Field2"
ws.range("B2").api.Font.Bold = True
# set font color to green
# color-index see here
ws["B3"].value = "Field3"
ws.range("B3").api.Font.ColorIndex = 4
This solution is working fine for me.
from xlwings.utils import rgb_to_int
import xlwings as xw
sht.range('A1').api.Font.Color = rgb_to_int((192, 192, 192))

Categories