Setting the width of a wxPython TextCtrl in number of characters - python

I have a TextCtrl in my wxPython program and I'd like to set its width to exactly 3 characters. However, the only way to set its size manually accepts only numbers of pixels. Is there any way to specify characters instead of pixels?

There doesn't seem to be a way. You can, however, use wxWindow::GetTextExtent. This is C++ code, but can be easily adapted to wxPython:
int x, y;
textCtrl->GetTextExtent(wxT("T"), &x, &y);
textCtrl->SetMinSize(wxSize(x * N + 10, -1));
textCtrl->SetMaxSize(wxSize(x * N + 10, -1));
/* re-layout the children*/
this->Layout();
/* alternative to Layout, will resize the parent to fit around the new
* size of the text control. */
this->GetSizer()->SetSizeHints(this);
this->Fit();
This is, you take the size of a reasonable width character (fonts may have variable width characters) and multiply it properly, adding some small value to account for native padding (say, 10px).

Realize that most fonts are proportional, which means that each character may take a different width. WWW and lll are both 3 characters, but they will require vastly different sizes of text box. Some fonts, such as Courier, are designed to be fixed width and will not have this problem. Unfortunately you may not have any control over which font is selected in the text box.
If you still want to try this, the key is to get the width of a character in pixels, multiply it by the number of characters, then add some padding for the borders around the characters. You may find this to be a good starting point:
http://docs.wxwidgets.org/stable/wx_wxdc.html#wxdcgetpartialtextextents
or, as litb suggests:
http://docs.wxwidgets.org/2.4/wx_wxwindow.html#wxwindowgettextextent

Related

tkinter wraplength units is pixels?

Re: wraplength for tkinter.Label, from the NM Tech docs by John Shipman: "You can limit the number of characters in each line by setting this option to the desired number. The default value, 0, means that lines will be broken only at newlines." Other sources agree that the units for wraplength are characters.
The code below seems to be breaking the line as if the units of wraplength were pixels, not characters. If I set wraplength to 10, for example, the label displays a column of text one or two characters wide. If I set wraplength to 20, the lines are 3 or 4 characters long.
In my application, the user will be creating his own simple widgets for custom forms, and it would be better if the wraplength option used character count for units instead of whatever it is doing. Since the NMTech docs are for Tkinter 8.5, but 8.6 is what comes with my Python 3.5, maybe that explains the difference, but I don't see docs for 8.6.
But ideally, lines should wrap at the nearest space between words with wraplength just used as a maximum line length. So if the user has to type in his own \n anyway, wraplength seems useless to me.
Summary: in a GUI for users who will be inputing option values for their own simple widgets, 1) is there a way to get Tkinter to accept characters for wraplength units and 2) can I get the line to break at the nearest space short of the wraplength with wraplength used as a maximum line length only, instead of an absolute line length?
Thanks for any solutions or suggestions.
import tkinter as tk
root = tk.Tk()
sib = tk.Label(root, text='Give em hell Harry', wraplength=10)
sib.grid()
root.mainloop()
Is there a way to get Tkinter to accept characters for wraplength units
No, there is not. If you need wrapping to happen at word boundaries you can use a text widget rather than a label.
Unless specified otherwise, the units are in pixels. From the canonical tk documentation:
wraplength For widgets that can perform word-wrapping, this option specifies the maximum line length. Lines that would exceed this length are wrapped onto the next line, so that no line is longer than the specified length. The value may be specified in any of the standard forms for screen distances. If this value is less than or equal to 0 then no wrapping is done: lines will break only at newline characters in the text.
In the above text, "any of the standard forms for screen distances" refers to the fact that you can use a suffix to specify the distance in printers points (eg: "72p"), centimeters (eg: "2.54c"), millimeters (eg: "1000m"), or inches (eg: "1i")
This answer is two years later, but if anyone that sees it and needs to wrap text in a label based on characters or words, you could use the textwrap module. It's very useful if you don't want words to get cut-off between lines but still want to keep them in a label widget.

