13

I am using Selenium with Python and Chrome. I am trying to hold down various keys -- specifically "w, a, s, and d." I have found Selenium's action_chains.key_press action as well as the elem.send_keys method. The problem with the first method is that it only holds the key down for as long as it takes to complete an action chain. The problem with the elem.send_keys method is that it does not have an element to send the keys to.

I am trying to control a web-browser based robot with W-A-S-D, so I need to hold keys down for varying durations.

I have tried the following:

action_chains = ActionChains(driver)
action_chains.key_down("w")
action_chains.key_up("w")

as well as:

action_chains.key_press(elem, "w")
for x in range (0, 100):
    action_chains.perform()
    time.sleep(.01)

Neither are ideal.

Andrew K
  • 1,571
  • 1
  • 17
  • 25
  • Just to confirm, you're saying that `action_chains.key_down("w")` didn't work? Or did you need a method of lifting the key after some time has elapsed? – alex Jul 19 '13 at 22:39
  • `action_chains.key_down("w")` does work, but only for the split second it takes for my computer to do that action. Obviously the designers of the package made it take the minimum time possible. I need a way to hold it down and then release after some time. – Andrew K Jul 19 '13 at 22:43
  • Possible duplicate of [Python simulate keydown](http://stackoverflow.com/questions/11906925/python-simulate-keydown) – isopach Dec 14 '16 at 16:56

4 Answers4

6

The current driver for Chrome (version 2.30) implements the previous protocol where holding a key is only supported for a modifier key (Control, Shift, Alt, Command).

So this code works with Firefox but fails with Chrome since the keyUp event is emitted for each keyDown:

action_key_down_w = ActionChains(driver).key_down("w")
action_key_up_w = ActionChains(driver).key_up("w")

endtime = time.time() + 1.0

while True:
  action_key_down_w.perform()

  if time.time() > endtime:
    action_key_up_w.perform()
    break

But, since version 2.30, the Chrome driver supports the send_command to directly send a raw command via the devtools protocol. So as a workaround, you can call Input.dispatchKeyEvent to emit low level events.

This is a working example with Selenium/Chrome to hold the key w during a second:

from selenium import webdriver
import json, time

def dispatchKeyEvent(driver, name, options = {}):
  options["type"] = name
  body = json.dumps({'cmd': 'Input.dispatchKeyEvent', 'params': options})
  resource = "/session/%s/chromium/send_command" % driver.session_id
  url = driver.command_executor._url + resource
  driver.command_executor._request('POST', url, body)

def holdKeyW(driver, duration):
  endtime = time.time() + duration
  options = { \
    "code": "KeyW",
    "key": "w",
    "text": "w",
    "unmodifiedText": "w",
    "nativeVirtualKeyCode": ord("W"),
    "windowsVirtualKeyCode": ord("W")
  }

  while True:
    dispatchKeyEvent(driver, "rawKeyDown", options)
    dispatchKeyEvent(driver, "char", options)

    if time.time() > endtime:
      dispatchKeyEvent(driver, "keyUp", options)
      break

    options["autoRepeat"] = True
    time.sleep(0.01)


driver = webdriver.Chrome()
driver.get("https://stackoverflow.com/questions")

# set the focus on the targeted element
driver.find_element_by_css_selector("input[name=q]").click()

# press the key W during a second
holdKeyW(driver, 1.0)
Florent B.
  • 41,537
  • 7
  • 86
  • 101
  • Excellent! Meanwhile, it's Ok for me to use Firefox, but why this information is missing from everywhere? And I still doesn't understand what is the problem for Chrome, to support this action! It's so usefull for some situation, like Canvas Fields, where you need to test different Keyboard Input. – GensaGames Jun 14 '17 at 17:27
  • The protocol has changed a lot between Selenium 2 and 3, especially for the chained actions. You can find this information by reading the old and the new spec. I don't know why Chrome hasn't moved to the new api. Might be resource/budget issue. – Florent B. Jun 14 '17 at 17:46
2

Selenium actions chain Should only be used with modifier keys (Control, Alt and Shift). So you want to press only the character w-a-s-d. so that, it didn't work.

You can use any gui automation tools like pyautogui, etc.

please try below code and let me know.

import pyautogui

pyautogui.PAUSE = 10
pyautogui.keyDown('w')
pyautogui.keyUp('w')

pyautogui.PAUSE=10 command make 10 seconds pause after each PyAutoGUI call

Murthi
  • 5,299
  • 1
  • 10
  • 15
  • I need some possible solution on Selenium. To make sure, this action is appears only in Web PageWindow. But not in other places. – GensaGames Jun 12 '17 at 16:54
  • This action takes place on where ever the cursor is. Before this code you can make the browser window active using selenium. And selenium action chain operation require brower should be the top window. Otherwise key board and mouse operations will not work. – Murthi Jun 12 '17 at 17:09
  • I didn't use Action Chain action before, but other action, like WebDriver.send_keys() works without problem, whenever Chrome Window is hidden. – GensaGames Jun 12 '17 at 19:06
1

According to the Selenium Documentation for key_down, it states:

Should only be used with modifier keys (Control, Alt and Shift).

I've searched through the docs for an alternative solution, but it appears the behavior to "hold down" non-modifier keys is not possible in Selenium.

budi
  • 6,351
  • 10
  • 55
  • 80
  • 1
    It's just like warning in Selenium. Calling` key_down ` works for other Keys, but only like clicking, and not holding (and I don't know, why). – GensaGames Jun 12 '17 at 19:04
  • @GensaGames Sometimes (unfortunately) the answer to these kinds of questions is simply "It's not possible". You can definitely request it as a new feature or contribute the feature yourself here: https://github.com/SeleniumHQ/www.seleniumhq.org/issues – budi Jun 12 '17 at 19:36
  • @GensaGames Can you please describe the behavior you are trying to test? – budi Jun 12 '17 at 21:50
  • 1
    Try to test Canvas changes. For ex. https://chromedino.com/ where need to hold Keys.ARROW_DOWN action for some time. – GensaGames Jun 13 '17 at 04:54
  • @GensaGames I haven't done any projects that require testing keydown events on a canvas before, but I don't believe Selenium would be the right tool for this. I would consider using a different automation tool. – budi Jun 13 '17 at 15:24
0

ActionChains(driver).key_down("w").pause(0.1).key_up("w").perform()

By chaining key_down and key_up with a .pause(0.1) in between you can hold down keys for any custom duration.

Even though key_down should only be used with modifier keys according to the docs, it turns out that it can be used with any key.

Bono
  • 17
  • 6