How to get the width of a string in pixels? - python

I am using wxPython's HyperTreeList and I want to set the column width exactly equal to length of the largest string in it.
To accomplish that, I'd like to to convert a python string size into pixels.
For Example: If we have a string like
str = "python"
len(str) = 6
How could I convert the above string length/size into pixels?
Is there another way?

You'll have to do something like (see the documentation of wxWidgets for more info)
f = window.GetFont()
dc = wx.WindowDC(window)
dc.SetFont(f)
width, height = dc.GetTextExtent("Text to measure")

It depends on how you are printing the text.
You may be interested by PIL ImageDraw which has a textsize method. See http://effbot.org/imagingbook/imagedraw.htm
Update: This was answering the original question. It may looks a little off-topic after question updates.

This is not my solution, I'm just passing it on as I found it works and
most useful
as in a program environment it reduces to only 3 lines. Python3.9 mac OS
from tkinter import *
from tkinter import font as W1
FW=Tk() ; FW.withdraw() # a must but don't need a window
Text ='1234567890'
def LENGTH(Text) :
W2 = W1.Font(family='Comicsans' , size = 20)
length = W2.measure(Text)
print(length)
LENGTH(Text)

Related

Pyautogui use size() to get two integers

so I am trying to create a bot in python, and for that cause I would need the size of the screen as two integers (x coordinate and y coordinate). I use pyautogui for that case, but the function size() only returns a string:
Size(width=2560, height=1440)
How would i go about "extracting" these values into integer variables?
edit: I managed to fix my problem, it's some spagetthi code, but I can clean it up later, just in case someone has the same problem:
import pyautogui
screen_size = str(pyautogui.size())
screen_size_x, screen_size_y = screen_size.split(",")
screen_size_x= screen_size_x.replace("Size(width=","")
screen_size_y = screen_size_y.replace("height=","")
screen_size_y = screen_size_y.replace(")","")
screen_size_y = screen_size_y.replace(" ","")
screen_size_x = int(screen_size_x)
screen_size_y = int(screen_size_y)
print(screen_size_x)
print(screen_size_y)
from inspect import getsource
print(getsource(pyautogui.size))
output:
def size():
"""Returns the width and height of the screen as a two-integer tuple.
Returns:
(width, height) tuple of the screen size, in pixels.
"""
return Size(*platformModule._size())
nicer code:
import pyautogui
x,y = pyautogui.size()
x,y=int(str(x)),int(str(y))
print(x)
print(y)
I've found multiple examples online of using pyautogui.size(). In all those examples, that method returns a two-item tuple containing width and height. So it seems that your code could be as simple as:
screen_size_x, screen_size_y = pyautogui.size()
print(screen_size_x)
print(screen_size_y)
This is shown in the first example in the pyautogui docs: https://github.com/asweigart/pyautogui/blob/master/README.md
If you did need to parse the string you mention, here's a cleaner way to do that:
import re
str = "Size(width=2560, height=1440)"
m = re.search(r"width=(\d+).*height=(\d+)", str)
screen_size_x, screen_size_y = int(m.group(1)), int(m.group(2))
print(screen_size_x)
print(screen_size_y)

Build frames for a text animation in Wand