Tkinter text integer to text index

Is there a way to get way to get an integer to a valid Tkinter text widget position. For example, 0 would get changed to '1.0' and if there were no characters on line 1, 1 would go to '2.0'. I am aware of the widget's index method however only to get the cursor position (in case this is the answer).
Many thanks
EDIT
I believe (having now done more research on the matter) that the Tk Text widget lazy loads the text (for performance). Therefore, without you getting the entire text contents and performing string actions on that, I don't think its possible (the Tk Text only has a minor understanding of what is above and below the currently viewed text - hence why a horizontal scrollbar will constantly adjust when scrolling vertically).
The text widget supports many modifiers to an index. For example, you can take an existing index and compute a new index by adding or subtracting characters or lines. For example, if you want to get the first 10 characters on line 2 you can use "2.0 + 10 chars" (or the more compact "2.0+10c"). If you want the 100th character in the text widget you can use "1.0+100c", etc.
In addition to characters, you can add or subtract lines (eg: "end-1line"), and you can get the start or end of a word or line (eg: "end-1c linestart" represents the first character of the last line in the widget).
You can read about all of the forms an index can take in the canonical tcl/tk documentation: http://tcl.tk/man/tcl8.5/TkCmd/text.htm#M7

Aligning strings in Python

I searched for creating aligned strings in Python and found some relevant stuff, but didn't work for me. Here's one example:
for line in [[1, 128, 1298039], [123388, 0, 2]]:
print('{:>8} {:>8} {:>8}'.format(*line))
Output:
1 128 1298039
123388 0 2
This is what I see in the shell:
As you can see, the alignment didn't happen. Same problem arises when using \t.
What can I do to align the strings in a neat, tabular format?
You have configured your IDLE shell to use a proportional font, one that uses different widths for different characters. Notice how the () pair takes almost the same amount of horizontal space as the > character above it.
Your code is otherwise entirely correct; with a fixed-width font the numbers will line up correctly.
Switch to using a fixed width font instead. Courier is a good default choice, but Windows has various other fonts installed that are proportional, including Consolas.
Configure the font in the Options -> Configure IDLE menu. Pick a different font from the Font Face list. The sample characters in the panel below should line up (except for the second line k at the end, it should stick out).

Python counting zero-length control characters in string formatting width field?

