pyWinAuto: c:\.projects\py_pywinauto\pywinauto\controls\win32_controls.py

0001# GUI Application automation and testing library
0002# Copyright (C) 2006 Mark Mc Mahon
0003#
0004# This library is free software; you can redistribute it and/or
0005# modify it under the terms of the GNU Lesser General Public License
0006# as published by the Free Software Foundation; either version 2.1
0007# of the License, or (at your option) any later version.
0008#
0009# This library is distributed in the hope that it will be useful,
0010# but WITHOUT ANY WARRANTY; without even the implied warranty of
0011# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
0012# See the GNU Lesser General Public License for more details.
0013#
0014# You should have received a copy of the GNU Lesser General Public
0015# License along with this library; if not, write to the
0016#    Free Software Foundation, Inc.,
0017#    59 Temple Place,
0018#    Suite 330,
0019#    Boston, MA 02111-1307 USA
0020
0021"Wraps various standard windows controls"
0022
0023__revision__ = "$Revision: 492 $"
0024
0025import time
0026
0027import ctypes
0028
0029import HwndWrapper
0030
0031from pywinauto import win32functions
0032from pywinauto import win32defines
0033from pywinauto import win32structures
0034#from pywinauto import findbestmatch
0035
0036from pywinauto import tests
0037from pywinauto.timings import Timings
0038
0039#====================================================================
0040class ButtonWrapper(HwndWrapper.HwndWrapper):
0041    "Wrap a windows Button control"
0042
0043    friendlyclassname = "Button"
0044    windowclasses = [
0045        "Button",
0046        r"WindowsForms\d*\.BUTTON\..*",
0047        "TButton" ]
0048
0049
0050    #-----------------------------------------------------------
0051    def __init__(self, hwnd):
0052        "Initialize the control"
0053        super(ButtonWrapper, self).__init__(hwnd)
0054
0055        #self._set_if_needs_image()
0056
0057
0058    def _set_if_needs_image(self, value):
0059        "Does nothing see _get_if_needs_image"
0060        pass
0061    #-----------------------------------------------------------
0062    def _get_if_needs_image(self):
0063        "Set the _NeedsImageProp attribute if it is an image button"
0064
0065        # optimization call Style once and work with that rather than
0066        # calling HasStyle a number of times
0067        style = self.Style()
0068
0069        if self.IsVisible() and (              style & win32defines.BS_BITMAP == style or               style & win32defines.BS_ICON == style or               style & win32defines.BS_OWNERDRAW == style):
0073
0074            #self._NeedsImageProp = True
0075            return True
0076        else:
0077            return False
0078    _NeedsImageProp = property(_get_if_needs_image, _set_if_needs_image)
0079
0080    #-----------------------------------------------------------
0081    def FriendlyClassName(self):
0082        """Return the friendly class name of the button
0083
0084        Windows controls with the class "Button" can look like different
0085        controls based on their style. They can look like the following
0086        controls:
0087
0088          - Buttons, this method returns "Button"
0089          - CheckBoxes, this method returns "CheckBox"
0090          - RadioButtons, this method returns "RadioButton"
0091          - GroupBoxes, this method returns "GroupBox"
0092
0093        """
0094        # get the least significant bit
0095        style_lsb = self.Style() & 0xF
0096
0097        f_class_name = 'Button'
0098
0099        if style_lsb == win32defines.BS_3STATE or               style_lsb == win32defines.BS_AUTO3STATE or               style_lsb == win32defines.BS_AUTOCHECKBOX or               style_lsb == win32defines.BS_CHECKBOX:
0103            f_class_name = "CheckBox"
0104
0105        elif style_lsb == win32defines.BS_RADIOBUTTON or               style_lsb == win32defines.BS_AUTORADIOBUTTON:
0107            f_class_name = "RadioButton"
0108
0109        elif style_lsb ==  win32defines.BS_GROUPBOX:
0110            f_class_name = "GroupBox"
0111
0112        if self.Style() & win32defines.BS_PUSHLIKE:
0113            f_class_name = "Button"
0114
0115        return f_class_name
0116
0117
0118    #-----------------------------------------------------------
0119    def GetCheckState(self):
0120        """Return the check state of the checkbox
0121
0122        The check state is represented by an integer
0123        0 - unchecked
0124        1 - checked
0125        2 - indeterminate
0126
0127        The following constants are defined in the win32defines module
0128        BST_UNCHECKED = 0
0129        BST_CHECKED = 1
0130        BST_INDETERMINATE = 2
0131        """
0132        return self.SendMessage(win32defines.BM_GETCHECK)
0133
0134    #-----------------------------------------------------------
0135    def Check(self):
0136        "Check a checkbox"
0137        self.SendMessageTimeout(win32defines.BM_SETCHECK,
0138            win32defines.BST_CHECKED)
0139
0140        win32functions.WaitGuiThreadIdle(self)
0141        time.sleep(Timings.after_buttoncheck_wait)
0142
0143        # return this control so that actions can be chained.
0144        return self
0145
0146
0147    #-----------------------------------------------------------
0148    def UnCheck(self):
0149        "Uncheck a checkbox"
0150        self.SendMessageTimeout(win32defines.BM_SETCHECK,
0151            win32defines.BST_UNCHECKED)
0152
0153        win32functions.WaitGuiThreadIdle(self)
0154        time.sleep(Timings.after_buttoncheck_wait)
0155
0156        # return this control so that actions can be chained.
0157        return self
0158
0159    #-----------------------------------------------------------
0160    def SetCheckIndeterminate(self):
0161        "Set the checkbox to indeterminate"
0162        self.SendMessageTimeout(win32defines.BM_SETCHECK,
0163            win32defines.BST_INDETERMINATE)
0164
0165        win32functions.WaitGuiThreadIdle(self)
0166        time.sleep(Timings.after_buttoncheck_wait)
0167
0168        # return this control so that actions can be chained.
0169        return self
0170
0171    #-----------------------------------------------------------
0172    def IsDialog(self):
0173        "Buttons are never dialogs so return False"
0174        return False
0175
0176    #-----------------------------------------------------------
0177    def Click(self, *args, **kwargs):
0178        "Click the Button control"
0179    #    import win32functions
0180    #    win32functions.WaitGuiThreadIdle(self)
0181    #    self.NotifyParent(win32defines.BN_CLICKED)
0182        HwndWrapper.HwndWrapper.Click(self, *args, **kwargs)
0183    #    win32functions.WaitGuiThreadIdle(self)
0184        time.sleep(Timings.after_button_click_wait)
0185
0186
0187    #def IsSelected (self):
0188    #    (for radio buttons)
0189
0190
0191
0192#====================================================================
0193def _get_multiple_text_items(wrapper, count_msg, item_len_msg, item_get_msg):
0194    "Helper function to get multiple text items from a control"
0195
0196    texts = []
0197
0198    # find out how many text items are in the combobox
0199    num_items = wrapper.SendMessage(count_msg)
0200
0201    # get the text for each item in the combobox
0202    for i in range(0, num_items):
0203        text_len = wrapper.SendMessage (item_len_msg, i, 0)
0204
0205        text = ctypes.create_unicode_buffer(text_len + 1)
0206
0207        wrapper.SendMessage(item_get_msg, i, ctypes.byref(text))
0208
0209        texts.append(text.value)
0210
0211    return texts
0212
0213
0214#====================================================================
0215class ComboBoxWrapper(HwndWrapper.HwndWrapper):
0216    "Wrap a windows ComboBox control"
0217
0218    friendlyclassname = "ComboBox"
0219    windowclasses = [
0220        "ComboBox",
0221        "WindowsForms\d*\.COMBOBOX\..*",
0222        "TComboBox"]
0223
0224    #-----------------------------------------------------------
0225    def __init__(self, hwnd):
0226        "Initialize the control"
0227        super(ComboBoxWrapper, self).__init__(hwnd)
0228
0229        self.writable_props.extend([
0230            "SelectedIndex",
0231            "DroppedRect",
0232            ])
0233
0234    #-----------------------------------------------------------
0235    def DroppedRect(self):
0236        "Get the dropped rectangle of the combobox"
0237        dropped_rect = win32structures.RECT()
0238
0239        self.SendMessage(
0240            win32defines.CB_GETDROPPEDCONTROLRECT,
0241            0,
0242            ctypes.byref(dropped_rect))
0243
0244        # we need to offset the dropped rect from the control
0245        dropped_rect -= self.Rectangle()
0246
0247        return dropped_rect
0248
0249    #-----------------------------------------------------------
0250    def ItemCount(self):
0251        "Return the number of items in the combobox"
0252        return self.SendMessage(win32defines.CB_GETCOUNT)
0253
0254    #-----------------------------------------------------------
0255    def SelectedIndex(self):
0256        "Return the selected index"
0257        return self.SendMessage(win32defines.CB_GETCURSEL)
0258
0259    #-----------------------------------------------------------
0260    def _get_item_index(self, ident):
0261        "Get the index for the item with this 'ident'"
0262        if isinstance(ident, (int, long)):
0263
0264            if ident >= self.ItemCount():
0265                raise IndexError(
0266                    "Combobox has %d items, you requested item %d"%
0267                        (self.ItemCount(),
0268                        ident))
0269
0270            # negative index
0271            if ident < 0:
0272                # convert it to a positive index
0273                ident = (self.ItemCount() + ident)
0274
0275        elif isinstance(ident, basestring):
0276            # todo - implement fuzzy lookup for ComboBox items
0277            # todo - implement appdata lookup for combobox items
0278            ident = self.ItemTexts().index(ident)
0279
0280        return ident
0281
0282
0283    #-----------------------------------------------------------
0284    def ItemData(self, item):
0285        "Return the item data associted with this item"
0286        index = self._get_item_index(item)
0287        "Returns the item data associated with the item if any"
0288        return self.SendMessage(win32defines.CB_GETITEMDATA, index)
0289
0290    #-----------------------------------------------------------
0291    def ItemTexts(self):
0292        "Return the text of the items of the combobox"
0293        return _get_multiple_text_items(
0294            self,
0295            win32defines.CB_GETCOUNT,
0296            win32defines.CB_GETLBTEXTLEN,
0297            win32defines.CB_GETLBTEXT)
0298
0299    #-----------------------------------------------------------
0300    def Texts(self):
0301        "Return the text of the items in the combobox"
0302        texts = [self.WindowText()]
0303        texts.extend(self.ItemTexts())
0304        return texts
0305
0306    #-----------------------------------------------------------
0307    def GetProperties(self):
0308        "Return the properties of the control as a dictionary"
0309        props = HwndWrapper.HwndWrapper.GetProperties(self)
0310
0311        #props['ItemData'] = []
0312        #for i in range(self.ItemCount()):
0313        #    props['ItemData'].append(self.ItemData(i))
0314
0315        return props
0316
0317    #-----------------------------------------------------------
0318    def Select(self, item):
0319        """Select the ComboBox item
0320
0321        item can be either a 0 based index of the item to select
0322        or it can be the string that you want to select
0323        """
0324        self.VerifyActionable()
0325
0326        index = self._get_item_index(item)
0327
0328        # change the selected item
0329        self.SendMessageTimeout(win32defines.CB_SETCURSEL, index)
0330
0331        # Notify the parent that we are finished selecting
0332        self.NotifyParent(win32defines.CBN_SELENDOK)
0333
0334        # Notify the parent that we have changed
0335        self.NotifyParent(win32defines.CBN_SELCHANGE)
0336
0337        # simple combo boxes don't have drop downs so they do not recieve
0338        # this notification
0339        if self.HasStyle(win32defines.CBS_DROPDOWN):
0340            # Notify the parent that the drop down has closed
0341            self.NotifyParent(win32defines.CBN_CLOSEUP)
0342
0343
0344        win32functions.WaitGuiThreadIdle(self)
0345        time.sleep(Timings.after_comboboxselect_wait)
0346
0347        # return this control so that actions can be chained.
0348        return self
0349
0350    #-----------------------------------------------------------
0351    #def Deselect(self, item):
0352    # Not implemented because it doesn't make sense for combo boxes.
0353
0354    #TODO def EditControl(self):
0355
0356    #TODO def ListControl(self):
0357
0358    #TODO def ItemText(self, index):
0359
0360    #TODO def EditText(self):  # or should this be self.EditControl.Text()?
0361
0362
0363#====================================================================
0364class ListBoxWrapper(HwndWrapper.HwndWrapper):
0365    "Wrap a windows ListBox control"
0366
0367    friendlyclassname = "ListBox"
0368    windowclasses = [
0369        "ListBox",
0370        r"WindowsForms\d*\.LISTBOX\..*",
0371        "TListBox",]
0372
0373    #-----------------------------------------------------------
0374    def __init__(self, hwnd):
0375        "Initialize the control"
0376        super(ListBoxWrapper, self).__init__(hwnd)
0377
0378        self.writable_props.extend([
0379            "SelectedIndices"])
0380
0381    #-----------------------------------------------------------
0382    def SelectedIndices(self):
0383        "The currently selected indices of the listbox"
0384        num_selected = self.SendMessage(win32defines.LB_GETSELCOUNT)
0385
0386        # if we got LB_ERR then it is a single selection list box
0387        if num_selected == win32defines.LB_ERR:
0388            items = [self.SendMessage(win32defines.LB_GETCURSEL)]
0389
0390        # otherwise it is a multiselection list box
0391        else:
0392            items = (ctypes.c_int * num_selected)()
0393
0394            self.SendMessage(
0395                win32defines.LB_GETSELITEMS, num_selected, ctypes.byref(items))
0396
0397        return items
0398
0399    #-----------------------------------------------------------
0400    def _get_item_index(self, ident):
0401        "Return the index of the item 'ident'"
0402        if isinstance(ident, (int, long)):
0403
0404            if ident >= self.ItemCount():
0405                raise IndexError(
0406                    "ListBox has %d items, you requested item %d"%
0407                        (self.ItemCount(),
0408                        ident))
0409
0410            # negative index
0411            if ident < 0:
0412                ident = (self.ItemCount() + ident)
0413
0414        elif isinstance(ident, basestring):
0415            # todo - implement fuzzy lookup for ComboBox items
0416            # todo - implement appdata lookup for combobox items
0417            ident = self.ItemTexts().index(ident) #-1
0418
0419        return ident
0420
0421    #-----------------------------------------------------------
0422    def ItemCount(self):
0423        "Return the number of items in the ListBox"
0424        return self.SendMessage(win32defines.LB_GETCOUNT)
0425
0426    #-----------------------------------------------------------
0427    def ItemData(self, i):
0428        "Return the ItemData if any associted with the item"
0429
0430        index = self._get_item_index(i)
0431
0432        return self.SendMessage(win32defines.LB_GETITEMDATA, index)
0433
0434    #-----------------------------------------------------------
0435    def ItemTexts(self):
0436        "Return the text of the items of the listbox"
0437        return _get_multiple_text_items(
0438            self,
0439            win32defines.LB_GETCOUNT,
0440            win32defines.LB_GETTEXTLEN,
0441            win32defines.LB_GETTEXT)
0442
0443    #-----------------------------------------------------------
0444    def Texts(self):
0445        "Return the texts of the control"
0446        texts = [self.WindowText()]
0447        texts.extend(self.ItemTexts())
0448        return texts
0449
0450#    #-----------------------------------------------------------
0451#    def GetProperties(self):
0452#        "Return the properties as a dictionary for the control"
0453#        props = HwndWrapper.HwndWrapper.GetProperties(self)
0454#
0455#        props['ItemData'] = []
0456#        for i in range(self.ItemCount()):
0457#            props['ItemData'].append(self.ItemData(i))
0458#
0459#        return props
0460
0461    #-----------------------------------------------------------
0462    def Select(self, item):
0463        """Select the ListBox item
0464
0465        item can be either a 0 based index of the item to select
0466        or it can be the string that you want to select
0467        """
0468        self.VerifyActionable()
0469
0470        # Make sure we have an index  so if passed in a
0471        # string then find which item it is
0472        index = self._get_item_index(item)
0473
0474        # change the selected item
0475        self.SendMessageTimeout(win32defines.LB_SETCURSEL, index)
0476
0477        # Notify the parent that we have changed
0478        self.NotifyParent(win32defines.LBN_SELCHANGE)
0479
0480        win32functions.WaitGuiThreadIdle(self)
0481        time.sleep(Timings.after_listboxselect_wait)
0482
0483        return self
0484
0485    #-----------------------------------------------------------
0486    def SetItemFocus(self, item):
0487        "Set the ListBox focus to the item at index"
0488
0489        index = self._get_item_index(item)
0490
0491        # if it is a multiple selection dialog
0492        if self.HasStyle(win32defines.LBS_EXTENDEDSEL) or               self.HasStyle(win32defines.LBS_MULTIPLESEL):
0494            self.SendMessageTimeout(win32defines.LB_SETCARETINDEX, index)
0495        else:
0496            self.SendMessageTimeout(win32defines.LB_SETCURSEL, index)
0497
0498        win32functions.WaitGuiThreadIdle(self)
0499        time.sleep(Timings.after_listboxfocuschange_wait)
0500
0501        # return this control so that actions can be chained.
0502        return self
0503
0504
0505    #-----------------------------------------------------------
0506    def GetItemFocus(self):
0507        "Retrun the index of current selection in a ListBox"
0508
0509        # if it is a multiple selection dialog
0510        if self.HasStyle(win32defines.LBS_EXTENDEDSEL) or               self.HasStyle(win32defines.LBS_MULTIPLESEL):
0512            return self.SendMessage(win32defines.LB_GETCARETINDEX)
0513        else:
0514            return self.SendMessage(win32defines.LB_GETCURSEL)
0515
0516
0517#====================================================================
0518class EditWrapper(HwndWrapper.HwndWrapper):
0519    "Wrap a windows Edit control"
0520
0521    friendlyclassname = "Edit"
0522    windowclasses = [
0523        "Edit",
0524        "TEdit",
0525        "TMemo",
0526        r"WindowsForms\d*\.EDIT\..*",
0527        ]
0528
0529    #-----------------------------------------------------------
0530    def __init__(self, hwnd):
0531        "Initialize the control"
0532        super(EditWrapper, self).__init__(hwnd)
0533
0534        self.writable_props.extend([
0535            'SelectionIndices'])
0536
0537    #-----------------------------------------------------------
0538    def LineCount(self):
0539        "Return how many lines there are in the Edit"
0540        return  self.SendMessage(win32defines.EM_GETLINECOUNT)-1
0541
0542    #-----------------------------------------------------------
0543    def LineLength(self, line_index):
0544        "Return how many characters there are in the line"
0545
0546        # need to first get a character index of that line
0547        char_index = self.SendMessage(win32defines.EM_LINEINDEX, line_index)
0548
0549        # now get the length of text on that line
0550        return self.SendMessage (
0551            win32defines.EM_LINELENGTH, char_index, 0)
0552
0553
0554    #-----------------------------------------------------------
0555    def GetLine(self, line_index):
0556        "Return the line specified"
0557
0558        text_len = self.LineLength(line_index)
0559        # create a buffer and set the length at the start of the buffer
0560        text = ctypes.create_unicode_buffer(text_len+3)
0561        text[0] = unichr(text_len)
0562
0563        # retrieve the line itself
0564        self.SendMessage(
0565            win32defines.EM_GETLINE, line_index, ctypes.byref(text))
0566
0567        return text.value
0568
0569    #-----------------------------------------------------------
0570    def Texts(self):
0571        "Get the text of the edit control"
0572
0573        texts = [self.WindowText(), ]
0574
0575        for i in range(0, self.LineCount()+1):
0576            texts.append(self.GetLine(i))
0577
0578        return texts
0579
0580    #-----------------------------------------------------------
0581    def TextBlock(self):
0582        "Get the text of the edit control"
0583
0584        length = self.SendMessage(win32defines.WM_GETTEXTLENGTH)
0585        text = ctypes.create_unicode_buffer(length + 1)
0586        self.SendMessage(win32defines.WM_GETTEXT, length+1, ctypes.byref(text))
0587
0588        #text = text.value.replace("\r\n", "\n")
0589        return text.value
0590
0591    #-----------------------------------------------------------
0592    def SelectionIndices(self):
0593        "The start and end indices of the current selection"
0594        start = ctypes.c_int()
0595        end = ctypes.c_int()
0596        self.