Reputation: 179
In my tkinter application I would like the user to be able to click on a button or menu option in order to be given more information. I would like to be able to open another window on which would be about an A4 page of detailed information.
Ideally I would like this to be nicely formatted, with headings, sub-headings, bulleted lists, numbered lists and bold & italic etc.
I naïvely thought that this would simply be a case of producing a markdown or RTF document and then finding the appropriate tk widget to display it. Unless I'm missing something, it seems that it's not that easy.
From what I've found out so far (for example from this thread) I think my main options are either to laboriously create tk labels with each different type of formatting; or to use some sort of python / HTML such as tk-html-widgets. I'm not a big fan of HTML (or at least of writing it), but if that's the only way...
Before I go to the pain of either of these approaches I thought I would ask here first. Is this indeed the only way of achieving a nicely formatted page of text? Is there a better way?
Looking forward to some suggestions!
Upvotes: 3
Views: 9019
Reputation: 385800
Tkinter doesn't directly support markdown or rst or any other rich format. However, the text widget has the ability to apply formatting to regions of text via tags. Tags are remarkably powerful, allowing you to apply fonts, colors, margins, spacing, and other attributes to a range of characters.
The text widget doesn't directly support bulleted lists, but they are easy to create with just a few lines of code and some custom tags.
Here's a quick example that shows how to configure and use a few tags, and how to create a method that creates bulleted items.
import tkinter as tk
from tkinter import font as tkFont
class RichText(tk.Text):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
default_font = tkFont.nametofont(self.cget("font"))
em = default_font.measure("m")
default_size = default_font.cget("size")
bold_font = tkFont.Font(**default_font.configure())
italic_font = tkFont.Font(**default_font.configure())
h1_font = tkFont.Font(**default_font.configure())
bold_font.configure(weight="bold")
italic_font.configure(slant="italic")
h1_font.configure(size=int(default_size*2), weight="bold")
self.tag_configure("bold", font=bold_font)
self.tag_configure("italic", font=italic_font)
self.tag_configure("h1", font=h1_font, spacing3=default_size)
lmargin2 = em + default_font.measure("\u2022 ")
self.tag_configure("bullet", lmargin1=em, lmargin2=lmargin2)
def insert_bullet(self, index, text):
self.insert(index, f"\u2022 {text}", "bullet")
if __name__ == "__main__":
root = tk.Tk()
text = RichText(root, width=40, height=15)
text.pack(fill="both", expand=True)
text.insert("end", "Rich Text Example\n", "h1")
text.insert("end", "Hello, world\n\n")
text.insert_bullet("end", "Item 1\n")
text.insert_bullet("end", "Item 2\n")
text.insert("end", "\n")
text.insert("end", "This line is bold\n", "bold")
text.insert("end", "This line is italicized\n", "italic")
root.mainloop()
For an example of a text widget that has a function for applying formatting to words that match a regular expression see this answer: https://stackoverflow.com/a/3781773/7432
Upvotes: 12
Reputation: 8037
I would start like in the code below, there you can draw your text and place it how you like.
import tkinter as tk
from tkinter import font as tkfont
root = tk.Tk()
class MyDocument(tk.Canvas):
def __init__(self, master):
tk.Canvas.__init__(self,master)
self.master = master
self.configure(width=500, height=500, relief='sunken', bd=2,
background='white')
self.Headline(100,100, self, "I'm the headline")
self.txt_listed(50,150, self, 'Hi',"I'm",'listed')
self.mixed(50,250,self, normal = 'This bit is ',
bold = 'really',
normal2 = 'important')
class Headline(object):
def __init__(self,x,y,canvas, text):
self.font = tkfont.Font(family="Helvetica", size=20,
weight="bold",underline=1)
canvas.create_text(x,y, text=text,anchor='nw',
font=self.font, fill='black',
activefill='green')
class txt_listed(object):
def __init__(self,x,y,canvas,*text):
self.font = tkfont.Font(family="Arial", size=11,underline=1)
add=20
for txt in text:
canvas.create_text(x,y+add, text=f"• {txt}",anchor='nw')
add = add+20
class mixed(object):
def __init__(self,x,y,canvas, **text):
forward = x
self.bold_font = tkfont.Font(family="Arial", size=10,
weight="bold")
self.normal_font = tkfont.Font(family="Arial", size=10)
for key,value in text.items():
if 'bold' in str(key):
key = canvas.create_text(forward,y,text=value, font=self.bold_font)
forward = canvas.bbox(key)[2]+30
if 'normal' in str(key):
key = canvas.create_text(forward,y,text=value, font=self.normal_font)
forward = canvas.bbox(key)[2]+15
text_me = MyDocument(root)
text_me.pack()
root.mainloop()
the mixed class dosent work stabel but just to give you the idea what is possible. Maybe its easier to make italic and bold classes and place them as you like instead of the math. Cause keep in mind that charackters arent in pixels, so even if you find something with another font it will be messed up.
Have fun with it.
Upvotes: 2