I have a textview widget and I can apply tags such as bold, underline etc.
self.tags = {}
self.tags['bold'] = self.textbuffer.create_tag("bold", weight=Pango.Weight.BOLD)
self.tags['italic'] = self.textbuffer.create_tag("italic", style=Pango.Style.ITALIC)
self.tags['underline'] = self.textbuffer.create_tag("underline", underline=Pango.Underline.SINGLE)
self.tags['ubuntu'] = self.textbuffer.create_tag("ubuntu", family = "Ubuntu Mono")
self.tags['size'] = self.textbuffer.create_tag("size", font_desc=Pango.FontDescription.from_string("32"))
The problem is with the last one, If I apply the font size tag, then the rest don't work.
The function that handles the insert-text signal is the following :
def insert_with_tags(self, buffer, start_iter, data, data_len):
if data_len == 1:
start_iter.backward_char()
end = self.textbuffer.props.cursor_position
end_iter = self.textbuffer.get_iter_at_offset(end)
self.textbuffer.apply_tag(self.tags['size'], start_iter, end_iter)
for tag in self.format_toolbar.buttons:
if self.format_toolbar.buttons[tag].get_active():
self.textbuffer.apply_tag(self.tags[tag], start_iter, end_iter)
If I remove the line that applies the font size everything works, but I want to let the user to be able to modify the font (here im testing it with just 1 font, but its the same when I add tags for the rest) while typing and not just modify it for the entire buffer.
Related
I am trying to edit a QTextEdit named self.chatTextField to display the HTML preview in a live format. For example if I do "" I want it to not show the tag, but bold the text. I am able to have this work if I use textBrowswer as a previewer, but I would like it all to be in one widget.
As you can see in the image above, I want match the part the displays the HTML as final product and not the tags. Is this possible?
I have tried various ways to convert .toPlainText() and then adding an HTML header and footer. I have tried to .insertHtml() and .setHtml(). I have tried to do tricks with open and close carrots, but nothing seems to be working. I am either getting it to simply remove all HTML (anything with carrots/tags) or it is causing recursion errors.
This is the only section of code that is dealing with updating the HTML. If you need further code please let me know. I can provide UI file as well.
def printMaster(self):
global my_text
if self.chatTextField.toPlainText() != "":
oldText = self.chatTextField.toPlainText()
newText = self.textBrowser.toPlainText()
if oldText != newText and oldText != my_text:
self.textBrowser.setText(self.chatTextField.toPlainText())
my_text = self.chatTextField.toPlainText()
if my_text.endswith('>'):
#my_text = self.chatTextField.toHtml()
# self.chatTextField.clear()
print(my_text)
# self.chatTextField.setHtml(my_text)
QCoreApplication.processEvents()
# cursor = self.chatTextField.textCursor()
# pos = cursor.selectionEnd()
#
# print(self.chatTextField.toHtml())
#
# my_text = self.chatTextField.toHtml()
#
# self.chatTextField.clear()
# # self.chatTextField.insertHtml(str(my_text))
#
# if my_text.endswith('<'):
# my_text = my_text + "<"
# self.chatTextField.setHtml(my_text)
# print(True)
# else:
# self.chatTextField.insertHtml(my_text)
#
#
# cursor.setPosition(pos)
#
# self.chatTextField.setTextCursor(cursor)
#
#
# QCoreApplication.processEvents()
#
# # self.chatTextField.show()
I'm trying to change the color (Red) of the font that will be highlighted. The problem I'm facing is, as soon as I highlight the first word, all the following text becomes Red.
def cursorPosition(self):
logging.debug("Cursor Positiong changed")
self.cursor = self.ui.captureWindow.textCursor()
if self.cursor.hasSelection():
fmt = QTextCharFormat()
fmt.setForeground(Qt.red)
logging.debug(self.cursor.selectedText())
# We insert the new text, which will override the selected text
self.cursor.insertText(self.cursor.selectedText(), fmt)
# txt = '<p style="color:red;">' + self.cursor.selectedText() + '</p> '
# self.cursor.insertHtml(txt)
# And set the new cursor
self.ui.captureWindow.setTextCursor(self.cursor)
I've tried doing it with two methods, insertText and insertHtml.
insertHtml works if there is a space after the closing tag. Remove that space, it behaves the same way as insertText, everything will be red after the first highlight.
Here's my code. My first function is based on the /Lib/site-packages/reportlab/lib/styles.py source code, to create an array of style:
def create_styles(p_tuples):
retour = StyleSheet1()
parent = None
for p_name, font_name, font in p_tuples:
# (!) change path if Linux:
ttf_file = "C:/Windows/Fonts/{}.ttf".format(font)
pdfmetrics.registerFont(TTFont(font_name, ttf_file))
if parent is None:
p = ParagraphStyle(name=p_name,
fontName=font_name,
fontSize=10,
leading=12)
retour.add(p)
parent = p
else:
retour.add(ParagraphStyle(name=p_name,
parent=parent,
fontName=font_name,
fontSize=10,
leading=12))
return retour
Then I build my own array with my installed fonts:
def render_to_response(self, context, **response_kwargs):
# this is a response for Django, but the question is about styles
response = HttpResponse(content_type='application/pdf; charset=utf-8')
# ! Hint : no filename -> the browser extracts it from the URL!
# -> create URLs like http://pdfreportlab.com/extract/myfilename.pdf
# this is the way to go to have 100% working UTF-8 filenames!
response['Content-Disposition'] = 'attachment; filename=""'
my_styles = self.create_styles([
('ms-regular', 'montserrat-regular',
'Montserrat-Regular'),
('ms-black', 'montserrat-black',
'Montserrat-Black'),
('ms-black-italic', 'montserrat-black-italic',
'Montserrat-BlackItalic'),
('ms-bold', 'montserrat-bold',
'Montserrat-Bold'),
('ms-bold-italic', 'montserrat-bold-italic',
'Montserrat-BoldItalic'),
])
doc = SimpleDocTemplate(response)
elements = []
c = canvas.Canvas(response, pagesize=A4, )
for idx in my_styles.byName:
p = Paragraph("Hello World <i>italic</i> <b>bold</b>",
style=my_styles[idx])
width, height = p.wrapOn(c, A4[0], A4[1])
elements.append(p)
doc.build(elements)
return response
Everything is working except the (very annoying) fact that the <i></i> and <b></b> tags are ignored! It only uses the current font in the style.
How could you modify my code so that it takes in account the tags and I finally get styles with the tags in the text itself?
You need to register a font family if you want Platypus to auto-select fonts - you're creating a different style for every font variant so of course the <i> and <b> tags have no effect - it doesn't know what fonts to use for them as the passed style references a single font.
Here's how to build a style using a font family:
from reportlab.lib.styles import ParagraphStyle
from reportlab.pdfbase.pdfmetrics import registerFont, registerFontFamily
from reportlab.pdfbase.ttfonts import TTFont
def create_paragraph_style(name, font_name, **kwargs):
ttf_path = "C:/Windows/Fonts/{}.ttf"
family_args = {} # store arguments for the font family creation
for font_type in ("normal", "bold", "italic", "boldItalic"): # recognized font variants
if font_type in kwargs: # if this type was passed...
font_variant = "{}-{}".format(font_name, font_type) # create font variant name
registerFont(TTFont(font_variant, ttf_path.format(kwargs[font_type])))
family_args[font_type] = font_variant # add it to font family arguments
registerFontFamily(font_name, **family_args) # register a font family
return ParagraphStyle(name=name, fontName=font_name, fontSize=10, leading=12)
Then you can create your paragraph style as:
pstyle = create_paragraph_style("ms", "montserrat",
normal="Montserrat-Regular",
bold="Montserrat-Bold",
boldItalic="Montserrat-BoldItalic")
assuming, of course, that you have TTF files with those names in your fonts directory.
You can then add it to your stylesheet (especially if you want parent inheritance - make sure you add it as an argument to be forwarded to the ParagraphStyle creation) or directly use it as:
p = Paragraph("Hello World <i>italic</i> <b>bold</b> <b><i>boldItalic</i></b>", style=pstyle)
(the standalone italic won't be affected as we didn't define it in the family, tho).
I am writing a converter code for our Data Department to convert fixed width files into delmited files. Normally we use import the file into Excel, use the text import wizard to set the field lengths, and then just save as a csv. However we have run into the limitation where we have started getting files that are millions of records long, and thus cant be imported into Excel. The files do not always have spaces in between the fields, espicially so between value fields like phone numbers or zip codes. The headers are also often filled completely in with no spaces.
A sample of a typical fixed width file we are dealing with:
SequenSack and PaFull Name****************************]JOB TITLE****************]HOSP NAME******************************]Delivery Address***********************]Alternate 1 Address********************]Calculated Text**********************************]POSTNET Bar
000001T1 P1 Sample A Sample 123 Any Street Anytown 12345-6789 12345678900
000002T1 P1 Sample A Sample Director of Medicine 123 Any Street Po Box 1234 Anytown 12345-6789 12345678900
The program needs to break file into the following delimited fields:
Sequen
Sack and Pa
Full name
Job Title
Hosp Name
Delivery Address
Alternate Address 1
Calculated Text
POSTNET Bar
Each file as a slightly different width of each field depending on the rest of the job. What i am looking for is a GUI oriented delimiter much like the Excel import wizard for fixed width files. I am writing this tool in Python as a part of a larger tool that does many other file operations such as breaking up files into multiple up, reversing a file, converting from delimited to fixed width and check digit checking. I am using Tkinter for the rest of the tools and it would be ideal if the solution use it as well.
Any help appreciated
If I understand the problem correctly (and there's a good chance I don't...), the simplest solution might be to use a text widget.
Make the first line be a series of spaces the same length as the row. Use a couple of alternating tags (eg: "even" and "odd") to give each character an alternate color so they stand out from one another. The second line would be the header, and any remaining lines would be a couple lines of sample data.
Then, set up bindings on the first row to convert a space into an "x" when the user clicks on a character. If they click on an "x", convert it back to a space. They can then go and click on the character that is the start of each column. When the user is done, you can get the first line of the text widget and it will have an "x" for each column. You then just need a little function that translates that into whatever format you need.
It would look roughly like this (though obviously the colors would be different than what appears on this website)
x x x ...
SequenSack and PaFull Name****************************]JOB...
000001T1 P1 Sample A Sample ...
Here's a quick hack to illustrate the general idea. It's a little sloppy but I think it illustrates the technique. When you run it, click on an area in the first row to set or clear a marker. This will cause the header to be highlighted in alternate colors for each marker.
import sys
import Tkinter as tk
import tkFont
class SampleApp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
header = "SequenSack and PaFull Name****************************]JOB TITLE****************]HOSP NAME******************************]Delivery Address***********************]Alternate 1 Address********************]Calculated Text**********************************]POSTNET Bar"
sample = "000001T1 P1 Sample A Sample 123 Any Street Anytown 12345-6789 12345678900"
widget = DelimiterWidget(self, header, sample)
hsb = tk.Scrollbar(orient="horizontal", command=widget.xview)
widget.configure(xscrollcommand=hsb.set)
hsb.pack(side="bottom", fill="x")
widget.pack(side="top", fill="x")
class DelimiterWidget(tk.Text):
def __init__(self, parent, header, samplerow):
fixedFont = tkFont.nametofont("TkFixedFont")
tk.Text.__init__(self, parent, wrap="none", height=3, font=fixedFont)
self.configure(cursor="left_ptr")
self.tag_configure("header", background="gray")
self.tag_configure("even", background="#ffffff")
self.tag_configure("header_even", background="bisque")
self.tag_configure("header_odd", background="lightblue")
self.tag_configure("odd", background="#eeeeee")
markers = " "*len(header)
for i in range(len(header)):
tag = "even" if i%2==0 else "odd"
self.insert("end", " ", (tag,))
self.insert("end", "\n")
self.insert("end", header+"\n", "header")
self.insert("end", samplerow, "sample")
self.configure(state="disabled")
self.bind("<1>", self.on_click)
self.bind("<Double-1>", self.on_click)
self.bind("<Triple-1>", self.on_click)
def on_click(self, event):
'''Handle a click on a marker'''
index = self.index("#%s,%s" % (event.x, event.y))
current = self.get(index)
self.configure(state="normal")
self.delete(index)
(line, column) = index.split(".")
tag = "even" if int(column)%2 == 0 else "odd"
char = " " if current == "x" else "x"
self.insert(index, char, tag)
self.configure(state="disabled")
self.highlight_header()
return "break"
def highlight_header(self):
'''Highlight the header based on marker positions'''
self.tag_remove("header_even", 1.0, "end")
self.tag_remove("header_odd", 1.0, "end")
markers = self.get(1.0, "1.0 lineend")
i = 0
start = "2.0"
tag = "header_even"
while True:
try:
i = markers.index("x", i+1)
end = "2.%s" % i
self.tag_add(tag, start, end)
start = self.index(end)
tag = "header_even" if tag == "header_odd" else "header_odd"
except ValueError:
break
if __name__ == "__main__":
app = SampleApp()
app.mainloop()
edit: I now see that you are looking for a gui. I'll leave this incorrect answer for posterity.
import csv
def fixedwidth2csv(fw_name, csv_name, field_info, headings=None):
with open(fw_name, 'r') as fw_in:
with open(csv_name, 'rb') as csv_out: # 'rb' => 'r' for python 3
wtr = csv.writer(csv_out)
if headings:
wtr.writerow(headings)
for line in fw_in:
wtr.writerow(line[pos:pos+width].strip() for pos, width in field_info)
I build PDF using ReportLab. My program has a MyDocTemplate(SimpleDocTemplate) class with two methods: beforePage(self) and afterPage(self) which add header and footer (as PNG image) on every page. There is also a MyDocStyle class which describe ParagraphStyle.
Main method looks like this:
TITLE = Paragraph(Title, MyDocStyle.h1)
TO = Paragraph(To, MyDocStyle.h2)
FROM = Paragraph(From, MyDocStyle.h2)
SUBJECT = Paragraph(Subject, MyDocStyle.h2)
LONG_PARAGRAPH = Paragraph(Text, MyDocStyle.h3)
...
Elements = [TITLE, TO, FROM, SUBJECT, LONG_PARAGRAPH, ...]
doc = MyDocTemplete('output.pdf', pagesize=A4,
leftMargin=2*cm, rightMargin=2*cm,
topMargin=4*cm, bottomMargin=4*cm)
doc.build(Elements)
Data comes from CSV files and GUI. From time to time (depends on data length) I receive an error:
Flowable <Spacer at 0x2631120 frame=normal>...(1 x 5.66929133858) too large
on page 1 in frame 'normal'(469.88976378 x 603.118110236) of template 'First'
This exception stop my program. For short Paragraphs I set in MyDocStyle class h2.keepWithNext = 1 however it's not perfect solution. ReportLab split correctly long paragraph if end of paragraph does not "coincide" with end of page (text area).
How can I deal with it?
This error occurs when ReportLab try to split a Spacer over two pages. It seems that the only way to workaround this issue is wrap your Spacer into a KeepTogether element:
elements.append(KeepTogether(Spacer(width, height)))
Solved. Don't use Spacer (e.g. Spacer(1, 0.2*cm)) as a separator for Paragraph. Instead, define spaceBefore and spaceAfter in ParagraphStyle, for example:
ParagraphStyle(name = 'Normal',
fontName = "Verdana",
fontSize = 11,
leading = 15,
alignment = TA_JUSTIFY,
allowOrphans = 0,
spaceBefore = 20,
spaceAfter = 20,
wordWrap = 1)