You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
940 lines
33 KiB
940 lines
33 KiB
6 years ago
|
# GUI Application automation and testing library
|
||
|
# Copyright (C) 2006-2018 Mark Mc Mahon and Contributors
|
||
|
# https://github.com/pywinauto/pywinauto/graphs/contributors
|
||
|
# http://pywinauto.readthedocs.io/en/latest/credits.html
|
||
|
# All rights reserved.
|
||
|
#
|
||
|
# Redistribution and use in source and binary forms, with or without
|
||
|
# modification, are permitted provided that the following conditions are met:
|
||
|
#
|
||
|
# * Redistributions of source code must retain the above copyright notice, this
|
||
|
# list of conditions and the following disclaimer.
|
||
|
#
|
||
|
# * Redistributions in binary form must reproduce the above copyright notice,
|
||
|
# this list of conditions and the following disclaimer in the documentation
|
||
|
# and/or other materials provided with the distribution.
|
||
|
#
|
||
|
# * Neither the name of pywinauto nor the names of its
|
||
|
# contributors may be used to endorse or promote products derived from
|
||
|
# this software without specific prior written permission.
|
||
|
#
|
||
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||
|
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||
|
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||
|
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||
|
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||
|
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||
|
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||
|
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||
|
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||
|
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||
|
|
||
|
"""Wraps various standard windows controls"""
|
||
|
from __future__ import unicode_literals
|
||
|
|
||
|
import time
|
||
|
import ctypes
|
||
|
import win32gui
|
||
|
import win32api
|
||
|
import win32con
|
||
|
import win32process
|
||
|
import locale
|
||
|
import six
|
||
|
|
||
|
from . import hwndwrapper
|
||
|
|
||
|
from .. import win32functions
|
||
|
from .. import win32defines
|
||
|
from .. import win32structures
|
||
|
|
||
|
from ..timings import Timings
|
||
|
from .. import deprecated
|
||
|
|
||
|
#====================================================================
|
||
|
class ButtonWrapper(hwndwrapper.HwndWrapper):
|
||
|
|
||
|
"""Wrap a windows Button control"""
|
||
|
|
||
|
friendlyclassname = "Button"
|
||
|
windowclasses = [
|
||
|
"Button",
|
||
|
".*Button",
|
||
|
r"WindowsForms\d*\.BUTTON\..*",
|
||
|
".*CheckBox",
|
||
|
]
|
||
|
can_be_label = True
|
||
|
|
||
|
#-----------------------------------------------------------
|
||
|
def __init__(self, hwnd):
|
||
|
"""Initialize the control"""
|
||
|
super(ButtonWrapper, self).__init__(hwnd)
|
||
|
|
||
|
#self._set_if_needs_image()
|
||
|
|
||
|
@property
|
||
|
def _needs_image_prop(self):
|
||
|
"""_needs_image_prop=True if it is an image button"""
|
||
|
# optimization call style once and work with that rather than
|
||
|
# calling has_style a number of times
|
||
|
style = self.style()
|
||
|
|
||
|
if self.is_visible() and (style & win32defines.BS_BITMAP or
|
||
|
style & win32defines.BS_ICON or
|
||
|
style & win32defines.BS_OWNERDRAW):
|
||
|
return True
|
||
|
else:
|
||
|
return False
|
||
|
# Non PEP-8 alias
|
||
|
_NeedsImageProp = deprecated(_needs_image_prop, deprecated_name='_NeedsImageProp')
|
||
|
|
||
|
#-----------------------------------------------------------
|
||
|
def friendly_class_name(self):
|
||
|
"""
|
||
|
Return the friendly class name of the button
|
||
|
|
||
|
Windows controls with the class "Button" can look like different
|
||
|
controls based on their style. They can look like the following
|
||
|
controls:
|
||
|
|
||
|
- Buttons, this method returns "Button"
|
||
|
- CheckBoxes, this method returns "CheckBox"
|
||
|
- RadioButtons, this method returns "RadioButton"
|
||
|
- GroupBoxes, this method returns "GroupBox"
|
||
|
"""
|
||
|
# get the least significant BIT
|
||
|
style_lsb = self.style() & 0xF
|
||
|
|
||
|
f_class_name = 'Button'
|
||
|
|
||
|
vb_buttons = {
|
||
|
"ThunderOptionButton": "RadioButton",
|
||
|
"ThunderCheckBox": "CheckBox",
|
||
|
"ThunderCommandButton": "Button"
|
||
|
}
|
||
|
|
||
|
if self.class_name() in vb_buttons:
|
||
|
f_class_name = vb_buttons[self.class_name()]
|
||
|
|
||
|
if style_lsb in [win32defines.BS_3STATE,
|
||
|
win32defines.BS_AUTO3STATE,
|
||
|
win32defines.BS_AUTOCHECKBOX,
|
||
|
win32defines.BS_CHECKBOX, ]:
|
||
|
f_class_name = "CheckBox"
|
||
|
elif style_lsb in [win32defines.BS_RADIOBUTTON,
|
||
|
win32defines.BS_AUTORADIOBUTTON, ]:
|
||
|
f_class_name = "RadioButton"
|
||
|
elif style_lsb == win32defines.BS_GROUPBOX:
|
||
|
f_class_name = "GroupBox"
|
||
|
|
||
|
if self.style() & win32defines.BS_PUSHLIKE:
|
||
|
f_class_name = "Button"
|
||
|
|
||
|
return f_class_name
|
||
|
|
||
|
#-----------------------------------------------------------
|
||
|
def get_check_state(self):
|
||
|
"""
|
||
|
Return the check state of the checkbox
|
||
|
|
||
|
The check state is represented by an integer
|
||
|
0 - unchecked
|
||
|
1 - checked
|
||
|
2 - indeterminate
|
||
|
|
||
|
The following constants are defined in the win32defines module
|
||
|
BST_UNCHECKED = 0
|
||
|
BST_CHECKED = 1
|
||
|
BST_INDETERMINATE = 2
|
||
|
"""
|
||
|
self._ensure_enough_privileges('BM_GETCHECK')
|
||
|
return self.send_message(win32defines.BM_GETCHECK)
|
||
|
# Non PEP-8 alias
|
||
|
GetCheckState = deprecated(get_check_state)
|
||
|
|
||
|
__check_states = {
|
||
|
win32defines.BST_UNCHECKED: False,
|
||
|
win32defines.BST_CHECKED: True,
|
||
|
win32defines.BST_INDETERMINATE: None,
|
||
|
}
|
||
|
#-----------------------------------------------------------
|
||
|
def is_checked(self):
|
||
|
"""Return True if checked, False if not checked, None if indeterminate"""
|
||
|
return self.__check_states[self.get_check_state()]
|
||
|
|
||
|
#-----------------------------------------------------------
|
||
|
def check(self):
|
||
|
"""Check a checkbox"""
|
||
|
self._ensure_enough_privileges('BM_SETCHECK')
|
||
|
self.send_message_timeout(win32defines.BM_SETCHECK,
|
||
|
win32defines.BST_CHECKED)
|
||
|
|
||
|
win32functions.WaitGuiThreadIdle(self)
|
||
|
time.sleep(Timings.after_buttoncheck_wait)
|
||
|
|
||
|
# return this control so that actions can be chained.
|
||
|
return self
|
||
|
# Non PEP-8 alias
|
||
|
Check = deprecated(check)
|
||
|
|
||
|
#-----------------------------------------------------------
|
||
|
def uncheck(self):
|
||
|
"""Uncheck a checkbox"""
|
||
|
self._ensure_enough_privileges('BM_SETCHECK')
|
||
|
self.send_message_timeout(win32defines.BM_SETCHECK,
|
||
|
win32defines.BST_UNCHECKED)
|
||
|
|
||
|
win32functions.WaitGuiThreadIdle(self)
|
||
|
time.sleep(Timings.after_buttoncheck_wait)
|
||
|
|
||
|
# return this control so that actions can be chained.
|
||
|
return self
|
||
|
# Non PEP-8 alias
|
||
|
UnCheck = deprecated(uncheck, deprecated_name='UnCheck')
|
||
|
|
||
|
#-----------------------------------------------------------
|
||
|
def set_check_indeterminate(self):
|
||
|
"""Set the checkbox to indeterminate"""
|
||
|
self._ensure_enough_privileges('BM_SETCHECK')
|
||
|
self.send_message_timeout(win32defines.BM_SETCHECK,
|
||
|
win32defines.BST_INDETERMINATE)
|
||
|
|
||
|
win32functions.WaitGuiThreadIdle(self)
|
||
|
time.sleep(Timings.after_buttoncheck_wait)
|
||
|
|
||
|
# return this control so that actions can be chained.
|
||
|
return self
|
||
|
# Non PEP-8 alias
|
||
|
SetCheckIndeterminate = deprecated(set_check_indeterminate)
|
||
|
|
||
|
#-----------------------------------------------------------
|
||
|
def is_dialog(self):
|
||
|
"""Buttons are never dialogs so return False"""
|
||
|
return False
|
||
|
|
||
|
#-----------------------------------------------------------
|
||
|
def click(self, *args, **kwargs):
|
||
|
"""Click the Button control"""
|
||
|
#import win32functions
|
||
|
#win32functions.WaitGuiThreadIdle(self)
|
||
|
#self.notify_parent(win32defines.BN_CLICKED)
|
||
|
hwndwrapper.HwndWrapper.click(self, *args, **kwargs)
|
||
|
#win32functions.WaitGuiThreadIdle(self)
|
||
|
time.sleep(Timings.after_button_click_wait)
|
||
|
|
||
|
#-----------------------------------------------------------
|
||
|
def check_by_click(self):
|
||
|
"""Check the CheckBox control by click() method"""
|
||
|
if self.get_check_state() != win32defines.BST_CHECKED:
|
||
|
self.click()
|
||
|
# Non PEP-8 alias
|
||
|
CheckByClick = deprecated(check_by_click)
|
||
|
|
||
|
#-----------------------------------------------------------
|
||
|
def uncheck_by_click(self):
|
||
|
"""Uncheck the CheckBox control by click() method"""
|
||
|
if self.get_check_state() != win32defines.BST_UNCHECKED:
|
||
|
self.click()
|
||
|
# Non PEP-8 alias
|
||
|
UncheckByClick = deprecated(uncheck_by_click)
|
||
|
|
||
|
#-----------------------------------------------------------
|
||
|
def check_by_click_input(self):
|
||
|
"""Check the CheckBox control by click_input() method"""
|
||
|
if self.get_check_state() != win32defines.BST_CHECKED:
|
||
|
self.click_input()
|
||
|
# Non PEP-8 alias
|
||
|
CheckByClickInput = deprecated(check_by_click_input)
|
||
|
|
||
|
#-----------------------------------------------------------
|
||
|
def uncheck_by_click_input(self):
|
||
|
"""Uncheck the CheckBox control by click_input() method"""
|
||
|
if self.get_check_state() != win32defines.BST_UNCHECKED:
|
||
|
self.click_input()
|
||
|
# Non PEP-8 alias
|
||
|
UncheckByClickInput = deprecated(uncheck_by_click_input)
|
||
|
|
||
|
#====================================================================
|
||
|
def _get_multiple_text_items(wrapper, count_msg, item_len_msg, item_get_msg):
|
||
|
"""Helper function to get multiple text items from a control"""
|
||
|
texts = []
|
||
|
|
||
|
# find out how many text items are in the combobox
|
||
|
num_items = wrapper.send_message(count_msg)
|
||
|
|
||
|
# get the text for each item in the combobox
|
||
|
for i in range(0, num_items):
|
||
|
text_len = wrapper.send_message (item_len_msg, i, 0)
|
||
|
|
||
|
if six.PY3:
|
||
|
text = ctypes.create_unicode_buffer(text_len + 1)
|
||
|
else:
|
||
|
text = ctypes.create_string_buffer(text_len + 1)
|
||
|
|
||
|
wrapper.send_message(item_get_msg, i, ctypes.byref(text))
|
||
|
|
||
|
if six.PY3:
|
||
|
texts.append(text.value.replace('\u200e', ''))
|
||
|
else:
|
||
|
texts.append(text.value.decode(locale.getpreferredencoding(), 'ignore').replace('?', ''))
|
||
|
|
||
|
return texts
|
||
|
|
||
|
|
||
|
#====================================================================
|
||
|
class ComboBoxWrapper(hwndwrapper.HwndWrapper):
|
||
|
|
||
|
"""Wrap a windows ComboBox control"""
|
||
|
|
||
|
friendlyclassname = "ComboBox"
|
||
|
windowclasses = [
|
||
|
"ComboBox",
|
||
|
"WindowsForms\d*\.COMBOBOX\..*",
|
||
|
".*ComboBox", ]
|
||
|
has_title = False
|
||
|
|
||
|
#-----------------------------------------------------------
|
||
|
def __init__(self, hwnd):
|
||
|
"""Initialize the control"""
|
||
|
super(ComboBoxWrapper, self).__init__(hwnd)
|
||
|
|
||
|
@property
|
||
|
def writable_props(self):
|
||
|
"""Extend default properties list."""
|
||
|
props = super(ComboBoxWrapper, self).writable_props
|
||
|
props.extend(["selected_index",
|
||
|
"dropped_rect",
|
||
|
])
|
||
|
return props
|
||
|
|
||
|
#-----------------------------------------------------------
|
||
|
def dropped_rect(self):
|
||
|
"""Get the dropped rectangle of the combobox"""
|
||
|
dropped_rect = win32structures.RECT()
|
||
|
|
||
|
self.send_message(
|
||
|
win32defines.CB_GETDROPPEDCONTROLRECT,
|
||
|
0,
|
||
|
ctypes.byref(dropped_rect))
|
||
|
|
||
|
# we need to offset the dropped rect from the control
|
||
|
dropped_rect -= self.rectangle()
|
||
|
|
||
|
return dropped_rect
|
||
|
# Non PEP-8 alias
|
||
|
DroppedRect = deprecated(dropped_rect)
|
||
|
|
||
|
#-----------------------------------------------------------
|
||
|
def item_count(self):
|
||
|
"""Return the number of items in the combobox"""
|
||
|
self._ensure_enough_privileges('CB_GETCOUNT')
|
||
|
return self.send_message(win32defines.CB_GETCOUNT)
|
||
|
# Non PEP-8 alias
|
||
|
ItemCount = deprecated(item_count)
|
||
|
|
||
|
#-----------------------------------------------------------
|
||
|
def selected_index(self):
|
||
|
"""Return the selected index"""
|
||
|
self._ensure_enough_privileges('CB_GETCURSEL')
|
||
|
return self.send_message(win32defines.CB_GETCURSEL)
|
||
|
# Non PEP-8 alias
|
||
|
SelectedIndex = deprecated(selected_index)
|
||
|
|
||
|
#-----------------------------------------------------------
|
||
|
def selected_text(self):
|
||
|
"""Return the selected text"""
|
||
|
return self.item_texts()[self.selected_index()]
|
||
|
# Non PEP-8 alias
|
||
|
SelectedText = deprecated(selected_text)
|
||
|
|
||
|
#-----------------------------------------------------------
|
||
|
def _get_item_index(self, ident):
|
||
|
"""Get the index for the item with this 'ident'"""
|
||
|
if isinstance(ident, six.integer_types):
|
||
|
|
||
|
if ident >= self.item_count():
|
||
|
raise IndexError(('Combobox has {0} items, you requested ' + \
|
||
|
'item {1} (0 based)').format(self.item_count(), ident))
|
||
|
|
||
|
# negative index
|
||
|
if ident < 0:
|
||
|
# convert it to a positive index
|
||
|
ident = (self.item_count() + ident)
|
||
|
|
||
|
elif isinstance(ident, six.string_types):
|
||
|
# todo - implement fuzzy lookup for ComboBox items
|
||
|
# todo - implement appdata lookup for combobox items
|
||
|
ident = self.item_texts().index(ident)
|
||
|
|
||
|
return ident
|
||
|
|
||
|
#-----------------------------------------------------------
|
||
|
def item_data(self, item):
|
||
|
"""Returns the item data associated with the item if any"""
|
||
|
index = self._get_item_index(item)
|
||
|
return self.send_message(win32defines.CB_GETITEMDATA, index)
|
||
|
# Non PEP-8 alias
|
||
|
ItemData = deprecated(item_data)
|
||
|
|
||
|
#-----------------------------------------------------------
|
||
|
def item_texts(self):
|
||
|
"""Return the text of the items of the combobox"""
|
||
|
self._ensure_enough_privileges('CB_GETCOUNT')
|
||
|
return _get_multiple_text_items(
|
||
|
self,
|
||
|
win32defines.CB_GETCOUNT,
|
||
|
win32defines.CB_GETLBTEXTLEN,
|
||
|
win32defines.CB_GETLBTEXT)
|
||
|
# Non PEP-8 alias
|
||
|
ItemTexts = deprecated(item_texts)
|
||
|
|
||
|
#-----------------------------------------------------------
|
||
|
def texts(self):
|
||
|
"""Return the text of the items in the combobox"""
|
||
|
texts = [self.window_text()]
|
||
|
texts.extend(self.item_texts())
|
||
|
return texts
|
||
|
|
||
|
#-----------------------------------------------------------
|
||
|
def get_properties(self):
|
||
|
"""Return the properties of the control as a dictionary"""
|
||
|
props = hwndwrapper.HwndWrapper.get_properties(self)
|
||
|
|
||
|
#props['item_data'] = []
|
||
|
#for i in range(self.item_count()):
|
||
|
# props['item_data'].append(self.item_data(i))
|
||
|
|
||
|
return props
|
||
|
|
||
|
#-----------------------------------------------------------
|
||
|
def select(self, item):
|
||
|
"""Select the ComboBox item
|
||
|
|
||
|
item can be either a 0 based index of the item to select
|
||
|
or it can be the string that you want to select
|
||
|
"""
|
||
|
self.verify_actionable()
|
||
|
|
||
|
index = self._get_item_index(item)
|
||
|
|
||
|
# change the selected item
|
||
|
self.send_message_timeout(win32defines.CB_SETCURSEL, index, timeout=0.05)
|
||
|
|
||
|
# Notify the parent that we are finished selecting
|
||
|
self.notify_parent(win32defines.CBN_SELENDOK)
|
||
|
|
||
|
# Notify the parent that we have changed
|
||
|
self.notify_parent(win32defines.CBN_SELCHANGE)
|
||
|
|
||
|
# simple combo boxes don't have drop downs so they do not recieve
|
||
|
# this notification
|
||
|
if self.has_style(win32defines.CBS_DROPDOWN):
|
||
|
# Notify the parent that the drop down has closed
|
||
|
self.notify_parent(win32defines.CBN_CLOSEUP)
|
||
|
|
||
|
|
||
|
win32functions.WaitGuiThreadIdle(self)
|
||
|
time.sleep(Timings.after_comboboxselect_wait)
|
||
|
|
||
|
# return this control so that actions can be chained.
|
||
|
return self
|
||
|
# Non PEP-8 alias
|
||
|
Select = deprecated(select)
|
||
|
|
||
|
#-----------------------------------------------------------
|
||
|
#def deselect(self, item):
|
||
|
# Not implemented because it doesn't make sense for combo boxes.
|
||
|
|
||
|
#TODO def edit_control(self): # return the edit control of the Combobox
|
||
|
|
||
|
#TODO def list_control(self): # return the list control of the combobox
|
||
|
|
||
|
#TODO def item_text(self, index): # get the test of item XX?
|
||
|
|
||
|
#TODO def edit_text(self): # or should this be self.EditControl.text()?
|
||
|
|
||
|
|
||
|
#====================================================================
|
||
|
class ListBoxWrapper(hwndwrapper.HwndWrapper):
|
||
|
|
||
|
"""Wrap a windows ListBox control"""
|
||
|
|
||
|
friendlyclassname = "ListBox"
|
||
|
windowclasses = [
|
||
|
"ListBox",
|
||
|
r"WindowsForms\d*\.LISTBOX\..*",
|
||
|
".*ListBox", ]
|
||
|
has_title = False
|
||
|
|
||
|
#-----------------------------------------------------------
|
||
|
def __init__(self, hwnd):
|
||
|
"""Initialize the control"""
|
||
|
super(ListBoxWrapper, self).__init__(hwnd)
|
||
|
|
||
|
@property
|
||
|
def writable_props(self):
|
||
|
"""Extend default properties list."""
|
||
|
props = super(ListBoxWrapper, self).writable_props
|
||
|
props.extend(["selected_indices"])
|
||
|
return props
|
||
|
|
||
|
#-----------------------------------------------------------
|
||
|
def is_single_selection(self):
|
||
|
"""Check whether the listbox has single selection mode."""
|
||
|
self._ensure_enough_privileges('LB_GETSELCOUNT')
|
||
|
num_selected = self.send_message(win32defines.LB_GETSELCOUNT)
|
||
|
|
||
|
# if we got LB_ERR then it is a single selection list box
|
||
|
return (num_selected == win32defines.LB_ERR)
|
||
|
# Non PEP-8 alias
|
||
|
IsSingleSelection = deprecated(is_single_selection)
|
||
|
|
||
|
#-----------------------------------------------------------
|
||
|
def selected_indices(self):
|
||
|
"""The currently selected indices of the listbox"""
|
||
|
self._ensure_enough_privileges('LB_GETSELCOUNT')
|
||
|
num_selected = self.send_message(win32defines.LB_GETSELCOUNT)
|
||
|
|
||
|
# if we got LB_ERR then it is a single selection list box
|
||
|
if num_selected == win32defines.LB_ERR:
|
||
|
items = tuple([self.send_message(win32defines.LB_GETCURSEL)])
|
||
|
|
||
|
# otherwise it is a multiselection list box
|
||
|
else:
|
||
|
items = (ctypes.c_int * num_selected)()
|
||
|
|
||
|
self.send_message(
|
||
|
win32defines.LB_GETSELITEMS, num_selected, ctypes.byref(items))
|
||
|
|
||
|
# Need to convert from Ctypes array to a python tuple
|
||
|
items = tuple(items)
|
||
|
|
||
|
return items
|
||
|
# Non PEP-8 alias
|
||
|
SelectedIndices = deprecated(selected_indices)
|
||
|
|
||
|
#-----------------------------------------------------------
|
||
|
def _get_item_index(self, ident):
|
||
|
"""Return the index of the item 'ident'"""
|
||
|
if isinstance(ident, six.integer_types):
|
||
|
|
||
|
if ident >= self.item_count():
|
||
|
raise IndexError(('ListBox has {0} items, you requested ' + \
|
||
|
'item {1} (0 based)').format(self.item_count(), ident))
|
||
|
|
||
|
# negative index
|
||
|
if ident < 0:
|
||
|
ident = (self.item_count() + ident)
|
||
|
|
||
|
elif isinstance(ident, six.string_types):
|
||
|
# todo - implement fuzzy lookup for ComboBox items
|
||
|
# todo - implement appdata lookup for combobox items
|
||
|
ident = self.item_texts().index(ident) #-1
|
||
|
|
||
|
return ident
|
||
|
|
||
|
#-----------------------------------------------------------
|
||
|
def item_count(self):
|
||
|
"""Return the number of items in the ListBox"""
|
||
|
self._ensure_enough_privileges('LB_GETCOUNT')
|
||
|
return self.send_message(win32defines.LB_GETCOUNT)
|
||
|
# Non PEP-8 alias
|
||
|
ItemCount = deprecated(item_count)
|
||
|
|
||
|
#-----------------------------------------------------------
|
||
|
def item_data(self, i):
|
||
|
"""Return the item_data if any associted with the item"""
|
||
|
index = self._get_item_index(i)
|
||
|
return self.send_message(win32defines.LB_GETITEMDATA, index)
|
||
|
# Non PEP-8 alias
|
||
|
ItemData = deprecated(item_data)
|
||
|
|
||
|
#-----------------------------------------------------------
|
||
|
def item_texts(self):
|
||
|
"""Return the text of the items of the listbox"""
|
||
|
self._ensure_enough_privileges('LB_GETCOUNT')
|
||
|
return _get_multiple_text_items(
|
||
|
self,
|
||
|
win32defines.LB_GETCOUNT,
|
||
|
win32defines.LB_GETTEXTLEN,
|
||
|
win32defines.LB_GETTEXT)
|
||
|
# Non PEP-8 alias
|
||
|
ItemTexts = deprecated(item_texts)
|
||
|
|
||
|
#-----------------------------------------------------------
|
||
|
def item_rect(self, item):
|
||
|
"""Return the rect of the item"""
|
||
|
index = self._get_item_index(item)
|
||
|
rect = win32structures.RECT()
|
||
|
res = self.send_message(win32defines.LB_GETITEMRECT, index, ctypes.byref(rect))
|
||
|
if res == win32defines.LB_ERR:
|
||
|
raise RuntimeError("LB_GETITEMRECT failed")
|
||
|
return rect
|
||
|
# Non PEP-8 alias
|
||
|
ItemRect = deprecated(item_rect)
|
||
|
|
||
|
#-----------------------------------------------------------
|
||
|
def texts(self):
|
||
|
"""Return the texts of the control"""
|
||
|
texts = [self.window_text()]
|
||
|
texts.extend(self.item_texts())
|
||
|
return texts
|
||
|
|
||
|
# #-----------------------------------------------------------
|
||
|
# def get_properties(self):
|
||
|
# "Return the properties as a dictionary for the control"
|
||
|
# props = hwndwrapper.HwndWrapper.get_properties(self)
|
||
|
#
|
||
|
# props['item_data'] = []
|
||
|
# for i in range(self.item_count()):
|
||
|
# props['item_data'].append(self.item_data(i))
|
||
|
#
|
||
|
# return props
|
||
|
|
||
|
#-----------------------------------------------------------
|
||
|
def select(self, item, select=True):
|
||
|
"""Select the ListBox item
|
||
|
|
||
|
item can be either a 0 based index of the item to select
|
||
|
or it can be the string that you want to select
|
||
|
"""
|
||
|
if self.is_single_selection() and isinstance(item, (list, tuple)) and len(item) > 1:
|
||
|
raise Exception('Cannot set multiple selection for single-selection listbox!')
|
||
|
|
||
|
if isinstance(item, (list, tuple)):
|
||
|
for i in item:
|
||
|
if i is not None:
|
||
|
self.select(i, select)
|
||
|
return self
|
||
|
|
||
|
self.verify_actionable()
|
||
|
|
||
|
# Make sure we have an index so if passed in a
|
||
|
# string then find which item it is
|
||
|
index = self._get_item_index(item)
|
||
|
|
||
|
if self.is_single_selection():
|
||
|
# change the selected item
|
||
|
self.send_message_timeout(win32defines.LB_SETCURSEL, index)
|
||
|
else:
|
||
|
if select:
|
||
|
# add the item to selection
|
||
|
self.send_message_timeout(win32defines.LB_SETSEL, win32defines.TRUE, index)
|
||
|
else:
|
||
|
# remove the item from selection
|
||
|
self.send_message_timeout(win32defines.LB_SETSEL, win32defines.FALSE, index)
|
||
|
|
||
|
# Notify the parent that we have changed
|
||
|
self.notify_parent(win32defines.LBN_SELCHANGE)
|
||
|
|
||
|
win32functions.WaitGuiThreadIdle(self)
|
||
|
time.sleep(Timings.after_listboxselect_wait)
|
||
|
|
||
|
return self
|
||
|
# Non PEP-8 alias
|
||
|
Select = deprecated(select)
|
||
|
|
||
|
#-----------------------------------------------------------
|
||
|
def set_item_focus(self, item):
|
||
|
"""Set the ListBox focus to the item at index"""
|
||
|
index = self._get_item_index(item)
|
||
|
|
||
|
# if it is a multiple selection dialog
|
||
|
if self.has_style(win32defines.LBS_EXTENDEDSEL) or \
|
||
|
self.has_style(win32defines.LBS_MULTIPLESEL):
|
||
|
self.send_message_timeout(win32defines.LB_SETCARETINDEX, index)
|
||
|
else:
|
||
|
self.send_message_timeout(win32defines.LB_SETCURSEL, index)
|
||
|
|
||
|
win32functions.WaitGuiThreadIdle(self)
|
||
|
time.sleep(Timings.after_listboxfocuschange_wait)
|
||
|
|
||
|
# return this control so that actions can be chained.
|
||
|
return self
|
||
|
# Non PEP-8 alias
|
||
|
SetItemFocus = deprecated(set_item_focus)
|
||
|
|
||
|
#-----------------------------------------------------------
|
||
|
def get_item_focus(self):
|
||
|
"""Retrun the index of current selection in a ListBox"""
|
||
|
# if it is a multiple selection dialog
|
||
|
if self.has_style(win32defines.LBS_EXTENDEDSEL) or \
|
||
|
self.has_style(win32defines.LBS_MULTIPLESEL):
|
||
|
return self.send_message(win32defines.LB_GETCARETINDEX)
|
||
|
else:
|
||
|
return self.send_message(win32defines.LB_GETCURSEL)
|
||
|
# Non PEP-8 alias
|
||
|
GetItemFocus = deprecated(get_item_focus)
|
||
|
|
||
|
|
||
|
#====================================================================
|
||
|
class EditWrapper(hwndwrapper.HwndWrapper):
|
||
|
|
||
|
"""Wrap a windows Edit control"""
|
||
|
|
||
|
friendlyclassname = "Edit"
|
||
|
windowclasses = [
|
||
|
"Edit",
|
||
|
".*Edit",
|
||
|
"TMemo",
|
||
|
r"WindowsForms\d*\.EDIT\..*",
|
||
|
"ThunderTextBox",
|
||
|
"ThunderRT6TextBox",
|
||
|
]
|
||
|
has_title = False
|
||
|
|
||
|
#-----------------------------------------------------------
|
||
|
def __init__(self, hwnd):
|
||
|
"""Initialize the control"""
|
||
|
super(EditWrapper, self).__init__(hwnd)
|
||
|
|
||
|
@property
|
||
|
def writable_props(self):
|
||
|
"""Extend default properties list."""
|
||
|
props = super(EditWrapper, self).writable_props
|
||
|
props.extend(['selection_indices'])
|
||
|
return props
|
||
|
|
||
|
#-----------------------------------------------------------
|
||
|
def line_count(self):
|
||
|
"""Return how many lines there are in the Edit"""
|
||
|
self._ensure_enough_privileges('EM_GETLINECOUNT')
|
||
|
return self.send_message(win32defines.EM_GETLINECOUNT)
|
||
|
# Non PEP-8 alias
|
||
|
LineCount = deprecated(line_count)
|
||
|
|
||
|
#-----------------------------------------------------------
|
||
|
def line_length(self, line_index):
|
||
|
"""Return how many characters there are in the line"""
|
||
|
self._ensure_enough_privileges('EM_LINEINDEX')
|
||
|
# need to first get a character index of that line
|
||
|
char_index = self.send_message(win32defines.EM_LINEINDEX, line_index)
|
||
|
|
||
|
# now get the length of text on that line
|
||
|
return self.send_message (
|
||
|
win32defines.EM_LINELENGTH, char_index, 0)
|
||
|
# Non PEP-8 alias
|
||
|
LineLength = deprecated(line_length)
|
||
|
|
||
|
#-----------------------------------------------------------
|
||
|
def get_line(self, line_index):
|
||
|
"""Return the line specified"""
|
||
|
text_len = self.line_length(line_index)
|
||
|
# create a buffer and set the length at the start of the buffer
|
||
|
text = ctypes.create_unicode_buffer(text_len+3)
|
||
|
text[0] = six.unichr(text_len)
|
||
|
|
||
|
# retrieve the line itself
|
||
|
win32functions.SendMessage(self, win32defines.EM_GETLINE, line_index, ctypes.byref(text))
|
||
|
|
||
|
return text.value
|
||
|
# Non PEP-8 alias
|
||
|
GetLine = deprecated(get_line)
|
||
|
|
||
|
#-----------------------------------------------------------
|
||
|
def texts(self):
|
||
|
"""Get the text of the edit control"""
|
||
|
texts = [self.window_text(), ]
|
||
|
|
||
|
for i in range(self.line_count()):
|
||
|
texts.append(self.get_line(i))
|
||
|
|
||
|
return texts
|
||
|
|
||
|
#-----------------------------------------------------------
|
||
|
def text_block(self):
|
||
|
"""Get the text of the edit control"""
|
||
|
length = self.send_message(win32defines.WM_GETTEXTLENGTH)
|
||
|
|
||
|
text = ctypes.create_unicode_buffer(length + 1)
|
||
|
|
||
|
win32functions.SendMessage(self, win32defines.WM_GETTEXT, length + 1, ctypes.byref(text))
|
||
|
|
||
|
return text.value
|
||
|
# Non PEP-8 alias
|
||
|
TextBlock = deprecated(text_block)
|
||
|
|
||
|
#-----------------------------------------------------------
|
||
|
def selection_indices(self):
|
||
|
"""The start and end indices of the current selection"""
|
||
|
self._ensure_enough_privileges('EM_GETSEL')
|
||
|
start = ctypes.c_int()
|
||
|
end = ctypes.c_int()
|
||
|
self.send_message(
|
||
|
win32defines.EM_GETSEL, ctypes.byref(start), ctypes.byref(end))
|
||
|
|
||
|
return (start.value, end.value)
|
||
|
# Non PEP-8 alias
|
||
|
SelectionIndices = deprecated(selection_indices)
|
||
|
|
||
|
#-----------------------------------------------------------
|
||
|
def set_window_text(self, text, append = False):
|
||
|
"""Override set_window_text for edit controls because it should not be
|
||
|
used for Edit controls.
|
||
|
|
||
|
Edit Controls should either use set_edit_text() or type_keys() to modify
|
||
|
the contents of the edit control."""
|
||
|
hwndwrapper.HwndWrapper.set_window_text(self, text, append)
|
||
|
raise UserWarning(
|
||
|
"set_window_text() should probably not be called for Edit Controls")
|
||
|
|
||
|
#-----------------------------------------------------------
|
||
|
def set_edit_text(self, text, pos_start = None, pos_end = None):
|
||
|
"""Set the text of the edit control"""
|
||
|
self._ensure_enough_privileges('EM_REPLACESEL')
|
||
|
self.verify_actionable()
|
||
|
|
||
|
# allow one or both of pos_start and pos_end to be None
|
||
|
if pos_start is not None or pos_end is not None:
|
||
|
|
||
|
# if only one has been specified - then set the other
|
||
|
# to the current selection start or end
|
||
|
start, end = self.selection_indices()
|
||
|
if pos_start is None:
|
||
|
pos_start = start
|
||
|
if pos_end is None and not isinstance(start, six.string_types):
|
||
|
pos_end = end
|
||
|
|
||
|
# set the selection if either start or end has
|
||
|
# been specified
|
||
|
self.select(pos_start, pos_end)
|
||
|
else:
|
||
|
self.select()
|
||
|
|
||
|
if isinstance(text, six.text_type):
|
||
|
if six.PY3:
|
||
|
aligned_text = text
|
||
|
else:
|
||
|
aligned_text = text.encode(locale.getpreferredencoding())
|
||
|
elif isinstance(text, six.binary_type):
|
||
|
if six.PY3:
|
||
|
aligned_text = text.decode(locale.getpreferredencoding())
|
||
|
else:
|
||
|
aligned_text = text
|
||
|
else:
|
||
|
# convert a non-string input
|
||
|
if six.PY3:
|
||
|
aligned_text = six.text_type(text)
|
||
|
else:
|
||
|
aligned_text = six.binary_type(text)
|
||
|
|
||
|
if isinstance(aligned_text, six.text_type):
|
||
|
buffer = ctypes.create_unicode_buffer(aligned_text, size=len(aligned_text) + 1)
|
||
|
else:
|
||
|
buffer = ctypes.create_string_buffer(aligned_text, size=len(aligned_text) + 1)
|
||
|
|
||
|
# replace the selection with
|
||
|
self.send_message(win32defines.EM_REPLACESEL, True, ctypes.byref(buffer))
|
||
|
|
||
|
#win32functions.WaitGuiThreadIdle(self)
|
||
|
#time.sleep(Timings.after_editsetedittext_wait)
|
||
|
|
||
|
if isinstance(aligned_text, six.text_type):
|
||
|
self.actions.log('Set text to the edit box: ' + aligned_text)
|
||
|
else:
|
||
|
self.actions.log(b'Set text to the edit box: ' + aligned_text)
|
||
|
|
||
|
# return this control so that actions can be chained.
|
||
|
return self
|
||
|
|
||
|
# set SetText as an alias to set_edit_text
|
||
|
set_text = set_edit_text
|
||
|
# Non PEP-8 alias
|
||
|
SetText = deprecated(set_text)
|
||
|
# Non PEP-8 alias
|
||
|
SetEditText = deprecated(set_edit_text)
|
||
|
|
||
|
#-----------------------------------------------------------
|
||
|
def select(self, start = 0, end = None):
|
||
|
"""Set the edit selection of the edit control"""
|
||
|
self._ensure_enough_privileges('EM_SETSEL')
|
||
|
self.verify_actionable()
|
||
|
win32functions.SetFocus(self)
|
||
|
|
||
|
# if we have been asked to select a string
|
||
|
if isinstance(start, six.text_type):
|
||
|
string_to_select = start
|
||
|
start = self.text_block().index(string_to_select)
|
||
|
|
||
|
if end is None:
|
||
|
end = start + len(string_to_select)
|
||
|
elif isinstance(start, six.binary_type):
|
||
|
string_to_select = start.decode(locale.getpreferredencoding())
|
||
|
start = self.text_block().index(string_to_select)
|
||
|
|
||
|
if end is None:
|
||
|
end = start + len(string_to_select)
|
||
|
|
||
|
if end is None:
|
||
|
end = -1
|
||
|
|
||
|
self.send_message(win32defines.EM_SETSEL, start, end)
|
||
|
|
||
|
# give the control a chance to catch up before continuing
|
||
|
win32functions.WaitGuiThreadIdle(self)
|
||
|
|
||
|
time.sleep(Timings.after_editselect_wait)
|
||
|
|
||
|
# return this control so that actions can be chained.
|
||
|
return self
|
||
|
# Non PEP-8 alias
|
||
|
Select = deprecated(select)
|
||
|
|
||
|
|
||
|
#====================================================================
|
||
|
class StaticWrapper(hwndwrapper.HwndWrapper):
|
||
|
|
||
|
"""Wrap a windows Static control"""
|
||
|
|
||
|
friendlyclassname = "Static"
|
||
|
windowclasses = [
|
||
|
"Static",
|
||
|
r"WindowsForms\d*\.STATIC\..*",
|
||
|
"TPanel",
|
||
|
".*StaticText"]
|
||
|
can_be_label = True
|
||
|
|
||
|
def __init__(self, hwnd):
|
||
|
"""Initialize the control"""
|
||
|
super(StaticWrapper, self).__init__(hwnd)
|
||
|
|
||
|
@property
|
||
|
def _needs_image_prop(self):
|
||
|
"""_needs_image_prop=True if it is an image static"""
|
||
|
# if the control is visible - and it shows an image
|
||
|
if self.is_visible() and (self.has_style(win32defines.SS_ICON) or
|
||
|
self.has_style(win32defines.SS_BITMAP) or
|
||
|
self.has_style(win32defines.SS_CENTERIMAGE) or
|
||
|
self.has_style(win32defines.SS_OWNERDRAW)):
|
||
|
|
||
|
return True
|
||
|
else:
|
||
|
return False
|
||
|
# Non PEP-8 alias
|
||
|
_NeedsImageProp = deprecated(_needs_image_prop, deprecated_name='_NeedsImageProp')
|
||
|
|
||
|
|
||
|
#====================================================================
|
||
|
class PopupMenuWrapper(hwndwrapper.HwndWrapper):
|
||
|
|
||
|
"""Wrap a Popup Menu"""
|
||
|
|
||
|
friendlyclassname = "PopupMenu"
|
||
|
windowclasses = ["#32768", ]
|
||
|
has_title = False
|
||
|
|
||
|
#-----------------------------------------------------------
|
||
|
def is_dialog(self):
|
||
|
"""Return whether it is a dialog"""
|
||
|
return True
|
||
|
|
||
|
#-----------------------------------------------------------
|
||
|
def _menu_handle(self):
|
||
|
"""Get the menu handle for the popup menu"""
|
||
|
hMenu = win32gui.SendMessage(self.handle, win32defines.MN_GETHMENU)
|
||
|
|
||
|
if not hMenu:
|
||
|
raise ctypes.WinError()
|
||
|
|
||
|
return (hMenu, False) # (hMenu, is_main_menu)
|
||
|
|