I found the code below which is supposed to programmatically change the console font size. I'm on Windows 10.
However, whatever values I tweak, I can't seem to get any control over the font size, and also for some reason the console which gets opened when I run this script is very wide.
I have no idea how ctypes works - all I want is to modify the size of the console font from inside Python.
Any actual working solutions?
import ctypes
LF_FACESIZE = 32
STD_OUTPUT_HANDLE = -11
class COORD(ctypes.Structure):
_fields_ = [("X", ctypes.c_short), ("Y", ctypes.c_short)]
class CONSOLE_FONT_INFOEX(ctypes.Structure):
_fields_ = [("cbSize", ctypes.c_ulong),
("nFont", ctypes.c_ulong),
("dwFontSize", COORD),
("FontFamily", ctypes.c_uint),
("FontWeight", ctypes.c_uint),
("FaceName", ctypes.c_wchar * LF_FACESIZE)]
font = CONSOLE_FONT_INFOEX()
font.cbSize = ctypes.sizeof(CONSOLE_FONT_INFOEX)
font.nFont = 12
font.dwFontSize.X = 11
font.dwFontSize.Y = 18
font.FontFamily = 54
font.FontWeight = 400
font.FaceName = "Lucida Console"
handle = ctypes.windll.kernel32.GetStdHandle(STD_OUTPUT_HANDLE)
ctypes.windll.kernel32.SetCurrentConsoleFontEx(
handle, ctypes.c_long(False), ctypes.pointer(font))
print("Foo")
Before everything, check [SO]: C function called from Python via ctypes returns incorrect value (@CristiFati's answer) for a common pitfall when working with CTypes (calling functions).
The CTypes home page (also listed in the above URL): [Python.Docs]: ctypes - A foreign function library for Python
I changed your code "a bit".
code00.py:
#!/usr/bin/env python
import ctypes as cts
import ctypes.wintypes as wts
import sys
LF_FACESIZE = 32
STD_OUTPUT_HANDLE = -11
class CONSOLE_FONT_INFOEX(cts.Structure):
_fields_ = (
("cbSize", wts.ULONG),
("nFont", wts.DWORD),
("dwFontSize", wts._COORD),
("FontFamily", wts.UINT),
("FontWeight", wts.UINT),
("FaceName", wts.WCHAR * LF_FACESIZE)
)
def main(*argv):
kernel32 = cts.WinDLL("Kernel32.dll")
GetLastError = kernel32.GetLastError
GetLastError.argtypes = ()
GetLastError.restype = wts.DWORD
GetStdHandle = kernel32.GetStdHandle
GetStdHandle.argtypes = (wts.DWORD,)
GetStdHandle.restype = wts.HANDLE
GetCurrentConsoleFontEx = kernel32.GetCurrentConsoleFontEx
GetCurrentConsoleFontEx.argtypes = (wts.HANDLE, wts.BOOL, cts.POINTER(CONSOLE_FONT_INFOEX))
GetCurrentConsoleFontEx.restype = wts.BOOL
SetCurrentConsoleFontEx = kernel32.SetCurrentConsoleFontEx
SetCurrentConsoleFontEx.argtypes = (wts.HANDLE, wts.BOOL, cts.POINTER(CONSOLE_FONT_INFOEX))
SetCurrentConsoleFontEx.restype = wts.BOOL
# Get stdout handle
stdout = GetStdHandle(STD_OUTPUT_HANDLE)
if not stdout:
print("{:s} error: {:d}".format(GetStdHandle.__name__, GetLastError()))
return
# Get current font characteristics
font = CONSOLE_FONT_INFOEX()
font.cbSize = cts.sizeof(CONSOLE_FONT_INFOEX)
res = GetCurrentConsoleFontEx(stdout, False, cts.byref(font))
if not res:
print("{:s} error: {:d}".format(GetCurrentConsoleFontEx.__name__, GetLastError()))
return
# Display font information
print("Console information for {:}".format(font))
for field_name, _ in font._fields_:
field_data = getattr(font, field_name)
if field_name == "dwFontSize":
print(" {:s}: {{X: {:d}, Y: {:d}}}".format(field_name, field_data.X, field_data.Y))
else:
print(" {:s}: {:}".format(field_name, field_data))
while 1:
try:
height = int(input("\nEnter font height (invalid to exit): "))
except:
break
# Alter font height
font.dwFontSize.X = 10 # Changing X has no effect (at least on my machine)
font.dwFontSize.Y = height
# Display font information
res = SetCurrentConsoleFontEx(stdout, False, cts.byref(font))
if not res:
print("{:s} error: {:d}".format(SetCurrentConsoleFontEx.__name__, GetLastError()))
return
print("OMG! The window changed :)")
# Get current font characteristics again
res = GetCurrentConsoleFontEx(stdout, False, cts.byref(font))
if not res:
print("{:s} error: {:d}".format(GetCurrentConsoleFontEx.__name__, GetLastError()))
return
print("\nNew sizes X: {:d}, Y: {:d}".format(font.dwFontSize.X, font.dwFontSize.Y))
if __name__ == "__main__":
print("Python {:s} {:03d}bit on {:s}\n".format(" ".join(elem.strip() for elem in sys.version.split("\n")),
64 if sys.maxsize > 0x100000000 else 32, sys.platform))
rc = main(*sys.argv[1:])
print("\nDone.\n")
sys.exit(rc)
Notes:
CTypes allows low level access similar to C (only the syntax is Python)
Code uses [MS.Learn]: SetConsoleTextAttribute function
In order to avoid setting invalid values, it's used in conjunction with its counterpart: [MS.Learn]: GetCurrentConsoleFontEx function. Call that function in order to populate the CONSOLE_FONT_INFOEX structure, then modify only the desired values
There are "simpler" functions (e.g. [MS.Learn]: GetCurrentConsoleFont function or [MS.Learn]: GetConsoleFontSize function), but unfortunately there are no corresponding setters
Note that all referenced WinAPI functions are deprecated
The ctypes.wintypes constants (which reference the standard CTypes types) are used (to give the code a Win like flavor)
It is very (almost painfully) long (also because I've added proper error handling)
An alternative, as suggested in one of the answers of [SO]: Change console font in Windows (where you copied the code from), would be to install a 3rd-party module (e.g. [GitHub]: mhammond/pywin32 - Python for Windows (pywin32) Extensions which is a Python wrapper over WINAPIs) which would require less code to write because the bridging between Python and C would be already implemented, and probably the above functionality could be accomplished in just a few lines
As I commented in the code, setting COORD.X seems to be ignored. But it's automatically set when setting COORD.Y (to a value close to COORD.Y // 2
- probably to preserve the aspect ratio). On my machine (Win 10 pc064) with the currently selected font, the default value is 16. You might want to set it back at the end, to avoid leaving the console in a "challenged" state (apparently, Win adjusts Cmd window size, to be (sort of) in sync with the font size):