Python Tkinter Menu Command Not Working - python

I am trying to execute the following code in Python 2.6.5. What I want to do is show a main window with an 'Applications' menu. I want the menu to have a series of commands which should correspond to the keys of the Apps dictionary. When I click the command, I would like the default web browser to open and navigate to the url in the Apps dictionary for that particular key. Instead, when I execute the code the browser is opening to the first url in the Apps dictionary without any clicking. Help please!
from Tkinter import *
import webbrowser
#Real links are to pages on Intranet.
Apps={
'Google':'http://www.google.com/',
'Yahoo':'http://www.yahoo.com/'
}
def openApp(appURL):
webbrowser.open(appURL, new=1, autoraise=1)
return None
root=Tk()
menubar=Menu(root)
root.config(menu=menubar)
appsMenu=Menu(menubar)
for app in Apps:
appsMenu.add_command(label=app, command=openApp(Apps[app]))
menubar.add_cascade(label='Apps', menu=appsMenu)
root.mainloop()

appsMenu.add_command(label=app, command=openApp(Apps[app]))
Command parameters that call functions need to be wrapped in a lambda, to prevent them from being called right away. Additionally, commands bound within a for loop need the looping variable as a default argument, in order for it to bind the right value each time.
appsMenu.add_command(label=app, command=lambda app=app: openApp(Apps[app]))

Related

tkinter checkbutton not displaying correct value when class imported from other file

I have a main program which does some cool stuff and I am currently setting up a 'settings editor' to let the user change some GUI related stuff and default values. It reads values from a text file, which is read in correctly and saves them to a dictionary self.propertiesDict. Some of the options are on/off switches, so I use checkbuttons for them. What is puzzling me is following behavior: the code works perfectly fine, when I execute the settingsEditor.py (the script creating the settings window) directly. All the checkbuttons are set to active / True. However, when I include my settingsEditor in my main program and call it, it creates fine but all the checkbuttons show the wrong value: False. I read a lot of topics here to find an answer, but I think I avoided the most common errors:
I use the tk variables
tk variables are created and set prior to the buttons
variables are not only in local scope (prefixed self.)
As you can see, I tried with an IntVar and a BooleanVar, but neither is working correctly. Something else is strange, when I use ttk.checkbuttons, I get the issue described here. I use Visual Studio for debugging and I can't see any difference in the process when going trough line by line, except for the wrong display result. I am happy for any suggestion. Sorry for not providing a full MWE, I will do, if nobody can help me from this here.
settingsEditor.py
import tkinter as tk
from tkinter import ttk
...
class mySettingsEditor:
def __init__(self):
...
def createGUI(self):
# Show main options on startup on/off
self.showOptionsVar = tk.IntVar()
self.showOptionsVar.set(str2int(self.propertiesDict['showMainOptionsExpanded']))
print(self.showOptionsVar.get())
self.checkBtn1 = tk.Checkbutton(Frame, text='Main Options Section', variable=self.showOptionsVar)
self.checkBtn1.grid(column=0,row=2)
# Show main STL section on startup on/off
self.showMainSTLVar = tk.BooleanVar()
self.showMainSTLVar.set(str2bool(self.propertiesDict['showMainSTLSectionExpanded']))
print(self.showMainSTLVar.get())
self.checkBtn2 = tk.Checkbutton(Frame, text='Main STL Section', variable=self.showMainSTLVar)
self.checkBtn2.grid(column=0,row=3)
main.py
from settingsEditor import mySettingsEditor
...
settEditor = mySettingsEditor()
This is how it looks in the GUI when executed separately (terminal with print output to the left):
Thats the result when I add it in main.py. The boxes are unchecked, but .get() tells me the values are correctly assigned to the tk variables.
As suggested by jasonharper, switching to Toplevel() for the child windows fixed the issue. Thanks alot!

Tkinter shortcuts in context menu on Windows

I a have a menu that I display as a context menu in a tkinter application:
rmenu = tk.Menu(None, tearoff=0, takefocus=0)
# add some commands here
rmenu.add_command(label="Copy", copyfunction, accelerator="Shift-C")
rmenu.bind("<Shift-KeyPress-C>", copyfunction) # this doesn't work on windows!
I define a shortcut for a command in this context menu, and then, when I try to run the command by typing <Shift>+<C> on windows it doesn't work. Actually, I get a beep.
Is there a way to define shortcut in this situation?
Update:
I have even tried to generate a event in the program using:
rmenu.event_generate("<Shift-KeyPress-C>", when="tail")
But the function copyfunction wasn't called.
PS: This works on Linux
bind the keyboard shortcut to the root. Only things with focus listen for key bindings. How often does the context menu have focus? By using bind_all on the root, whatever has focus will trigger the key binding.
root.bind_all("<Shift-C>", copyfunction)

