How to make text fit inside a python curses textbox? - python

I've tried many things attempting to make the text stay inside its borders but I can't find a way. Below is what I've already tried.
#!/usr/bin/env python
import curses
import textwrap
screen = curses.initscr()
screen.immedok(True)
try:
screen.border(0)
box1 = curses.newwin(20, 40, 6, 50)
box1.immedok(True)
text = "I want all of this text to stay inside its box. Why does it keep going outside its borders?"
box1.box()
box1.addstr(1, 0, textwrap.fill(text, 39))
#box1.addstr("Hello World of Curses!")
screen.getch()
finally:
curses.endwin()

Your first problem is that calling box1.box() takes up space in your box. It uses up the top row, the bottom row, the first column, and the last column. When you use box1.addstr() to put a string in a box, it starts at col 0, row 0, and so overwrites the box characters. After creating your borders, your box only has 38 available characters per line.
I'm not a curses expert, but one way of resolving this is to create a new box inside box1 that is inset by one character all the way around. That is:
box2 = curses.newwin(18,38,7,51)
Then you can write your text into that box without overwriting the box drawing characters in box1. It's also not necessary to call textwrap.fill; it appears that writing a string to a window with addstr automatically wraps the text. In fact, calling textwrap.fill can interact badly with the window: if text wrap breaks a line at exactly the window width, you may end up with an erroneous blank line in your output.
Given the following code:
try:
screen.border(0)
box1 = curses.newwin(20, 40, 6, 50)
box2 = curses.newwin(18,38,7,51)
box1.immedok(True)
box2.immedok(True)
text = "I want all of this text to stay inside its box. Why does it keep going outside its borders?"
text = "The quick brown fox jumped over the lazy dog."
text = "A long time ago, in a galaxy far, far away, there lived a young man named Luke Skywalker."
box1.box()
box2.addstr(1, 0, textwrap.fill(text, 38))
#box1.addstr("Hello World of Curses!")
screen.getch()
finally:
curses.endwin()
My output looks like this:

The box is part of the window, and uses the same real estate as the text. You can make a subwindow of the first window after drawing a box on the first window. Then write your wrapped text in the subwindow.
Something like
box1 = curses.newwin(20, 40, 6, 50)
box1.immedok(True)
text = "I want all of this text to stay inside its box. Why does it keep going outside its borders?"
box1.box()
box1.refresh()
# derwin is relative to the parent window:
box2 = box1.derwin(18, 38, 1,1)
box2.addstr(1, 0, textwrap.fill(text, 39))
See the description of derwin in the reference.

Related

How do you make the tkinter text widget recognise lines in the index?

I'm working on highlighting chosen parts of a text green in a text widget in Tkinter.
To my understanding, on Tkinter the index for the second line, fourth character of a text would be 2.04. For some reason, it's not recognising a new line and the decimal number keeps increasing into the hundreds so I'm struggling to highlight words once the total characters of the text exceed 99. When it does, it highlights lots of text after the chosen word in green.
I've set the max width of the text widget to 99 characters to make the index create lines but but it's still not doing anything.
For clarification, I'm trying to highlight the word 'calculates' in green with this code.
from tkinter import *
window = Tk()
text_window = Text(window, height=10, width=99, wrap=WORD)
text_window.insert(INSERT, sample_text)
text_window.configure(state=DISABLED)
text_window.pack()
countVar = StringVar()
pos = text_window.search("calculates", "1.0", stopindex="end", count=countVar)
end_num = float(countVar.get()) / 100
position = float(pos)
print(position)
print(end_num)
end_point = position + end_num
text_window.tag_configure("search", background="green")
text_window.tag_add("search", pos, end_point)
window.mainloop()
"2.04" is not valid. It will work, but something like "2.08" won't since 08 is an invalid octal number. A text widget index must be string in the form of a valid integer, a period, and then another valid integer.
The text widget index also supports modifiers, so you can easily add or subtract characters. In your case it would look something like this:
end_point = f"{pos}+{countVar.get()}chars"
or
end_point = f"{pos}+{countVar.get()}c"

How to create a text shape with python pptx?

I want to add a text box to a presentation with python pptx. I would like to add a text box with several paragraphs in the specific place and then format it (fonts, color, etc.). But since text shape object always comes with the one paragraph in the beginning, I cannot edit first of my paragraphs. The code sample looks like this:
txBox = slide.shapes.add_textbox(left, top, width, height)
tf = txBox.text_frame
p = tf.add_paragraph()
p.text = "This is a first paragraph"
p.font.size = Pt(11)
p = tf.add_paragraph()
p.text = "This is a second paragraph"
p.font.size = Pt(11)
Which creates output like this:
I can add text to this first line with tf.text = "This is text inside a textbox", but it won't be editable in terms of fonts or colors. So is there any way how I can omit or edit that line, so all paragraphs in the box would be the same?
Access the first paragraph differently, using:
p = tf.paragraphs[0]
Then you can add runs, set fonts and all the rest of it just like with a paragraph you get back from tf.add_paragraph().

