Программный поворот монитора
Я работаю над созданием утилиты script, которая выполняет целую кучу вещей. Одна из вещей, которую я хочу сделать, - повернуть дисплей; У меня есть несколько мониторов, и я хочу, чтобы главный вращался. Я знаю, что такие вещи обычно работают через win32api
, и я нашел там несколько функций, которые кажутся полезными, но я борюсь с реализацией.
Все, что находится между этой строкой и следующей, устарело, см. ниже вторую строку для современного описания попыток решения.
После того, как я уткнулся лицом в документы, я боюсь, что у меня все еще нет большой идеи о том, как двигаться дальше, кроме того, что это, скорее всего, связано с win32api.ChangeDisplaySettingsEx()
. Я знаю, что мне нужно дать этой функции указатель на объект DEVMODE
(даже не знаю, как сделать C-указатели в python), который, я думаю, можно получить из win32api.EnumDisplaySettingsEx()
. Поэтому, если я попытаюсь,
>>> import win32api as win32
>>> a = win32.EnumDisplayDevices()
>>> type(a)
Я должен получить что-то, что включает в себя указатель DEVMODE
или что-то еще, но вместо этого я получаю
>>> type(a)
<type 'PyDISPLAY_DEVICE'>
И я понятия не имею, что с этим делать, но я думаю это структура
Итак, как мне получить DEVMODE
ojbect, который я могу передать ChangeDisplaySettingsEx()
, чтобы я мог поворачивать один из моих дисплеев? Спасибо заранее.
Я запускаю Python 2.7 на Windows 7
EDIT: Если я использую правильную функцию, она по-прежнему не работает. Может ли это быть Python-модуль не полным?
>>> a = win32.EnumDisplaySettings()
>>> type(a)
<type 'PyDEVMODEA'>
>>> a.dmSize
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'PyDEVMODEA' object has no attribute 'dmSize'
>>> a.dmScale
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'PyDEVMODEA' object has no attribute 'dmScale'
>>> a.dmDisplayOrientation
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'PyDEVMODEA' object has no attribute 'dmDisplayOrientation'
Теперь я заметил, что это дает мне объект DEVMODEA
вместо DEVMODE
, но эта страница говорит, что это одна и та же. Что может быть проблемой здесь?
EDIT: Теперь, когда я использую правильные имена атрибутов, я могу получить действительный объект DEVMODEA
:
>>> a = win32.EnumDisplaySettings()
>>> a.Size
124L
>>> a.DisplayOrientation
0L
И измените ориентацию в объекте:
>>> a.DisplayOrientation = 90L
>>> a.DisplayOrientation
90L
Затем я могу попытаться применить эти изменения, предоставив этому объекту DEVMODEA
значение ChangeDisplaySettingsEx()
>>> win32.ChangeDisplaySettingsEx(a.DeviceName, a, 0)
-5L
Это ничего не делает. К сожалению, документы не очень полезны, помогая мне интерпретировать возвращаемое значение. Я предполагаю, что -5L
- это своего рода код ошибки, так как он не работает, но я не знаю, какой из них. Что означает это возвращаемое значение и как мне получить мой новый объект DEVMODEA
для "применения"
Я выяснил, что возвращаемое значение -5L
указывает на плохой параметр. В частности, он сердится на первое поле. Если я заменил это на DISPLAY_DEVICE.DeviceName
, я получаю результат -2L
. Это соответствует плохому режиму (независимо от того, что есть). Это происходит, даже если я даю ChangeDisplaySettingsEx()
то, что выдает EnumDisplaySettings()
.
Итак, мой прогресс:
>>> import win32api as win32
>>> import win32con
>>> a = win32.EnumDisplaySettings()
>>> a.DisplayOrientation
0L
>>> a.DisplayOrientation = win32con.DMDO_90
>>> a.DisplayOrientation
1L
>>> a.PelsWidth, a.PelsHeight = a.PelsHeight, a.PelsWidth
>>> a.Fields = a.Fields & win32con.DM_DISPLAYORIENTATION
>>> name = win32.EnumDisplayDevices().DeviceName
>>> name
'\\\\.\\DISPLAY1'
>>> win32.ChangeDisplaySettingsEx(name, a)
-2L
Последняя попытка (6/4, 10:50 утра EST)
Python 2.7.6 (default, Nov 10 2013, 19:24:18) [MSC v.1500 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import win32api as win32
>>> import win32con
>>>
>>> device = win32.EnumDisplayDevices(None, 1)
>>> print "Rotate device {} ({})".format(device.DeviceString, device.DeviceName)
Rotate device Intel(R) HD Graphics 4000 (\\.\DISPLAY2)
>>>
>>> dm = win32.EnumDisplaySettings(device.DeviceName, win32con.ENUM_CURRENT_SETTINGS)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
pywintypes.error: (0, 'EnumDisplaySettings', 'No error message is available')
>>>
>>> dm = win32.EnumDisplaySettings(device.DeviceName)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
pywintypes.error: (123, 'EnumDisplaySettings', 'The filename, directory name, or volume label syntax is incorrect.')
>>>
>>> dm = win32.EnumDisplaySettings(device.DeviceName, win32con.ENUM_CURRENT_SETTINGS)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
pywintypes.error: (123, 'EnumDisplaySettings', 'The filename, directory name, or volume label syntax is incorrect.')
>>>
Обратите внимание, что первая и третья попытка создания dm
терпят неудачу с разными ошибками, несмотря на то, что они являются одним и тем же кодом.
Ответы
Ответ 1
Вы вызываете API EnumDisplayDevices
, который возвращает PDISPLAY_DEVICE.
(см. http://msdn.microsoft.com/en-us/library/windows/desktop/dd162609(v=vs.85).aspx)
Вы можете получить объект двумя способами:
1) Из EnumDisplayDevicesEx
>>> import win32api
>>> win32api.EnumDisplaySettingsEx()
<PyDEVMODEA object at 0x00512E78>
2) или создать его (он будет пустым)
>>> import pywintypes;
>>> dmode = pywintypes.DEVMODEType()
>>> type(dmode)
<type 'PyDEVMODEA'>
EDIT:
свойство, выставленное объектом, не, названное как версия win32: например dmSize
становится Size
, полный список можно увидеть с помощью команды dir(dmode)
на PyDEVMODEA
объект.
Описание полей можно прочитать с помощью команды help(dmode)
объекта PyDEVMODEA
.
Для полного подробного сопоставления обратитесь к PyDEVMODE.cpp
внутри дистрибутива источника pywin32
EDIT2:
Процедура поворота монитора:
В вашем script вам не хватает шагов с 3-5. (также поля используются как двоичная маска, поэтому они должны обрабатываться правильно).
Вы можете увидеть полный пример использования api (в C) на сайте msdn:
http://msdn.microsoft.com/en-us/library/ms812499.aspx
Edit3
Во время "Set Rotation" вам также необходимо поменять ширину и высоту, иначе вы попросите экранный режим, который невозможен:
(dmode.PelsWidth,dmode.PelsHeight) = (dmode.PelsHeight,dmode.PelsWidth)
EDIT4
Полный пример (без проверки ошибок):
import win32api as win32
import win32con
def printAllScreen():
i = 0
while True:
try:
device = win32.EnumDisplayDevices(None,i);
print("[%d] %s (%s)"%(i,device.DeviceString,device.DeviceName));
i = i+1;
except:
break;
return i
screen_count=printAllScreen()
x = int(input("\nEnter a display number [0-%d]: "%screen_count-1))
device = win32.EnumDisplayDevices(None,x);
print("Rotate device %s (%s)"%(device.DeviceString,device.DeviceName));
dm = win32.EnumDisplaySettings(device.DeviceName,win32con.ENUM_CURRENT_SETTINGS)
dm.DisplayOrientation = win32con.DMDO_90
dm.PelsWidth, dm.PelsHeight = dm.PelsHeight, dm.PelsWidth
dm.Fields = dm.Fields & win32con.DM_DISPLAYORIENTATION
win32.ChangeDisplaySettingsEx(device.DeviceName,dm)
Ответ 2
Мой ответ немного взломан, но он работает. Он основан на том, что экран вращается, когда пользователь нажимает специальную комбинацию клавиш.
from win32api import keybd_event
def rotate_screen(orientation):
vals = dict(zip(['left', 'up', 'right', 'down'],
[37, 38, 39, 40]))
comb = 165, vals[orientation]
for k in comb:
keybd_event(k, 0, 1, 0)
for k in reversed(comb):
keybd_event(k, 0, 2, 0)
Пример:
rotate_screen('down')
Примечание: подход Ibenini, очевидно, лучше, потому что он более надежный. Я опубликовал этот метод только как пример более простого способа сделать это.