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):
It's not a pure python problem, but covers Windows API.
Look at the documentation of CONSOLE_FONT_INFOEX structure. There is a COORD member on it for width and height of each character.
To change console font size, you can give these attributes as a proper value:
font.dwFontSize.X = 11
font.dwFontSize.Y = 18
Reference: Change console font in Windows
For anyone in the future who wants to use the console as a method of displaying (black and white) images or just getting the smallest possible font size, this is the modified answer code I used. I found a height and width of 2px was the best smallest size, but be cautious, all sizes distort the picture, some more than others. As the font, I use the only "rasterfont" that's available. (Personally I had massive problems getting this to work, not sure why tho... and this is NOT the same as the accepted answer)
def changeFontSize(size=2): #Changes the font size to *size* pixels (kind of, but not really. You'll have to try it to chack if it works for your purpose ;) )
from ctypes import POINTER, WinDLL, Structure, sizeof, byref
from ctypes.wintypes import BOOL, SHORT, WCHAR, UINT, ULONG, DWORD, HANDLE
LF_FACESIZE = 32
STD_OUTPUT_HANDLE = -11
class COORD(Structure):
_fields_ = [
("X", SHORT),
("Y", SHORT),
]
class CONSOLE_FONT_INFOEX(Structure):
_fields_ = [
("cbSize", ULONG),
("nFont", DWORD),
("dwFontSize", COORD),
("FontFamily", UINT),
("FontWeight", UINT),
("FaceName", WCHAR * LF_FACESIZE)
]
kernel32_dll = WinDLL("kernel32.dll")
get_last_error_func = kernel32_dll.GetLastError
get_last_error_func.argtypes = []
get_last_error_func.restype = DWORD
get_std_handle_func = kernel32_dll.GetStdHandle
get_std_handle_func.argtypes = [DWORD]
get_std_handle_func.restype = HANDLE
get_current_console_font_ex_func = kernel32_dll.GetCurrentConsoleFontEx
get_current_console_font_ex_func.argtypes = [HANDLE, BOOL, POINTER(CONSOLE_FONT_INFOEX)]
get_current_console_font_ex_func.restype = BOOL
set_current_console_font_ex_func = kernel32_dll.SetCurrentConsoleFontEx
set_current_console_font_ex_func.argtypes = [HANDLE, BOOL, POINTER(CONSOLE_FONT_INFOEX)]
set_current_console_font_ex_func.restype = BOOL
stdout = get_std_handle_func(STD_OUTPUT_HANDLE)
font = CONSOLE_FONT_INFOEX()
font.cbSize = sizeof(CONSOLE_FONT_INFOEX)
font.dwFontSize.X = size
font.dwFontSize.Y = size
set_current_console_font_ex_func(stdout, False, byref(font))
Related
I am trying to make a console program using the pywin32 module
My code is:
import win32con, win32console, time, random
MyCon = win32console.CreateConsoleScreenBuffer(DesiredAccess = win32con.GENERIC_READ | win32con.GENERIC_WRITE, ShareMode = 0, SecurityAttributes = None, Flags = 1)
MyCon.SetConsoleActiveScreenBuffer()
rect = win32console.PySMALL_RECTType(20, 30, 600, 600)
MyCon.SetConsoleWindowInfo(Absolute = False, ConsoleWindow = rect)
while True:
y = str(random.randint(1,100)) + ' '
MyCon.WriteConsoleOutputCharacter(Characters = y, WriteCoord = win32console.PyCOORDType(5,6))
time.sleep(0.1)
This is the error i am facing when i am trying to run the program from cmd.exe
I am giving the parameters as said in the docs here http://timgolden.me.uk/pywin32-docs/PyConsoleScreenBuffer__SetConsoleWindowInfo_meth.html
How to fix this ?
According to [MS.Docs]: SetConsoleWindowInfo function - Remarks (emphasis is mine):
The function fails if the specified window rectangle extends beyond the boundaries of the console screen buffer. This means that the Top and Left members of the lpConsoleWindow rectangle (or the calculated top and left coordinates, if bAbsolute is FALSE) cannot be less than zero. Similarly, the Bottom and Right members (or the calculated bottom and right coordinates) cannot be greater than (screen buffer height – 1) and (screen buffer width – 1), respectively. The function also fails if the Right member (or calculated right coordinate) is less than or equal to the Left member (or calculated left coordinate) or if the Bottom member (or calculated bottom coordinate) is less than or equal to the Top member (or calculated top coordinate).
Your argument combination (relative coordinates + given rectangle) must have gone beyond screen buffer boundaries.
Below is an example that works.
code00.py:
#!/usr/bin/env python
import msvcrt
import random
import sys
import time
import win32api as wapi
import win32con as wcon
import win32console as wcons
def main(*argv):
buf = wcons.CreateConsoleScreenBuffer(DesiredAccess=wcon.GENERIC_READ | wcon.GENERIC_WRITE, ShareMode=0, SecurityAttributes=None, Flags=wcons.CONSOLE_TEXTMODE_BUFFER)
buf.SetConsoleActiveScreenBuffer()
#print("csbi: {:}\nlcws: {:}".format(buf.GetConsoleScreenBufferInfo(), buf.GetLargestConsoleWindowSize()))
so_buf = wcons.GetStdHandle(wcons.STD_OUTPUT_HANDLE)
so_csbi = so_buf.GetConsoleScreenBufferInfo()
#print("csbi: {:}\nlcws: {:}".format(so_csbi, so_buf.GetLargestConsoleWindowSize()))
#rect = wcons.PySMALL_RECTType(0, 1, 0, 1)
buf.SetConsoleWindowInfo(Absolute=True, ConsoleWindow=so_csbi["Window"])
write_coord = wcons.PyCOORDType(10, 4)
buf = so_buf # #TODO - cfati: Uncomment this line to write on the same output buffer (and thus preserve screen contents)
while not msvcrt.kbhit():
chrs = "------- {:d} -------".format(random.randint(1, 100))
buf.WriteConsoleOutputCharacter(Characters=chrs, WriteCoord=write_coord)
time.sleep(0.5)
wapi.CloseHandle(buf)
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.")
sys.exit(rc)
Output (last value after pressing a key):
[cfati#CFATI-5510-0:e:\Work\Dev\StackOverflow\q073678914]> "e:\Work\Dev\VEnvs\py_pc064_03.09_test0\Scripts\python.exe" ./code00.py
Python 3.9.9 (tags/v3.9.9:ccb0e6a, Nov 15 2021, 18:08:50) [MSC v.1929 64 bit (AMD64)] 064bit on win32
------- 63 -------
Done.
I was making an application for Night Light using Python. I'm using windows API for use of Gamma Ramp to accomplish my task.
I used EnumDisplayDevicesW from user32.dll to get the information and number of displays connected to my PC.
I have only one monitor connected to my desktop, but the output is giving information of two monitors.
Here's my code. I'm using Python and accessing WinAPI through ctypes module.
import ctypes
from ctypes import wintypes
class DISPLAY_DEVICEW(ctypes.Structure):
_fields_ = [
('cb', wintypes.DWORD),
('DeviceName', wintypes.WCHAR * 32),
('DeviceString', wintypes.WCHAR * 128),
('StateFlags', wintypes.DWORD),
('DeviceID', wintypes.WCHAR * 128),
('DeviceKey', wintypes.WCHAR * 128)
]
if __name__ == '__main__':
EnumDisplayDevices = ctypes.windll.user32.EnumDisplayDevicesW # get the function address
EnumDisplayDevices.restype = ctypes.c_bool # set return type to BOOL
displays = [] # to store display information
i = 0 # iteration variable for 'iDevNum'
while True:
INFO = DISPLAY_DEVICEW() # struct object
INFO.cb = ctypes.sizeof(INFO) # setting 'cnSize' prior to calling 'EnumDisplayDevicesW'
if not EnumDisplayDevices(None, i, ctypes.byref(INFO), 0):
break # break as soon as False is returned by 'EnumDisplayDevices'
displays.append(INFO) # append information to the list
i += 1
# display information in a sequential form
for x in displays:
print('DeviceName:\t\t', x.DeviceName)
print("DeviceString:\t", x.DeviceString)
print("StateFlags:\t\t", x.StateFlags)
print("DeviceID:\t\t", x.DeviceID)
print("DeviceKey:\t\t", x.DeviceKey)
print(), print()
And the output returned by the code is as follows:-
DeviceName: \\.\DISPLAY1
DeviceString: Intel(R) HD Graphics 510
StateFlags: 5
DeviceID: PCI\VEN_8086&DEV_1902&SUBSYS_D0001458&REV_06
DeviceKey: \Registry\Machine\System\CurrentControlSet\Control\Video\{C31A4E45-2A30-11EB-953B-92862920CE33}\0000
DeviceName: \\.\DISPLAY2
DeviceString: Intel(R) HD Graphics 510
StateFlags: 0
DeviceID: PCI\VEN_8086&DEV_1902&SUBSYS_D0001458&REV_06
DeviceKey: \Registry\Machine\System\CurrentControlSet\Control\Video\{C31A4E45-2A30-11EB-953B-92862920CE33}\0001
As far as I know, the first one, i.e, \\.\DISPLAY1 is mine one, but why there's a need for a second one??
I own a Desktop PC with standard Samsung monitor.
Any help will be very helpful. Thanks in advance!
I have only one monitor connected to my desktop, but the output is giving information of two monitors.
Running this code does not tell you that you have two "monitors", but two "adapters".
According to EnumDisplayDevicesW:
To get information on the display adapter, call EnumDisplayDevices with lpDevice set to NULL. For example, DISPLAY_DEVICE.DeviceString contains the adapter name.
To obtain information on a display monitor, first call EnumDisplayDevices with lpDevice set to NULL. Then call EnumDisplayDevices with lpDevice set to DISPLAY_DEVICE.DeviceName from the first call to EnumDisplayDevices and with iDevNum set to zero. Then DISPLAY_DEVICE.DeviceString is the monitor name.
So if you need to get the monitor information, you need to call:
EnumDisplayDevices(INFO.DeviceName,j,ctypes.byref(Monitor_INFO),0):
Here is a sample:
import ctypes
from ctypes import wintypes
class DISPLAY_DEVICEW(ctypes.Structure):
_fields_ = [
('cb', wintypes.DWORD),
('DeviceName', wintypes.WCHAR * 32),
('DeviceString', wintypes.WCHAR * 128),
('StateFlags', wintypes.DWORD),
('DeviceID', wintypes.WCHAR * 128),
('DeviceKey', wintypes.WCHAR * 128)
]
if __name__ == '__main__':
EnumDisplayDevices = ctypes.windll.user32.EnumDisplayDevicesW # get the function address
EnumDisplayDevices.restype = ctypes.c_bool # set return type to BOOL
displays = [] # to store display information
i = 0 # iteration variable for 'iDevNum'
j = 0
while True:
INFO = DISPLAY_DEVICEW() # struct object
INFO.cb = ctypes.sizeof(INFO) # setting 'cnSize' prior to calling 'EnumDisplayDevicesW'
Monitor_INFO = DISPLAY_DEVICEW()
Monitor_INFO.cb = ctypes.sizeof(Monitor_INFO)
if not EnumDisplayDevices(None, i, ctypes.byref(INFO), 0):
break # break as soon as False is returned by 'EnumDisplayDevices'
#j = 0
while EnumDisplayDevices(INFO.DeviceName,j,ctypes.byref(Monitor_INFO),0):
print("monitor name:\t\t",Monitor_INFO.DeviceName,'\n\n')
j+=1
displays.append(INFO) # append information to the list
i += 1
# display information in a sequential form
for x in displays:
print('DeviceName:\t\t', x.DeviceName)
print("DeviceString:\t", x.DeviceString)
print("StateFlags:\t\t", x.StateFlags)
print("DeviceID:\t\t", x.DeviceID)
print("DeviceKey:\t\t", x.DeviceKey)
print(), print()
So, after doing required changes, following is the final code to get all the display monitors connected to all the display adapters.
import ctypes
from ctypes import wintypes
class DISPLAY_DEVICEW(ctypes.Structure):
_fields_ = [
('cb', wintypes.DWORD),
('DeviceName', wintypes.WCHAR * 32),
('DeviceString', wintypes.WCHAR * 128),
('StateFlags', wintypes.DWORD),
('DeviceID', wintypes.WCHAR * 128),
('DeviceKey', wintypes.WCHAR * 128)
]
if __name__ == '__main__':
EnumDisplayDevices = ctypes.windll.user32.EnumDisplayDevicesW # get the function address
EnumDisplayDevices.restype = ctypes.c_bool # set return type to BOOL
"""
the following list 'displays', stores display adapter info in the following Structure:
'List containing Tuple of displayAdapterInfo and list of monitorInfo controlled by the adapter'
[
(dispAdapterInfo1, [Monitor1, Monitor2, . . . ]),
(dispAdapterInfo2, [Monitor1, Monitor2, . . . ]),
. . . .
]
Number of dispAdapter depends on the graphics driver, and number of Monitors per adapter depends on
number of monitors connected and controlled by adapter.
"""
displays = []
i = 0 # iteration variable for 'iDevNum'
while True:
DISP_INFO = DISPLAY_DEVICEW() # struct object for adapter info
DISP_INFO.cb = ctypes.sizeof(DISP_INFO) # setting 'cb' prior to calling 'EnumDisplayDevicesW'
if not EnumDisplayDevices(None, i, ctypes.byref(DISP_INFO), 0):
break # break as soon as False is returned by 'EnumDisplayDevices'
monitors = [] # stores list of monitors per adapter
j = 0
while True:
MONITR_INFO = DISPLAY_DEVICEW() # struct object for Monitor info
MONITR_INFO.cb = ctypes.sizeof(MONITR_INFO) # setting 'cb' prior to calling 'EnumDisplayDevicesW'
if not EnumDisplayDevices(DISP_INFO.DeviceName, j, ctypes.byref(MONITR_INFO), 0):
break # break as soon as False is returned by 'EnumDisplayDevices'
monitors.append(MONITR_INFO)
j += 1
displays.append((DISP_INFO, monitors)) # add the tuple (dispAdapterInfo, [MonitorsInfo])
i += 1
for display in displays:
if display[1]: # filter out the adapter with no monitor connected, i.e, empty list
print("Adapter object:", display[0])
print("List of Monitor objects :", display[1])
print()
Now, display[0] is the adapter object, and can be used to get the information by referring the above DISPLAY_DEVICEW class, and display[1] is the list of monitors, which can be iterated over to get the objects of the monitor info, and get the information about it by referring DISPLAY_DEVICEW class.
I'd like to incorporate a way to have my GUI open on a second monitor, if available. I want to add in some type of error handling so if there is a second monitor, use it, else open on the center of the detected display. Can this be done?
I've been using this snippet using the win32 API to enumerate monitors on windows:
import ctypes
user = ctypes.windll.user32
class RECT(ctypes.Structure):
_fields_ = [
('left', ctypes.c_long),
('top', ctypes.c_long),
('right', ctypes.c_long),
('bottom', ctypes.c_long)
]
def dump(self):
return [int(val) for val in (self.left, self.top, self.right, self.bottom)]
class MONITORINFO(ctypes.Structure):
_fields_ = [
('cbSize', ctypes.c_ulong),
('rcMonitor', RECT),
('rcWork', RECT),
('dwFlags', ctypes.c_ulong)
]
def get_monitors():
retval = []
CBFUNC = ctypes.WINFUNCTYPE(ctypes.c_int, ctypes.c_ulong, ctypes.c_ulong, ctypes.POINTER(RECT), ctypes.c_double)
def cb(hMonitor, hdcMonitor, lprcMonitor, dwData):
r = lprcMonitor.contents
#print("cb: %s %s %s %s %s %s %s %s" % (hMonitor, type(hMonitor), hdcMonitor, type(hdcMonitor), lprcMonitor, type(lprcMonitor), dwData, type(dwData)))
data = [hMonitor]
data.append(r.dump())
retval.append(data)
return 1
cbfunc = CBFUNC(cb)
temp = user.EnumDisplayMonitors(0, 0, cbfunc, 0)
#print(temp)
return retval
def monitor_areas():
retval = []
monitors = get_monitors()
for hMonitor, extents in monitors:
data = [hMonitor]
mi = MONITORINFO()
mi.cbSize = ctypes.sizeof(MONITORINFO)
mi.rcMonitor = RECT()
mi.rcWork = RECT()
res = user.GetMonitorInfoA(hMonitor, ctypes.byref(mi))
data = mi.rcMonitor.dump()
# data.append(mi.rcWork.dump())
retval.append(data)
return retval
if __name__ == "__main__":
print(monitor_areas())
On my system that prints
[[0, 0, 3440, 1440], [3440, 0, 5120, 1050]]
In tkinter you can then move your app window to a given location with
appwindow.geometry('+1720+720')
Which puts it 1720px in from the left and 720 down from the top.
Any monitors that are to the left of or above your primary monitor may get negative coordinates, but they should work just fine.
On MacOS, use Appkit:
import AppKit
[(screen.frame().size.width, screen.frame().size.height)
for screen in AppKit.NSScreen.screens()]
will give you a list of tuples containing all screen sizes (if multiple monitors present)
In Python2.7 with win32, I can easily get other window's toolStrip1 handle through win32gui.EnumChildWindows, however I do not know how to get it's button item control through it's item name, e.g click item button "Load" in toolStrip1.
Can I try to use win32gui.SendMessage(handle, click, "Load"......) or a similar way to achieve this?
I can use rect x,y of toolStrip1 via win32gui.GetWindowRect, and roughly can do click button via x+40, x+80 accordingly, but it is not really good specially when the item position changes in the new version.
Here is the code from a sample and modified with a few other issues:
import win32con
import commctrl, ctypes
from ctypes import *
import sys
import time
class TextBox:
# represent the TBBUTTON structure
# note this is 32 bit, 64 bit padds 4 more reserved bytes
class TBBUTTON(Structure):
_pack_ = 1
_fields_ = [
('iBitmap', c_int),
('idCommand', c_int),
('fsState', c_ubyte),
('fsStyle', c_ubyte),
('bReserved', c_ubyte * 2),
('dwData', c_ulong),
('iString', c_int),
]
class RECT(Structure):
_pack_ = 1
_fields_ = [
('left',c_ulong),
('top',c_ulong),
('right',c_ulong),
('bottom',c_ulong),
]
def run(self):
#init vars
self.count=0
#get the TestApp window
lhWnd = win32gui.FindWindow(None,'TestApp')
print "TestApp handle=",lhWnd
#get the TestApp child window
win32gui.EnumChildWindows(lhWnd, self.EnumChildWindows, 0)
print "toolStrip1 handle=",self.toolbar_hwnd
#focus TestApp window
ctypes.windll.user32.SetFocus(lhWnd)
win32gui.SetForegroundWindow(lhWnd)
win32gui.ShowWindow(lhWnd, win32con.SW_SHOWNORMAL)
#############################################donot work part####################################################
#to get how many item buttons in toolStrip1, it return 0 -- WHY? <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
cnt = windll.user32.SendMessageA(self.toolbar_hwnd, commctrl.TB_BUTTONCOUNT, 0, 0)
print "Why button cnt=0?? cnt=",cnt
#and seems this is no affection as well -- Why? <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
index=1 #assume we got Load button index from it's name (do not know how?, Load Button index=1 based on 0
win32api.PostMessage(self.toolbar_hwnd,commctrl.TCM_SETCURFOCUS,index,0) #TCM_SETCURSEL
print "Why?? cannot focus on [Load] Button"
time.sleep(3)
#try to get [Load] button's rect, but not work <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
pid = c_ulong();
windll.user32.GetWindowThreadProcessId(self.toolbar_hwnd, byref(pid))
hProcess = windll.kernel32.OpenProcess(win32con.PROCESS_ALL_ACCESS, 0, pid)
lpPointer = windll.kernel32.VirtualAllocEx(hProcess, 0, sizeof(TBBUTTON), win32con.MEM_COMMIT, win32con.PAGE_READWRITE)
rlpPointer = windll.kernel32.VirtualAllocEx(hProcess, 0, sizeof(RECT), win32con.MEM_COMMIT, win32con.PAGE_READWRITE)
# init our tool bar button and a handle to it
tbButton = TBBUTTON()
butHandle = c_int()
idx_rect = RECT()
# query the button into the memory we allocated
windll.user32.SendMessageA(self.toolbar_hwnd, commctrl.TB_GETBUTTON, index, lpPointer)
# read the memory into our button struct
windll.kernel32.ReadProcessMemory(hProcess, lpPointer, addressof(tbButton), 20, None)
# read the 1st 4 bytes from the dwData into the butHandle var
# these first 4 bytes contain the handle to the button
windll.kernel32.ReadProcessMemory(hProcess, tbButton.dwData, addressof(butHandle), 4, None)
# get the pid that created the button
butPid = c_ulong()
windll.user32.GetWindowThreadProcessId(butHandle, byref(butPid))
wszBuff = create_unicode_buffer(win32con.MAX_PATH)
windll.kernel32.ReadProcessMemory(hProcess, tbButton.iString, wszBuff, win32con.MAX_PATH, None)
win32api.SendMessage(self.toolbar_hwnd,commctrl.TB_GETRECT,tbButton.idCommand,rlpPointer)
windll.kernel32.ReadProcessMemory(hProcess, rlpPointer, addressof(idx_rect), sizeof(idx_rect), None)
xpos = int((idx_rect.right-idx_rect.left)/2)+idx_rect.left
ypos = int((idx_rect.bottom-idx_rect.top)/2)+idx_rect.top
lParam = ypos<<16 | xpos
print "Why x,y=0?? [Load] button X,Y=",xpos,ypos
###############################################################################################################
#but assume we got button [Load] Rect[10,80] based on toolStrip1, and click it WORKS!
lParam = 10<<16 | 80
win32api.PostMessage(self.toolbar_hwnd,win32con.WM_LBUTTONDOWN,win32con.MK_LBUTTON,lParam)
win32api.PostMessage(self.toolbar_hwnd,win32con.WM_LBUTTONUP,win32con.MK_LBUTTON,lParam)
def EnumChildWindows(self,lhWnd,lParam):
text = win32gui.GetWindowText(lhWnd)
#rect1 = win32gui.GetWindowRect(lhWnd)
if text=="toolStrip1":
self.toolbar_hwnd=lhWnd
rx=TextBox()
rx.run()
This page says:
Enumeration types are not implemented. You can do it easily yourself,
using c_int as the base class.
If it were easy, why isn't it implemented yet? How do I get the current color temperature of my monitor, for instance?
BOOL GetMonitorColorTemperature(
_In_ HANDLE hMonitor,
_Out_ LPMC_COLOR_TEMPERATURE pctCurrentColorTemperature
);
Parameters
hMonitor [in]
Handle to a physical monitor. To get the monitor handle, call GetPhysicalMonitorsFromHMONITOR or GetPhysicalMonitorsFromIDirect3DDevice9.
pctCurrentColorTemperature [out]
Receives the monitor's current color temperature, specified as a member of the MC_COLOR_TEMPERATURE enumeration.
Return value
If the function succeeds, the return value is TRUE. If the function fails, the return value is FALSE. To get extended error information, call GetLastError.
This is the closest i was able to get:
from ctypes import *
import win32api # http://sourceforge.net/projects/pywin32/files/pywin32/
for idx, (hMon, hDC, (left, top, right, bottom)) in enumerate(win32api.EnumDisplayMonitors(None, None)):
print(hMon.handle) # or int(hMon)
class MyEnum(c_int):
MC_COLOR_TEMPERATURE_UNKNOWN = 0
MC_COLOR_TEMPERATURE_4000K = 1
MC_COLOR_TEMPERATURE_5000K = 2
MC_COLOR_TEMPERATURE_6500K = 3
MC_COLOR_TEMPERATURE_7500K = 4
MC_COLOR_TEMPERATURE_8200K = 5
MC_COLOR_TEMPERATURE_9300K = 6
MC_COLOR_TEMPERATURE_10000K = 7
MC_COLOR_TEMPERATURE_11500K = 8
o = MyEnum()
print(o)
po = pointer(o)
t = windll.dxva2.GetMonitorColorTemperature(hMon.handle, po) #byref(o))
#print(dir(o))
print(o)
print(po.contents)
print(t)
print(windll.kernel32.GetLastError()) # ERROR_GRAPHICS_INVALID_PHYSICAL_MONITOR_HANDLE = -1071241844 # Variable c_long '-0x03fd9da74'
...returns this:
65537
65539
<MyEnum object at 0x006F6DA0>
<MyEnum object at 0x006F6DA0>
<MyEnum object at 0x0234FAD0>
0
-1071241844
It's the same for either monitor handle. What am I doing wrong?
Using this answer, I somehow found out that it works using None as physical monitor handle:
from ctypes import *
from ctypes.wintypes import BOOL, HMONITOR, HDC, RECT, LPARAM, DWORD, BYTE, WCHAR, HANDLE
import win32api # http://sourceforge.net/projects/pywin32/files/pywin32/
_MONITORENUMPROC = WINFUNCTYPE(BOOL, HMONITOR, HDC, POINTER(RECT), LPARAM)
class _PHYSICAL_MONITOR(Structure):
_fields_ = [('handle', HANDLE),
('description', WCHAR * 128)]
def _iter_physical_monitors(close_handles=True):
"""Iterates physical monitors.
The handles are closed automatically whenever the iterator is advanced.
This means that the iterator should always be fully exhausted!
If you want to keep handles e.g. because you need to store all of them and
use them later, set `close_handles` to False and close them manually."""
def callback(hmonitor, hdc, lprect, lparam):
monitors.append(hmonitor)
return True
monitors = []
if not windll.user32.EnumDisplayMonitors(None, None, _MONITORENUMPROC(callback), None):
raise WinError('EnumDisplayMonitors failed')
for monitor in monitors:
# Get physical monitor count
count = DWORD()
if not windll.dxva2.GetNumberOfPhysicalMonitorsFromHMONITOR(monitor, byref(count)):
raise WinError()
# Get physical monitor handles
physical_array = (_PHYSICAL_MONITOR * count.value)()
if not windll.dxva2.GetPhysicalMonitorsFromHMONITOR(monitor, count.value, physical_array):
raise WinError()
for physical in physical_array:
yield physical.handle
if close_handles:
if not windll.dxva2.DestroyPhysicalMonitor(physical.handle):
raise WinError()
mons = [m for m in _iter_physical_monitors(False)]
#for idx, (hMon, hDC, (left, top, right, bottom)) in enumerate(win32api.EnumDisplayMonitors(None, None)):
# print(hMon.handle) # or int(hMon)
temps = (
'UNKNOWN',
'4000K',
'5000K',
'6500K',
'7500K',
'8200K',
'9300K',
'10000K',
'11500K'
)
class MyEnum(c_int):
MC_COLOR_TEMPERATURE_UNKNOWN = 0
MC_COLOR_TEMPERATURE_4000K = 1
MC_COLOR_TEMPERATURE_5000K = 2
MC_COLOR_TEMPERATURE_6500K = 3
MC_COLOR_TEMPERATURE_7500K = 4
MC_COLOR_TEMPERATURE_8200K = 5
MC_COLOR_TEMPERATURE_9300K = 6
MC_COLOR_TEMPERATURE_10000K = 7
MC_COLOR_TEMPERATURE_11500K = 8
o = MyEnum()
print(o)
po = pointer(o)
pm = mons[0]
print("physical %r" % pm)
t = windll.dxva2.GetMonitorColorTemperature(pm, po) #byref(o))
if t:
#print(o)
#print(dir(po.contents))
print(temps[po.contents.value])
else:
print("Err: %s" % windll.kernel32.GetLastError()) # ERROR_GRAPHICS_INVALID_PHYSICAL_MONITOR_HANDLE = -1071241844 # Variable c_long '-0x03fd9da74'
Result:
<MyEnum object at 0x005D6120>
physical None
6500K