Delete range from Text

I am trying to create a keypad using python tk library. I am running Python version 3.6.3.
1) I have a Text widget in my UI. When I press the backspace button, I want to delete the last character in the Text widget. I am keeping the count of the total number of characters in the Text widget as well.
So far I have tried:
def back():
global char_count # contains total char count
text.delete(char_count)
I also tried to adjust the last line to text.delete(char_count-1) thinking that may be the index count was off by 1 (I wasn't sure if my count matched index in the Text widget). The above code doesn't delete anything.
2) I was also trying to see how a range of text can be deleted. I have checked online and I find to delete the entire Text content, people use:
text.delete("1.0", tk.END)
This works, but if I try another approach to delete everything from the second index as follows, nothing happens:
text.delete("2.0", tk.END)
I wanted to ask what is the right way to delete the last character or a range from the text, assuming the indices to be used are in variables and not hard coded like "2.0" above.
1) The Text widget always insures that the last character in the widget is a newline character, so you could delete the one you want, the second-to-last character, like this:
def back():
text.delete('%s - 2c' % 'end')
No need to keep track of the character count in the Text widget.
A full working sample is here:
import sys
if sys.version_info.major == 3:
import tkinter as tk
else:
import Tkinter as tk
def back():
text.delete('%s - 2c' % tk.END)
root = tk.Tk()
text = tk.Text(root)
text.pack()
tk.Button(root, text='Delete', command = back).pack()
root.mainloop()
2) Watch out for text.delete("2.0", tk.END). "2.0" is the start of the second line. The index of a Text widget has different formats, but the simplest is text string 'X.Y', where X is the line number (starting at 1) and Y is the column number (starting at 0) of that line. And the columns don't always line up, since a tab character will take a single column but look much wider in the Text widget.
You can call the delete() method with variables, like text.delete(startIndex, stopIndex). The trick is making sure that the indexes are valid. '1.0' represents the very first position in the Text widget, and 'end' represents the very last. Have a look at http://effbot.org/tkinterbook/text.htm for a pretty reasonable and concise look at Text widgets and how their indexes work.
There are different ways to manipulate a Text index, but some of the most common are with the text.index(arg) method, which returns an 'X.Y' representation of arg, and the nextpos = text.index('%s + 1 chars' % thispos) format, which allows you to do basic math on an index. In this case, it would set nextpos to the next column. But the '+ 1' can be plus or minus any_number, and the 'chars' can be 'lines' or 'words'. There's a lot to it. But have a look at that effbot.org page.

Changing font for part of text in python-pptx

I'm using the python-pptx module to create presentations.
How can I change the font properties only for a part of the text?
I currently change the font like this:
# first create text for shape
ft = pres.slides[0].shapes[0].text_frame
ft.clear()
p = ft.paragraphs[0]
run = p.add_run()
run.text = "text"
# change font
from pptx.dml.color import RGBColor
from pptx.util import Pt
font = run.font
font.name = 'Lato'
font.size = Pt(32)
font.color.rgb = RGBColor(255, 255, 255)
Thanks!
In PowerPoint, font properties are applied to a run. In a way, it is what defines a run; a "run" of text sharing the same font treatment, including typeface, size, color, bold/italic, etc.
So to make two bits of text look differently, you need to make them separate runs.
As #scanny says in another post. The post
By "different run", it means you should write the text in first color in a run, then afterwards, write the text in the second color in another run as shown below.
There's also an example in the post, I'll just copy and paste it to here
from docx.shared import RGBColor
# ---reuse existing default single paragraph in cell---
paragraph = cell.paragraphs[0]
###(First run)
#---add distinct runs of text one after the other to
# --- form paragraph contents.
paragraph.add_run("A sentence with a ")
###(Second run)
# ---colorize the run with "red" in it.
red_run = paragraph.add_run("red")
red_run.font.color.rgb = RGBColor(255, 0, 0)
###(Third run)
paragraph.add_run(" word.")

Get the textarea contents in python

I have a text area. And i have to get the contents of the text area using Tkinter in python.
self.outputText = Text(self)
self.outputText.place(relx = 0, rely = 0.15, relwidth = 1, relheight = 0.7)
self.outputText.insert(index = INSERT, chars = textParam)
The way to get the data out of the text widget is with the get method. Tkinter always guarantees the data ends in a newline, so to get exactly what the user entered you want to get one less character than is in the widget, otherwise you will get this automatically inserted newline. For example:
self.outputText.get("1.0", "end-1c")

Categories