I'm trying to write an automated script in Wand on Python that builds the frames for a text animation by writing a caption of an image one letter at a time.
The problem is that when I write one letter using the caption command (documentation here http://docs.wand-py.org/en/0.4.4/wand/image.html) it writes a giant letter, while when I write the whole text, it is fitted nicely in the image.
I thought of a possible solution: write the first letter colored and the rest transparent and cycle through that, however the caption command is not capable of doing multicolored text as far as I know.
If someone could suggest me another option I would be grateful. I could use draw.text, however that doesn't automatically calculate when to go on the next line as far as I know...
My code looks like this:
imgname = random.choice(os.listdir('/home/gionny/Downloads/HighResImg'))
text = 'Hello, world! This is a slightly longer sentence.'
fontname = random.choice(os.listdir('/home/gionny/Downloads/font'))
with Image(filename='HighResImg/'+imgname) as i:
font = Font(path = 'font/'+fontname, color = Color('#fff'))
textWidth = i.width*2/3
textHeight = i.height*2/3
offsetLeft = (i.width - textWidth)/2
offsetTop = (i.height - textHeight)/2
with Image(filename='logo.gif') as l:
l.resize(80,80)
l.transparentize(0.7)
with Drawing() as draw:
draw.composite(operator='atop', left=i.width-90, top=i.height-90, width=l.width, height=l.height, image=l)
for c in range(0, len(text)):
caption = i.caption(text = text[c], left = offsetLeft, top = offsetTop, width=textWidth, height=textHeight, font = font, gravity = 'center')
print(caption)
cl = i.clone()
cl.format = 'jpeg'
cl.save(filename='Text/text'+str(c)+'.jpg')
cl.destroy()
If someone could suggest me another option I would be grateful. I could use draw.text, however that doesn't automatically calculate when to go on the next line as far as I know...
There's no quick way around it, you are responsible for calculating the x,y coordinates with each iteration. Especially when using mixed fonts pulled at random.
The method wand.drawing.Drawing.get_font_metrics has be provided for this sort of thing. Simply keep an accumulator & update with each iteration.
from wand.image import Image
from wand.color import Color
from wand.drawing import Drawing
with Image(width=400, height=250, background=Color("skyblue")) as background:
leftOffset = 35 # <= Starting position.
topOffset = background.height/2;
for letter in "Hello World":
with Drawing() as ctx:
ctx.font = "TimesNewRoman"
ctx.font_size = 64.0
metrics = ctx.get_font_metrics(background, letter)
ctx.text(leftOffset, int(topOffset+metrics.text_height/4), letter)
with Image(width=background.width,
height=background.height,
background=Color("transparent")) as frame:
ctx.draw(frame)
background.sequence.append(frame)
leftOffset += int(metrics.text_width) # <= Adjust for next iteration.
background.save(filename="output.gif")
Now for repeating the next-line process, just increase topOffset by the font metrics text_height if the leftOffset is greater than canvas width.

Using python binding for flycapture to retrieve color image

I am working with the CMLN-13S2C-CS CCD camera from PointGrey Systems. It uses FlyCapture API to grab images. I would like to grab these images and do some stuff in OpenCV with them using python.
I am aware of the following python binding: pyflycapture2. With this binding I am able to retrieve images. However, I cannot retrieve the images in color, which is what the camera should be able to do.
The videomode and framerate that the camera is able to handle are VIDEOMODE_1280x960Y8, and FRAMERATE_15, respectively. I think it has something to do with the pixel_format, which I think should be raw8.
Is anyone able to retrieve a color image using this or any existing python binding for flycapture? Note that I am working on Linux.
You don't need to use the predefined modes. The Context class has the set_format7_configuration(mode, x_offset, y_offset, width, height, pixel_format) method with which you can use your custom settings. Using this you can at least change the resolution of the grabbed image.
Usage example:
c.set_format7_configuration(fc2.MODE_0, 320, 240, 1280, 720, fc2.PIXEL_FORMAT_MONO8)
As for the coloring issue. I've so far managed to get a colored image using PIXEL_FORMAT_RGB8 and modifying the Image class in flycapture2.pyx as follows:
def __array__(self):
cdef np.ndarray r
cdef np.npy_intp shape[3] # From 2 to 3
cdef np.dtype dtype
numberofdimensions = 2 # New variable
if self.img.format == PIXEL_FORMAT_MONO8:
dtype = np.dtype("uint8")
elif self.img.format == PIXEL_FORMAT_MONO16:
dtype = np.dtype("uint16")
elif self.img.format == PIXEL_FORMAT_RGB8: # New condition
dtype = np.dtype("uint8")
numberofdimensions = 3
shape[2] = 3
else:
dtype = np.dtype("uint8")
Py_INCREF(dtype)
shape[0] = self.img.rows
shape[1] = self.img.cols
# nd value (numberofdimensions) was always 2; stride set to NULL
r = PyArray_NewFromDescr(np.ndarray, dtype,
numberofdimensions, shape, NULL,
self.img.pData, np.NPY_DEFAULT, None)
r.base = <PyObject *>self
Py_INCREF(self)
return r
This code is most likely not flawless (i.e I removed the stride stuff) for the simple reason that I have pretty much 0 experience with C and Cython but this way I at least managed to get a colored frame (now in the process of trying to get the PIXEL_FORMAT_RAW8 working).
And just as a reminder: the flycapture2.pyx is a Cython file so you need to recompile it before you can use it (I just run the pyflycap2 install script again).
I'm using the same camera with Matlab and also got an issues with "raw8" format. So, I've chose "rgb8", specifically, "F7_RGB_644x482_Mode1" and all things starts to work (not sure, how it should look at Python).
P.S. At the moment I'm trying to start work with Python and pyflycapture2, let's see, if I would be able to find workaround.
UPD: Okay, now I know the things. :)
Your (and mine) issue reasons are buried inside the pyflycapture2 itself, especially "Image" class definition. You can have a look here: https://github.com/jordens/pyflycapture2/blob/eec14acd761e89d8e63a0961174e7f5900180d54/src/flycapture2.pyx
if self.img.format == PIXEL_FORMAT_MONO8:
dtype = np.dtype("uint8")
stride[1] = 1
elif self.img.format == PIXEL_FORMAT_MONO16:
dtype = np.dtype("uint16")
stride[1] = 2
else:
dtype = np.dtype("uint8")
stride[1] = self.img.stride/self.img.cols
ANY image will be converted into grayscale, even if it was RGB initially. So, we need to update that file somehow.

