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")
Related
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"
There is an object UltimateListCtrl
from wx.lib.agw import ultimatelistctrl as ULC
self._ulc_graphs = ULC.UltimateListCtrl(self, size = (-1, 150),
agwStyle=wx.LC_REPORT|wx.LC_VRULES|wx.LC_HRULES|ULC.ULC_HAS_VARIABLE_ROW_HEIGHT|ULC.ULC_SINGLE_SEL)
This list contains six columns. Last column "Comment". Since This object does not allow direct editing of the element value in the nth column (only the 0th column can be edited). It was decided to insert a button into each row of the last column, clicking on which flies a dialog with wx.TextCtrl. The user enters a comment there, clicks OK and the text of the comment is entered in the last column of the nth item in the list. 5th column (if with 0):
self._ulc_graphs.InsertColumn(5, 'Комментарий', ULC.ULC_FORMAT_CENTER, 100)
# in the loop I add rows to the list
self._ulc_graphs.SetStringItem (self._ulc_graphs_index, 5, '')
button_comment = wx.Button(self._ulc_graphs, -1, '...', size=(23, 23))
self._ulc_graphs.SetItemWindow(self._ulc_graphs_index, 5, button_comment, False)
button_comment.Bind(wx.EVT_BUTTON, self.OnButtonComment)
self._ulcItem_btn[self._ulc_graphs_index] = button_comment
Result:
Of course, forcing a user to write text into one row is a bad idea. Therefore, i included the wx.TE_MULTILINE style for the wx.TextCtrl object. But when I insert multi-line text into the list cell, this cell increases its size: element height = number of lines in the text.
It looks awful, of course:
Can I ban it somehow (height increase)?
I may have misunderstood your problem but why don't you simply replace all line feeds in the dialog result.
Something along these lines:
def OnButtonComment(self,event):
dlg = wx.TextEntryDialog(self, "Comment", caption="Input Data",
value="", style=wx.OK|wx.CANCEL|wx.TE_MULTILINE)
dlg.ShowModal()
txt = dlg.GetValue()
txt = txt.replace('\n',' ')
self.list.SetStringItem(self.index, 1, txt)
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.
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.
I have a block of text that is dynamically pulled from a database and is placed in a PDF before being served to a user. The text is being placed onto a lined background, much like notepad paper. I want to space the text so that only one line of text is between each background line.
I was able to use the following code to create a vertical spacing between paragraphs (used to generate another part of the PDF).
style = getSampleStyleSheet()['Normal']
style.fontName = 'Helvetica'
style.spaceAfter = 15
style.alignment = TA_JUSTIFY
story = [Paragraph(choice.value,style) for choice in chain(context['question1'].itervalues(),context['question2'].itervalues())]
generated_file = StringIO()
frame1 = Frame(50,100,245,240, showBoundary=0)
frame2 = Frame(320,100,245,240, showBoundary=0)
page_template = PageTemplate(frames=[frame1,frame2])
doc = BaseDocTemplate(generated_file,pageTemplates=[page_template])
doc.build(story)
However, this won't work here because I have only a single, large paragraph.
Pretty sure what yo u want to change is the leading. From the user manual in chapter 6.
To get double-spaced text, use a high
leading. If you set
autoLeading(default "off") to
"min"(use observed leading even if
smaller than specified) or "max"(use
the larger of observed and specified)
then an attempt is made to determine
the leading on a line by line basis.
This may be useful if the lines
contain different font sizes etc.
Leading is defined earlier in chapter 2:
Interline spacing (Leading)
The vertical offset between the point
at which one line starts and where the
next starts is called the leading
offset.
So try different values of leading, for example:
style = getSampleStyleSheet()['Normal']
style.leading = 24
Add leading to ParagraphStyle
orden = ParagraphStyle('orden')
orden.leading = 14
orden.borderPadding = 10
orden.backColor=colors.gray
orden.fontSize = 14
Generate PDF
buffer = BytesIO()
p = canvas.Canvas(buffer, pagesize=letter)
text = Paragraph("TEXT Nro 0001", orden)
text.wrapOn(p,500,10)
text.drawOn(p, 45, 200)
p.showPage()
p.save()
pdf = buffer.getvalue()
buffer.close()
The result