I have an issue with running another Python file with tkinter

I am trying to link my code that runs motors from tkinter condition. I didn't get any methods to do so.
I have tried hyperlink method, but it only opens the file on my browser.
...python
from tkinter import *
import webbrowser
def callback(url):
webbrowser.open_new(url)
root = Tk()
link1 = Label(root, text="Running Hyperlink", fg="blue", cursor="hand2")
link1.pack()
link1.bind("<Button-1>", lambda e: callback(r"/home/pi/ROBOT/pdf folder/GUIfunal1.py"))
root.mainloop()
The statement webbrowser.open_new(url) opens the python script on the browser, but does not run it.
Without setting up a Flask server and loading the python script to the server, a python script will not be run. (There might be other ways, but Flask server is the one I know of).
If you do not want to hyperlink it, running it like exec('path to python file.py') should run it.

Python windows app UI automation testing: how to send mouse and keyboard events to winappdriver?

I am testing a window app( window form). I use python(appium, robotframework), winAppDriver.
My task:
right click a button, show the context menu, and select one of them.
what I need to do:
using python to send out right click, and then select menu action( just like action chain in selenium web)
I have located the element. but I went through doc, still did not able to find out how to do it in python.
is it possible via:
send post to url 127.0.0.1:4723/:sessionId/buttondown.
using python to send keys to web element location?(i managed to do this in my code, but this is not what I want, the code looks ugly)
my short code:
#to test a window application, wrote by C# windows form
from appium import webdriver
desired_caps = {}
desired_caps["app"] = "D:\\sample.exe"
driver =webdriver.Remote(command_executor='http://127.0.0.1:4723',desired_capabilities=desired_caps)
button= driver.find_element_by_name("Root")
#button.contextClick()??
#how to
#I managed to use pyautogui, to send mouse and keyboard event, but the code look ugly. FYI.
driver_location=driver.get_window_position()
root=driver.find_element_by_name("Root")
root.click()
button_location=root.location
x, y = pyautogui.position()
pyautogui.moveTo(button_location['x']+driver_location['x'],button_location['y']+driver_location['y'])
pyautogui.click( button='right')
You can use driver.send_keys(Keys.SHIFT + Keys.F10) (Shift+F10 from keyboard is shortcut for Right click).

Which is the best way to interact with already open native OS dialog boxes like (Save AS) using Python?