Pygame Font.render() cropping uppercase letters with acute accents, tilde or circunflex

I am trying to recreate my tiny python/pygame project to include multiple languages, and one of the problems I have encountered is the fact that for some reason the accents, tildes etc. are being cropped from the top so only 1 or 2 pixels of them are visible. This applies to most of the 'accented' uppercase letters in ie. French, Polish, Spanish, German, etc.
This happens both in python 2.7.2 and python 3.2, using default pygame font.
Trying to render the following string:
'ÀÂÉÈÊËÎÏÔÙÛÜŸ'
results in the rendered image like this:
I guess, I am using the standard rendering/blitting technique:
# -*- coding: utf-8 -*-
#...
value = 'ÀÂÉÈÊËÎÏÔÙÛÜŸ'
if sys.version_info < (3, 0):
try:
val = unicode(value, "utf-8")
except UnicodeDecodeError:
val = value
text = font.render("%s" % (val), 1, font_color)
else:
val = value
text = font.render("%s" % (val), 1, font_color)
self.image.blit(text, (300,300))
#...
anyone have had this problem before? any workarounds? or is it just that pygame.font.render creates too small surface to accommodate the extra space needed at the top? Maybe some way to set the line height, but I haven't found that anywhere...
Try specifying a font rather than the one youa re using (are you using the default one?) - it is most likely a problem in the font. - It worked for me with all fonts I tried in my system.
For full games, an ideal solution is distributing the font file alongside your project - since Pygame open fonts given the ".ttf" file name.
For using a system font, you have to chain calls pygame.font.get_fonts()- to get font names, to pygame.font.match_font to actually get the path to the font file given the font name, and finally pygame.font.Font passing that path and a point-size to get a usable font object (with the render method)
>>> pygame.font.get_fonts()
[u'lohitbengali', u'liberationserif', u'dejavuserif', u'wenquanyimicroheimono', u'dejavulgcsans', u'arplumingcn', u'msam10', u'jomolhari', u'esint10', u'cmr10', u'arplumingtwmbe', u'dejavusans', u'lohitgujarati', u'lohitpunjabi', u'arplumingtw', u'arpluminghk', u'dejavusansmono', u'lohitassamese', u'lohitkannada', u'wenquanyimicrohei', u'khmeros', u'waree', u'lohittamil', u'wasy10', u'padauk', u'lohitdevanagari', u'paktypenaqsh', u'abyssinicasil', u'meera', u'eufm10', u'cmex10', u'khmerossystem', u'lklug', u'lohitoriya', u'msbm10', u'liberationsans', u'rsfs10', u'opensymbol', u'cmsy10', u'lohittelugu', u'paktypetehreer', u'cmmi10', u'liberationmono', u'khmeroscontent']
>>> f2 = pygame.font.match_font(u'paktypetehreer')
>>> f2
u'/usr/share/fonts/paktype-tehreer/PakType_Tehreer.ttf'
>>> font = pygame.font.Font(f2, 60)
>>> f = font.render(u"maçã, joão", 1, (255,255,255))
>>> scr.blit(f, (200,100))

How can i convert Integer to Signed Word in Python (for use with PySerial)?

I'm having some problems making Python talk to a hardware display using pyserial.
Some of the display's functions require a signed word to be sent as arguments after commands (ie. X or Y on display screen).
I've been getting by with chr() previously, but that only works with numbers < 255.
I've tried the following for conversion but it's giving some weird results, placing things way off the set position:
def ByteIt(self,data):
datastring = str()
for each in tuple(str(data)):
datastring = datastring + chr(int(each))
return datastring
I may be way off myself here :)
Example of how i would use it:
x = 100
y = 350
serial.Write('\x01' + ByteIt(x) + ByteIt(y)) # command , xpos , ypos
The thing is, when i do this, the stuff is not placed at x100,y350, most times the display will crash :(
Any tips on how this can be done properly?
Read about the struct module.
http://docs.python.org/library/struct.html
Replace all of the "chr" and stuff with proper struct.pack() calls.
Specifically
bytes = struct.pack( 'h', some_data )
Should give you a "signed word".
You may want to revise again the documentation about "pack" and "unpack". Selecting the appropriate upper-case or lower-case allows you to specify the Endianness. So, based on the example above which didn't work perfectly on your device, I presume you need:
x = 100
y = 350
serial.Write('\x01' +
struct.pack('>hh', x) +
struct.pack('>hh', y)) # command , xpos , ypos

Categories

Resources