Программный поворот монитора

Я работаю над созданием утилиты 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: Процедура поворота монитора:

  • Получить Devmode

  • Получить DisplayName

  • Установить поворот

  • Настройте флаги с помощью полей a.Fields = a.Fields и win32con.DM_DISPLAYORIENTATION

  • Вызвать ChangeDisplaySettingsEx

В вашем 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, очевидно, лучше, потому что он более надежный. Я опубликовал этот метод только как пример более простого способа сделать это.