Is there any efficient way using any Python module like PyWind32 to interact with already existing Native OS dialog boxes like 'Save As' boxes?
I tried searching on Google but no help.
EDIT:
1: The Save As dialog box is triggered when user clicks on a Save As dialog box on a web application.
2: Any suggestion are welcome to handle any native OS dialog boxes which are already triggered using Python.(Need not be specific to Selenium webdriver, I am looking for a generic suggestion.)
(When I was posting the question I thought that by 'interacting with a dialog box' will implicitly mean that it is an existing one as if I am able to create one then surely I can interact with it as it is under my programs control. After reading the first 2 answers I realized I was not explicitly clear. Thats why the EDIT )
Thanks
While looking for a possible solution for this I came across several solutions on SO and otherwise.
Some of them were using AutoIT, or editing browsers profile to make it store the file directly without a prompt.
I found all this solution too specific like you can overcome the issue for Save As dialog by editing the browser profile but if later you need to handle some other window then you are stuck.
For using AutoIT is overkill, this directly collides for the fact I choose Python to do this task. (I mean Python is itself so powerful, depending on some other tool is strict NO NO for any Pythonist)
So after a long search for a possible generic solution to this problem which not only serves any one who is looking to handle any Native OS dialog boxes like 'Save As', 'File Upload' etc in the process of automating a web application using selenium web driver but also to any one who wants to interact with a specific window using only Python APIs.
This solution makes use of Win32gui, SendKeys modules of Python.
I will explain first a generic method to get hold of any window desired then a small code addition which will also make this usable while automating a web application using Selenium Webdriver.
Generic Solution::
import win32gui
import re
import SendKeys
class WindowFinder:
"""Class to find and make focus on a particular Native OS dialog/Window """
def __init__ (self):
self._handle = None
def find_window(self, class_name, window_name = None):
"""Pass a window class name & window name directly if known to get the window """
self._handle = win32gui.FindWindow(class_name, window_name)
def _window_enum_callback(self, hwnd, wildcard):
'''Call back func which checks each open window and matches the name of window using reg ex'''
if re.match(wildcard, str(win32gui.GetWindowText(hwnd))) != None:
self._handle = hwnd
def find_window_wildcard(self, wildcard):
""" This function takes a string as input and calls EnumWindows to enumerate through all open windows """
self._handle = None
win32gui.EnumWindows(self._window_enum_callback, wildcard)
def set_foreground(self):
"""Get the focus on the desired open window"""
win32gui.SetForegroundWindow(self._handle)
win = WindowFinder()
win.find_window_wildcard(".*Save As.*")
win.set_foreground()
path = "D:\\File.txt" #Path of the file you want to Save
ent = "{ENTER}" #Enter key stroke.
SendKeys.SendKeys(path) #Use SendKeys to send path string to Save As dialog
SendKeys.SendKeys(ent) #Use SendKeys to send ENTER key stroke to Save As dialog
To use this code you need to provide a string which is the name of the window you want to get which in this case is 'Save As'. So similarly you can provide any name and get that window focused.
Once you have the focus of the desired window then you can use SendKeys module to send key strokes to the window which in this case includes sending file path where you want to save the file and ENTER.
Specific to Selenium Webdriver::
The above specified code segment can be used to handle native OS dialog boxes which are triggered through a web application during the automation using Selenium Webdriver with the addition of little bit of code.
The issue you will face which I faced while using this code is that once your automation code clicks on any Web Element which triggers a native OS dialog window, the control will stuck at that point waiting for any action on the native OS dialog window. So basically you are stuck at this point.
The work around is to generate a new thread using Python threading module and use it to click on the Web Element to trigger the native OS dialog box and your parent thread will be moving on normally to find the window using the code I showed above.
#Assume that at this point you are on the page where you need to click on a Web Element to trigger native OS window/dialog box
def _action_on_trigger_element(_element):
_element.click()
trigger_element = driver.find_element_by_id('ID of the Web Element which triggers the window')
th = threading.Thread(target = _action_on_trigger_element, args = [trigger_element]) #Thread is created here to call private func to click on Save button
th.start() #Thread starts execution here
time.sleep(1) #Simple Thread Synchronization handle this case.
#Call WindowFinder Class
win = WindowFinder()
win.find_window_wildcard(".*Save As.*")
win.set_foreground()
path = "D:\\File.txt" #Path of the file you want to Save
ent = "{ENTER}" #Enter key stroke.
SendKeys.SendKeys(path) #Use SendKeys to send path string to Save As dialog
SendKeys.SendKeys(ent) #Use SendKeys to send ENTER key stroke to Save As dialog
#At this point the native OS window is interacted with and closed after passing a key stroke ENTER.
# Go forward with what ever your Automation code is doing after this point
NOTE::
When using the above code in automating a web application, check the name of the window you want to find and pass that to find_window_wildcard(). The name of windows are browser dependent. E.g. A window which is triggered when you click on an Element to Upload a file is called 'File Upload' in Firefox and Open in Chrome.
Uses Python2.7
I hope this will help any one who is looking for a similar solution whether to use it in any generic form or in automating a web application.
EDIT:
If you are trying to run your code through command line arguments then try to use the thread to find the window using Win32gui and use the original program thread to click on the element (which is clicked here using the thread). The reason being the urllib library will throw an error while creating a new connection using the thread.)
References::
SO Question
SenKeys
Win32gui
There is a Python module called win32ui. Its found in the Python for Windows extensions package. You want the CreateFileDialog function.
Documentation
Edit:
This is a save dialog example. Check the documentation for the other settings.
import win32ui
if __name__ == "__main__":
select_dlg = win32ui.CreateFileDialog(0, ".txt", "default_name", 0, "TXT Files (*.txt)|*.txt|All Files (*.*)|*.*|")
select_dlg.DoModal()
selected_file = select_dlg.GetPathName()
print selected_file
from time import sleep
from pywinauto import Application, keyboard
app = Application(backend='uia').start('notepad.exe')
app.top_window().set_focus()
main = app.window(title='Untitled - Notepad', control_type='Window')
main.draw_outline(colour='red', thickness=2)
fileMenu = main.child_window(title='File')
fileMenu.draw_outline(colour='red', thickness=2)
fileMenu.click_input()
keyboard.send_keys("{VK_CONTROL}{VK_SHIFT}{S}")
sleep(2)
dlg = main.window(title='Save As')
dlg.draw_outline(colour='red', thickness=2)
savebtn = dlg.child_window(title='Save')
filePath = dlg.child_window(title='File name:', control_type='Edit')
savebtn.draw_outline(colour='red', thickness=2)
filePath.set_text('D:\\aa.txt')
filePath.draw_outline(colour='red', thickness=2)
savebtn.click_input()
I recommend using pywinauto lib.
If you have opened notepad and "Save as" dialog window:
from pywinauto import application
app = application.Application()
app.connect(title='test1.txt - Notepad')
app.SaveAs.Edit1.type_keys("test2.txt")
app.SaveAs.Save.click()

Categories