I'm trying to write an AppleScript that tells if application has a specific menubar item. I've found this snippet on the internet, and tried to run, but it doesn't produce any effect. I've spliced into it some debug statements to check the control flow, and it seems not to enter the conditional 'if' at all.
No error message, no output, nothing!
on menuItemExists({appName, menuName1, menuItem1})
display notification "1"
tell application "System Events"
display notification "2"
tell application process appName
display notification "3"
if menu item menuItem1 of menu 1 of menu bar item menuName1 of menu bar 1 exists then
display notification "4"
return true
else
return false
end if
end tell
display notification "5"
end tell
end menuItemExists
if menuItemExists({"timeEdition", "Extras", "Start Recording"}) then
display dialog "hoda"
end if
If we assume that the AppleScript is functioning as it should, then this leads to one conclusion: at least one of those menu or menu item GUI objects does not exist, and therefore the script has nothing to return.
If that conditional statement passed—and only if it passed—then notification 4 would be displayed, and the function menuItemExists would return true. If this function returns true—and only if it returns true—then a dialog box would appear displaying the message "hoda".
However, no such dialog appeared, which implies that menuItemExists did not return true. If it did not return true, then it must have returned false. And if it returned false, this means the condition of some menu item’s existence failed, so no notification after number 3 would have been seen.
I believe you were expecting some output even in the event of failure. However, there is no error in the script, and the only statements that return values are all inside a function handler. That returns its value (which we’ve now deduced was the boolean value false) to the parent script that called it; and it’s only the parent script that would return any value—if it had a value to return—to the output window you were expecting to display a result.
Without having the application you’re using on my computer, it’s not possible for me to determine where in that conditional clause the assertion fails. My advice is to test each object individually, from menu bar 1 through to the menu item in question, and I’m confident the culprit will emerge.
The AppleScript code in your OP is only going to return true and trigger the display dialog "hoda" if and only if timeEdition is running and is not actively recording. When it's actively recording the menu shows Stop Recording. If timeEdition is not running, you can add tell application "timeEdition" to activate to the top of the script if you want the handler to result in true and display "hoda" in a dialog box when it's running and not actively recording.
display notification "5" will never trigger because of the use of return with both true and or false triggering prior to it.
I installed timeEdition and tested the AppleScript code in your OP and it does return false in Script Editor if it's not running. If running and not actively recording, it does trigger display dialog "hoda".
So, the AppleScript code in your OP actually works just as coded, although how it's coded is not of any practical use other then some testing, until its recoded for a real world use case scenario.
Related
Context: In my rspec (using Ruby and Capybara)
I click on a link to test an action in my app: adding a branch to my app.
A modal window opens, where I select the branch, and then I click a "submit" button to add the branch to my app. After clicking "submit" the modal window is closed.
The rspecs continues by clicking "Save" in the main screen, to save the state of the application (and effectively saving adding the branch).
Problem: The rspec is failing because (seemingly) it is trying to click the "Save" button on the main screen while the modal window that is used to select the branch is still present. The test doesn't complain that it can't find the "Save" button component, but that it can't be clicked.
The error in the log is:
[...]Save</button> is not clickable at point (692, 23). Other element would receive the click[...]
A gotcha: this rspec passes correctly on some environments, like when it is run against my local server, but it fails when it is executed by our automation server. Thus, this test has been tagged as "flaky".
Potential solutions: Things we have tried so far:
Play around our "clicks configuration", making sure we are on "ready state" and that the modal window is gone. We failed with this, since we kept hitting the same error.
Implement a "wait". We added a loop to sleep for a bit while the modal window seemed to exist
XYZ.add_new_branch_name(#branch_name)
while Utilities.element_visible?(:xpath, myElement)
sleep(0.5)
end
XYZ.save
The while condition checks if the "submit" button of the modal window exists. The element_visible function uses
find(method,element).visible?
but I'm not sure if find should already take into account that the button may exist and be visible but not be clickable.
Since this still fails, in spite of all our effort to make sure that the modal is gone before we attempt to click on the "save" button, I want tot ask:
Is there a proper way to detect if an element behind a modal window is clickable or not using rspecs?
find only cares about "visibility", not "clickability" (and different drivers may have slightly different interpretations of "visibility"). The reason for the flakiness you're seeing is most likely speed of the machine running the tests which affects the timing of the modal animating away. The best way to solve this issue is to disable animations in the test mode (how you do that is dependent on exactly what library and/or CSS you're using for the animations). The other way is to do as you're doing - checking that the modal has disappeared before clicking the 'Save' button, however you should just be using the Capybara provided methods (which include waiting/retrying behavior) rather than writing your own loop for that.
expect(page).not_to have_css('css selector of the modal') # RSpec version
assert_no_css('css selector of the modal') # minitest version
After looking at the mouse position from your error, one other potential issue you may be having is with screen size and scrolling. If the page requires to be scrolled to get to the 'Save' button and (692, 23) would put the button behind a fixed header (you should be able to verify that by taking a screenshot before the button click attempt) then it may not be possible for whatever driver you're using to click the button. In that case you'd need to use execute_script to scroll the page to a different location so the button is not covered on the page and/or increase the "browser" size so scrolling isn't necessary in the test.
I had a similar problem and solved it by writing my own click_on_with_wait helper function:
def click_on_with_wait(text, wait_time: Capybara.default_max_wait_time)
success = false
(wait_time * 10).round.times do
click_on text
success = true
break
rescue Selenium::WebDriver::Error::WebDriverError
sleep(0.1)
end
# Try clicking one last time, so that the error will get raised if it still doesn't work
click_on text unless success
end
This will try to click on the element. If it's still hidden by the modal, the function will wait 100ms and then try again, until the given wait_time is reached.
Using Rails, I put it in system_spec_helpers.rb so that I can simply replace click_on 'Submit Form' with click_on_with_wait 'Submit Form'.
UPDATE3 - SOLVED with reservations, please see my solution below; leaving it open since the cause of the problem is unclear, and I don't know how robust the solution is.
UPDATE1: here's the short short version.
Currently, after .setEditText on a QComboBox, I get this:
so the next thing you type will overwrite 'Team '.
But the desired effect is this (unhighlighted / unselected), so that the next thing you type will be appended to 'Team ' instead of overwriting it:
Thanks for any help. The rambling details are below, which is the original post:
(this is all PyQt 5.4)
UPDATE2:
Apparently python doesn't think anything is actually selected:
self.entryForm.ui.teamField.lineEdit().setText("Team ")
print("selected text:"+self.entryForm.ui.teamField.lineEdit().selectedText())
prints "selected text:" and nothing else. To make sure that's working:
self.entryForm.ui.teamField.lineEdit().setText("Team ")
self.entryForm.ui.teamField.lineEdit().setSelection(0,4)
print("selected text:"+self.entryForm.ui.teamField.lineEdit().selectedText())
prints "selected text:Team"
So that might be why many of the methods that affect selection are not working as expected (.deselect(), .setSelection(5,5), etc, and even some of the other methods give unexpected behavior, i.e. cursorForward(False,1) or cursorBackward(False,1) and such.
Original post:
This is for a radio log GUI, so keyboard interactions must be minimal and intuitive. openNewEntryForm (below) is called as a slot from a pushbutton on the main application GUI window:
self.ui.pushButton.clicked.connect(self.openNewEntryDialog)
It can also be called using a keyPressEvent in the same class:
def keyPressEvent(self,event):
if type(event)==QKeyEvent:
print("QKeyEvent:"+str(event.key()))
if event.key()==Qt.Key_T:
self.openNewEntryDialog('t')
event.accept()
else:
event.ignore()
Here's the method in question:
def openNewEntryDialog(self,key=None):
self.entryDialog=newEntryDialog()
if key=='t':
self.entryDialog.ui.to_fromField.setCurrentIndex(1)
self.entryDialog.ui.teamField.setFocus()
self.entryDialog.ui.teamField.setEditText("Team ")
if self.entryDialog.exec_():
self.newEntry(self.entryDialog.getValues()) # adds the log entry
so, the intended key press sequence is (from the main application GUI window):
a single keyboard press of 't' will open the entryForm, set the to_fromField to index 1 (which happens to be "TO"), give focus to teamField (also a QComboBox), set its text to "Team " and set itself up so that the very next keypress will appear as the text following "Team " in teamField.
So, starting from the main app GUI again, the plan is that typing 't3' should open the new entry window, set the to_fromField to "TO", and set the teamField to "Team 3", ready for a keypress of the tab key to move on to the next field in the entryForm.
The problem is that the teamField.setEditText("Team ") call leaves all of the text highlighted/selected, so that a subsequent key press of '3' would replace "Team " with "3"; I'm looking for a way to unhighlight/unselect "Team " but leave the cursor active at the right of that string, so that the subsequent key press of '3' would make the entire string "Team 3".
Ideas? Thanks in advance.
You can access the line-edit of the combo box, and then remove the selection:
self.entryDialog.ui.teamField.setEditText("Team ")
self.entryDialog.ui.teamField.lineEdit().deselect()
UPDATE:
The above code is correct, but it seems that the dialog will then clobber it when it initialises the focus handling for its child widgets after it is shown. If a dialog is opened with exec(), it will start its own event-loop, and some events (including focus events) will only be processed after it is fully shown. This is why it may appear that some changes made to child widgets before the dialog is shown are being ignored.
One way to work around this is to use a single-shot timer to ensure the changes are only attempted after the dialog is shown.
So add a method to the entry dialog class something like this:
def resetUI(self, key):
if key == 't':
self.ui.to_fromField.setCurrentIndex(1)
self.ui.teamField.setFocus()
self.ui.teamField.setEditText('Team ')
QtCore.QTimer.singleShot(0, self.ui.teamField.lineEdit().deselect)
and then use it like this:
def openNewEntryDialog(self, key=None):
self.entryDialog = newEntryDialog()
self.entryDialog.resetUI(key)
if self.entryDialog.exec_():
self.newEntry(self.entryDialog.getValues())
SOLVED with reservations, see UPDATE3 in the original post.
So, with the initial text all highlighted, tests show that it didn't actually think anything was selected. This solution was just stumbled upon by trial and error, fiddling with setting and clearing focus, selecting text and trying deselect:
def openNewEntryDialog(self,key=None):
self.entryForm=newEntryDialog()
if key=='t':
self.entryForm.ui.to_fromField.setCurrentIndex(1)
self.entryForm.ui.teamField.lineEdit().setFocus()
self.entryForm.ui.teamField.lineEdit().setText("Team ")
self.entryForm.ui.teamField.lineEdit().setSelection(5,1)
Notice there are two spaces after 'Team' and the second one is intentionally selected. Then the very next keypress will overwrite that second space; that is basically the desired behavior.
Anyway it looks like something bizarro with the selection scheme; one way to look at this is that the highlight isn't really a selection, but, if you set a valid real selection then it will override the original highlighted 'pseudo-selection'. The original highlighting behaves like a selection in that a keypress will replace everything that's highlighted, but, not like a selection in that the selection methods reveal that there is no 'selection', see UPDATE2 in the original post.
Can anyone help explain this behavior? I'd like to build some more confidence in it before accepting this coincidental answer.
Thanks
I have the following script:
gosub, menucreate
Settimer, CheckRun, 1000
ExitSub:
ExitApp
return
Mess:
msgbox tada
return
menucreate:
Menu, TRAY, DeleteAll
Menu, TRAY, NoStandard
Menu, TRAY, Add, Exit , ExitSub
Menu, TRAY, Add, Message , Mess
return
CheckRun:
gosub ,menucreate
return
On occasions the menu Message works, but sometimes not.
Every few starts of this program it shows the message, and it keeps working if I reload.
But if I restart it might not working (or might ... I couldn't figure out any pattern)
To me it seems like you fail to end the Auto-execute section.
After the script has been loaded, it begins executing at the top line, continuing until a Return, Exit, hotkey/hotstring label, or the physical end of the script is encountered (whichever comes first). This top portion of the script is referred to as the auto-execute section.
So try and put a return after your settimer and see if that helps
OK, in mean time I found out, but till now hadn't time to add this here (and kinda forgot).
The thing is: If the menu gets redrawn every second it will only cause an action until it gets redrawn.
So if you have pulled it up and the redraw happens before you click on a item nothing happens.
This thing here was just a test script to find out why the actual one has stopped working.
In that one I have included a test if actually something has changed and only then the menu get redrawn ... and voila it works.
I want to perform a operation after a window is closed, i.e Suppose I have a window or any dialog popped up and now I want my code to wait until this particular dialog get closed and after that I want to code continue.
Means I want to wait till that window is opened without using hardcoded 'Wait()' function.
Is there any method in VBScript or QTP that fulfills my needs?
You could try the 'WaitProperty' method on your window to determine when visible property becomes false, but that might throw an error once the window is no longer available. Otherwise, you can always loop until it no longer exists
While Window("My Window").Exist(0)
Wait 0, 500 ' Pause briefly before looking again
Wend
I'm writing a MEL script which involves opening the grease pencil UI toolbar. I want to remove the close button on that toolbar. I tried doing
GreasePencilTool;
window -edit -tbm 0 greasePencilFloatingWindow;
but get Error: line 2: window: Object 'greasePencilFloatingWindow' not found.
Further tests reveal that running
GreasePencilTool;
window -q -exists greasePencilFloatingWindow;
will return a result of 0.
Running GreasePencilTool; and then window -edit -tbm 0 greasePencilFloatingWindow; at separate times works as expected, as does running window -edit -tbm 0 greasePencilFloatingWindow; when the toolbar is already open.
However, I need to be able to remove the close button immediately when the toolbar opens.
The closest thing I can think of that illustrates what I want to do are Javascript callback functions, where another function can be executed once the current function is finished... but is there a way to do something like that in MEL?
I've also tried using the evalDeferred command without success.
The grease pencil tool is launched asynchronously so the window will not be present for some unknown length of time. This means the best you could do is trigger a function which would check periodically and do it the next time you find the correctly named window; you could attach this to an idle time script job.
It's ugly. But it is probably the only way since there's no event that will notify when thje window arrives. If you do that, make the script job suicide after it fires so it's not sitting there on every idle check till the end of time.