In Python 3.4.3, I was trying to width-align some fields using the string.format() operator, and it appears to count zero-length control characters against the width total. Sample code:
ANSI_RED = "\033[31m"
ANSI_DEFAULT="\033[39m\033[49m"
string1 = "12"
string2 = ANSI_RED+"12"+ANSI_DEFAULT
print("foo{:4s}bar".format(string1))
print("foo{:4s}bar".format(string2))
This will output:
foo12 bar
foo12bar
(with the second output having '12' in red, but I can't reproduce that in SO)
In the second case, I've lost my field width, I assume because Python saw that the total number of chars in the string was larger than the width, despite most of those chars resulting in zero-length on an ANSI-conforming terminal.
What's a clean way of having ANSI colors and working field widths?
What's a clean way of having ANSI colors and working field widths?
Unfortunately, you will have to strip the escape sequences to get a displayed field width.
The len() function returns the number of bytes in a Python 2 str type and the number of code points in a Python 3 str type. That length has never been guaranteed to match the display width (which is a more challenging problem):
>>> s = 'abc\bde'
>>> print s
abcde
>>> len(s)
6
In general, you can't know the display width for certain unless you know something about how the display will interpret the codes (i.e. the width is different depending on whether the device supports ANSI escape sequences).
I don't know if it will qualify as "clean" but something in the vain of the following is workable:
print("foo{0}{1:4s}{2}bar".format(ANSI_RED, string1, ANSI_DEFAULT))
Getting terminal control codes right is really difficult (as seen below, not all of them have a well-defined width), so your best bet is probably to use explicit column movement.
# string2 defined as above
def col(n): return "\033[{:d}G".format(n)
print("foo{:s}{:s}bar".format(string2,col(8)))
Output:
foo12 bar

How do I return the most similar Unicode character to a section of an image?

I made a simple converter in Python to convert images to ASCII. Right now it uses various shades of dark characters, so it works but it is hard to make out at low resolutions: for example, the Google logo comes out as:
.. .;. .#
a; .. .; . .. a. # ...;.
aa .a.▒. ▒.;. ;.;; a. ▒ #a
.;.. .; ..... . ..;;; ; ;..
.a. .;
This can barely be made out. Is there a way that I could compare each section to a subset of Unicode characters and return the most similar, so it could return for example something like:
./--.\. /▒
a; ./-.; / \ ./ \\ ▒ ./━\.
aa -a.▒. ▒.|. |.;▒ ┃ ▒ ▒-~┘
\;.. /| \\_// \ / .\;;; ▒ \\.-
.pp--▒
Generate an image for each character you'd like to use, in the font which you'll be using. You will probably use a fixed width font which will make it possible to create one large image and break it up later. This might be as easy as typing the characters into an editor and doing a screen capture.
For each patch of the input image, compare the patch to all of the character images. I would take corresponding pixels from the patch and the character and square the difference, and sum them up - the character with the lowest sum is the one that most closely matches the patch.
You might improve the results by doing a blur on the character images, the input image, or both. You also might get better results by increasing the contrast on the input image.
Another idea to improve both result quality and speed would be to calculate the average darkness of each character, and only attempt to match characters that are nearly the same darkness as the patch.
This is an old thread, but I might as well add my solution here. You can use braille characters to get pixel-perfect representations. Like so:
⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⡿⡻⡫⡫⡣⣣⢣⢇⢧⢫⢻⣿⣿⣿⣿
⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⡟⡟⣝⣜⠼⠼⢚⢚⢚⠓⠷⣧⣇⠧⡳⡱⣻⣿⣿⣿
⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⡟⣏⡧⠧⠓⠍⡂⡂⠅⠌⠄⠄⠄⡁⠢⡈⣷⡹⡸⣪⣿⣿⣿⣿
⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⢿⠿⢿⢿⢿⢟⢏⡧⠗⡙⡐⡐⣌⢬⣒⣖⣼⣼⣸⢸⢐⢁⠂⡐⢰⡏⣎⢮⣾⣿⣿⣿⣿
⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣽⣾⣶⣿⢿⢻⡱⢕⠋⢅⠢⠱⢼⣾⣾⣿⣿⣿⣿⣿⣿⣿⡇⡇⠢⢁⢂⡯⡪⣪⣿⣿⣿⣿⣿⣿
⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⢟⠏⢎⠪⠨⡐⠔⠁⠁⠀⠀⠀⠙⢿⣿⣿⣿⣿⣿⣿⣿⢱⠡⡁⣢⢏⢮⣾⣿⣿⣿⣿⣿⣿⣿
⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⢟⢍⢆⢃⢑⠤⠑⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⣿⣿⣿⣿⡿⡱⢑⢐⢼⢱⣵⣿⣿⣿⣿⣿⣿⣿⣿⣿
⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⢿⢫⡱⢊⢂⢢⠢⡃⠌⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⣿⣿⢟⢑⢌⢦⢫⣪⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⡻⡱⡑⢅⢢⣢⣳⢱⢑⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠹⡑⡑⡴⡹⣼⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⢟⢝⠜⠨⡐⣴⣵⣿⣗⡧⡣⠢⢈⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣜⢎⣷⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⡫⡱⠑⡁⣌⣮⣾⣿⣿⣿⣟⡮⡪⡪⡐⠠⠀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡟⢏⠜⠌⠄⣕⣼⣿⣿⣿⣿⣿⣿⣯⡯⣎⢖⠌⠌⠄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢨⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⢟⢕⠕⢁⠡⣸⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⡽⡮⡪⡪⠨⡂⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⢟⢕⠕⢁⢐⢔⣽⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⢽⡱⡱⡑⡠⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⢟⢕⠕⢁⢐⢰⣼⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣟⣞⢜⠔⢄⠡⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
⣿⣿⣿⣿⣿⣿⣿⣿⡿⡹⡰⠃⢈⠠⣢⣿⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡮⣇⢏⢂⠢⠀⠀⠀⠀⠀⠀⠀⣠⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
⣿⣿⣿⣿⣿⣿⣿⢫⢒⡜⠐⠀⢢⣱⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣳⢕⢕⠌⠄⡀⠀⠀⢀⣤⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
⣿⣿⣿⣿⣿⡿⡑⣅⠗⠀⡀⣥⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠟⢙⠙⠿⣿⣿⣿⣿⣿⣿⣿⣿⣯⢮⡪⣂⣢⣬⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
⣿⣿⣿⣿⡟⡜⢌⡞⡀⣡⣾⣿⣿⣿⣿⣿⣿⣿⡿⠛⠉⢀⡠⠔⢜⣱⣴⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
⣿⣿⣿⡿⡸⡘⢜⣧⣾⣿⣿⣿⣿⣿⣿⠿⢛⡡⠤⡒⢪⣑⣬⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
⣿⣿⣿⡇⡇⡣⣷⣿⣿⣿⣿⣿⠿⡛⡣⡋⣕⣬⣶⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
⣿⣿⣿⣿⣮⣺⣿⣿⣟⣻⣩⣢⣵⣾⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿
I built a tool for this in Go called dotmatrix: https://github.com/kevin-cantwell/dotmatrix
When you say
compare each section to a subset of Unicode
this is not really clear, because there is more than one way to do this. I would bring the comparing down to the level of pixel. In a gray image, every pixel has a gray-value. Assume you want to replace every pixel by an appropriate character, how has this character to match the pixel? If you look at a character from really far, you'll see only a gray spot. If you replace now a pixel with a character, you should choose the character with the most similar gray-value to that pixel.
In a monospaced font, every character uses the same amount of space. If you take now this rectangle of space, draw a character on it, you can calculate the mean gray-value. This mean gray-value is not more than how much area of the rectangle is white compared to the whole rectangle. A space has a gray-value of 1. And maybe a dollar-sign is one of the most black characters you'll find.
So here is what I would do:
Take a set of characters, no matter whether you use only ascii or uni-code. Calculate for every character the amount of white. It should be obvious, that this could be different for different fonts, but you have to use a monospaced one.
You have now a list which maps every character to a gray-value. You should now rescale the gray-values to your target gray-value interval. When you have an 8-bit image, then your brightest character (space) should correspond to a value of 255 and your darkest should correstpond to gray-level 0.
Now, rescale your input image, so that it is not too big, because even with a very small font, you'll maybe not getting 2000 characters on one line.
Replace every pixel with the character whose gray-level is nearest to its own graylevel
In Mathematica this is only a few lines of code. In python it's maybe a bit longer, but it should be ok too.
Using this way, you get pretty amazing results when you look at the text from far away and when you get closer, you see that it all consists of characters.
Update
When you want to create an image of the same size as the original, then the approach is not very different but even here you have, as Mark already pointed out, to create a raster image of every letter you are using.
I don't really see a faster way of comparing your image-tiles with a letter to decide which one is the most appropriate.
Maybe one hint: If your using this approach, the letters will be visible in your image, because when you have e.g. a 12pt font, each letter will have at least an image-size of about 10x15. When you now convert an image of 1000x1500, which is not so small, you use only 100x100 letters.
Therefore, it might be worth a thought to not use the image itself but the image gradients. This may give better images, because then a letter is choosen, which follows the edges quite good.
Using only the gradients, the google logo doesn't look so bad

Categories

Resources