Tkinter TTK OptionMenu won't open - unix

Please see my edit at the bottom, this issue is now OS specific.
A gif of the problem in action
So I'm having an issue with an instance of ttk.OptionMenu. I've successfully implemented the widget before, however I'm trying to use it here as a dropdown for available files in a pop-up window, and I can't seem to get it to work right.
The issue
Only the first option in the menu is seen, and no other options are available
It doesn't respond to the mouse other than dimming when clicked
When it is clicked, no errors or output are seen in the terminal, making it hard to trace
The Code
The actual call is made from another file, let's say myproject/main.py
from classes.load_window import *
start_load_menu()
The class for this is stored in a file at myproject/classes/load_window.py, and it accesses save files stored in myproject/saved/
import tkinter
import tkinter.ttk as ttk
from os import listdir
from os.path import join, isfile
class LoadMenu(object):
def __init__(self):
root = self.root = tkinter.Tk()
root.title("Save Manager")
root.overrideredirect(True)
""" MAIN FRAME """
frm_1 = ttk.Frame(root)
frm_1.pack(ipadx=2, ipady=2)
""" MESSAGE LABEL """
self.msg = str("Would you like to load from a save file?")
message = ttk.Label(frm_1, text=self.msg)
message.pack(padx=8, pady=8)
""" INNER FRAME """
frm_2 = ttk.Frame(frm_1)
frm_2.pack(padx=4, pady=4)
""" TEST IMPLEMENTAITON [DOES NOT WORK] """
mylist = ['1', '2', '3', '4', '5', '6', '7']
test_var = tkinter.StringVar(frm_2)
test_var.set(mylist[3])
test_dropdown = ttk.OptionMenu(frm_2, test_var, *mylist)
test_dropdown.pack(padx=4, pady=4)
print(mylist) # Results in ['1', '2', '3', '4', '5', '6', '7']
""" REAL IMPLEMENTATION [ALSO DOES NOT WORK] """
files = [f for f in listdir('saved') if isfile(join('saved', f))]
file_var = tkinter.StringVar(frm_2)
file_var.set(files[3])
file_dropdown = ttk.OptionMenu(frm_2, file_var, *files)
file_dropdown.pack(padx=4, pady=4)
print(files) # Results in ['DS_Store', 'test1', 'test2', 'test3']
""" BUTTON FUNCTIONALITY """
btn_1 = ttk.Button(frm_2, width=8, text="Load File")
btn_1['command'] = self.b1_action
btn_1.pack(side='left')
btn_2 = ttk.Button(frm_2, width=8, text="Cancel")
btn_2['command'] = self.b2_action
btn_2.pack(side='left')
btn_3 = ttk.Button(frm_2, width=8, text="Create New")
btn_3['command'] = self.b3_action
btn_3.pack(side='left')
btn_2.bind('<KeyPress-Return>', func=self.b3_action)
root.update_idletasks()
""" Position the window """
xp = (root.winfo_screenwidth() // 2) - (root.winfo_width() // 2)
yp = (root.winfo_screenheight() // 2) - (root.winfo_height() // 2)
geom = (root.winfo_width(), root.winfo_height(), xp, yp)
root.geometry('{0}x{1}+{2}+{3}'.format(*geom))
root.protocol("WM_DELETE_WINDOW", self.close_mod)
root.deiconify()
def b1_action(self, event=None):
print("B1")
def b2_action(self, event=None):
self.root.quit()
def b3_action(self, event=None):
print("B3")
def nothing(self):
print("nothing")
def close_mod(self):
pass
def time_out(self):
print ("TIMEOUT")
def to_clip(self, event=None):
self.root.clipboard_clear()
self.root.clipboard_append(self.msg)
def start_load_menu():
menu = LoadMenu()
menu.root.mainloop()
menu.root.destroy()
return menu.returning
Notes
This code is based on a response here for a pop up window that I'm in the process of adapting for a specific purpose (the load menu).
I distilled this code to the minimum to reproduce the issue, but you can probably ignore the function definitions and window geometry.
Everything works fine other than this; the window is displayed center screen, and the button with actual functionality closes the window, it's just this odd quirk with the OptionMenu that I can't seem to find anyone else struggling with, either on here, or other forums.
In case you didn't see the link at the top, you can find a demonstration of the troublesome behavior at this link.
I'm using Python 3.6.4 on OSX 10.12.6
EDIT:
I've since tested this code in a VM running Hydrogen Linux, and it works fine. My question then changes a little:
How can I ensure that this code translates well to OSX? Is there reading available on the discrepancies between running TKinter on different platforms?
I have found this page on the issues regarding Python, TKinter, and OSX, but even when using the recommended TCL packages with the latest stable release of Python, this issue persists.
EDIT 2:
Just to update, I have since found a workaround for the problem. It doesn't answer the question of the odd behavior of the OptionMenu, but I figured I would edit. Honestly, I think Listbox is probably better suited for what I wanted to do anyways. Here it is in action.
Please let me know if I need to make any edits for clarity, or provide additional info. As I'm new to stackoverflow, I don't have much experience sharing issues here. Thank you!

Doing nothing other than changing "files" to a hard-coded list allows the program to run on my machine. I can help you no further.
import tkinter
import tkinter.ttk as ttk
from os import listdir
from os.path import join, isfile
class LoadMenu(object):
def __init__(self):
root = self.root = tkinter.Tk()
root.title("Save Manager")
root.overrideredirect(True)
""" MAIN FRAME """
frm_1 = ttk.Frame(root)
frm_1.pack(ipadx=2, ipady=2)
""" MESSAGE LABEL """
self.msg = str("Would you like to load from a save file?")
message = ttk.Label(frm_1, text=self.msg)
message.pack(padx=8, pady=8)
""" INNER FRAME """
frm_2 = ttk.Frame(frm_1)
frm_2.pack(padx=4, pady=4)
""" TEST IMPLEMENTAITON [DOES NOT WORK] """
mylist = ['1', '2', '3', '4', '5', '6', '7']
test_var = tkinter.StringVar(frm_2)
test_var.set(mylist[3])
test_dropdown = ttk.OptionMenu(frm_2, test_var, *mylist)
test_dropdown.pack(padx=4, pady=4)
print(mylist) # Results in ['1', '2', '3', '4', '5', '6', '7']
""" REAL IMPLEMENTATION [ALSO DOES NOT WORK] """
##files = [f for f in listdir('saved') if isfile(join('saved', f))]
files=['a', 'b', 'c', 'd', 'e', 'f']
file_var = tkinter.StringVar(frm_2)
file_var.set(files[3])
file_dropdown = ttk.OptionMenu(frm_2, file_var, *files)
file_dropdown.pack(padx=4, pady=4)
print(files) # Results in ['DS_Store', 'test1', 'test2', 'test3']
""" BUTTON FUNCTIONALITY """
btn_1 = ttk.Button(frm_2, width=8, text="Load File")
btn_1['command'] = self.b1_action
btn_1.pack(side='left')
btn_2 = ttk.Button(frm_2, width=8, text="Cancel")
btn_2['command'] = self.b2_action
btn_2.pack(side='left')
btn_3 = ttk.Button(frm_2, width=8, text="Create New")
btn_3['command'] = self.b3_action
btn_3.pack(side='left')
btn_2.bind('<KeyPress-Return>', func=self.b3_action)
root.update_idletasks()
""" Position the window """
xp = (root.winfo_screenwidth() // 2) - (root.winfo_width() // 2)
yp = (root.winfo_screenheight() // 2) - (root.winfo_height() // 2)
geom = (root.winfo_width(), root.winfo_height(), xp, yp)
root.geometry('{0}x{1}+{2}+{3}'.format(*geom))
root.protocol("WM_DELETE_WINDOW", self.close_mod)
root.deiconify()
def b1_action(self, event=None):
print("B1")
def b2_action(self, event=None):
self.root.quit()
def b3_action(self, event=None):
print("B3")
def nothing(self):
print("nothing")
def close_mod(self):
pass
def time_out(self):
print ("TIMEOUT")
def to_clip(self, event=None):
self.root.clipboard_clear()
self.root.clipboard_append(self.msg)
##def start_load_menu():
menu = LoadMenu()
menu.root.mainloop()
## menu.root.destroy()
## return menu.returning

I figured it out!
After digging into this problem further, I distilled the code to the minimum (which I probably should have done before posting here...) and was able to pinpoint root.overrideredirect(True) as the offending line.
When using overrideredirect(True), it's necessary to also use update_idletasks() before, in order to ensure that the Widget refreshes properly. While it seems Linux can still produce normal behavior without manually updating idle tasks, OS X cannot, so it becomes necessary to preface the code with
root.update_idletasks()
Here's a good excerpt from the documentation that I found on Billal BEGUERADJ's response on an overrideredirect() question.
If you want to force the display to be updated before the application next idles, call the w.update_idletasks() method on any widget.
Some tasks in updating the display, such as resizing and redrawing widgets, are called idle tasks because they are usually deferred until the application has finished handling events and has gone back to the main loop to wait for new events.
If you want to force the display to be updated before the application next idles, call the w.update_idletasks() method on any widget.
While I still don't understand why exactly this widget breaks without update_idletasks() on OSX, I do understand now why it's good practice to use update_idletasks() in conjunction with overrideredirect() to ensure consistent behavior.
Hope this helps anyone else who may get hung up on this.

Related

How to access event data?

Click handlers in bokeh 1.0.3 used to have the signature attr, old, new. Now the are passed a single event object. How can I access its values?
menu = [('a', 'a'), ('b', 'b')]
dropdown = Dropdown(label='clickme', menu=menu)
def click_handler(event):
print(event)
returns
bokeh.events.MenuItemClick object at 0x7ff7de1cc208
EDIT: Where in the documentation is access to values of events described? I could not find anything on https://docs.bokeh.org/en/latest/docs/reference/events.html
I'm not sure where you got this information, but it's not quite correct. Dropdown was refined as a type of Button recently, so the ability to respond to the same kind of click events that other buttons do was added. But nothing was replaced. Callbacks for property changes work for any Bokeh object property, including Dropdown.value, and this has not changed:
from bokeh.io import curdoc
from bokeh.models import Dropdown
menu = [('a', 'a'), ('b', 'b')]
dropdown = Dropdown(label='clickme', menu=menu)
def cb(attr, old, new):
print(attr, old, new)
dropdown.on_change('value', cb)
curdoc().add_root(dropdown)
I'm coming in late, but I had the same issue as Leevi, and bigreddot's answer did not work for me (Bokeh 2.4.2, Python 3.10.2). Short answer is that a Bokeh Dropdown does not have a value attribute, so on_change('value, handler) will not work. The MenuItemClick object, has an item attribute, which is what you want.
menu = [('a', 'a'), ('b', 'b')]
dropdown = Dropdown(label='clickme', menu=menu)
def click_handler(event):
print(event.item)
dropdown.on_click(click_handler)
Long answer: I had the following code:
def choose_pipeline(event):
"""Change to the chosen pipeline"""
print(event)
pipeline_dropdown = Dropdown(label='Available Pipelines',
menu=[("Pipeline1", "pipeline1_value"),
("Pipeline2", "pipeline2_value")])
pipeline_dropdown.on_change('value', choose_pipeline)
and got this error:
File "C:\Users\me\PycharmProjects\DHLLDV\Scripts\SystemTab.py", line 69, in system_panel
pipeline_dropdown.on_change('value', choose_pipeline)
File "C:\Users\me\PycharmProjects\DHLLDV\venv\lib\site-packages\bokeh\model\model.py", line 434, in on_change
descriptor = self.lookup(attr)
File "C:\Users\me\PycharmProjects\DHLLDV\venv\lib\site-packages\bokeh\core\has_props.py", line 469, in lookup
raise AttributeError(f"{cls.__name__}.{name} property descriptor does not exist")
AttributeError: Dropdown.value property descriptor does not exist
I wrote this:
def choose_pipeline(event):
"""Change to the chosen pipeline"""
print(event.__dict__)
pipeline_dropdown = Dropdown(label='Available Pipelines', menu=[("Pipeline1", "pipeline1_value"),
("Pipeline2", "pipeline2_value")])
pipeline_dropdown.on_click(choose_pipeline)
And saw the result when I picked Pipeline2:
{'item': 'pipeline2_value', '_model_id': '1365'}

Best approach to show / hide dialogs

I have encountered a theoretical question. I'm using pyqt5, but this is probably are very generalistic and framework independent question.
I have a QMainwindow sitting around waiting for the user to do stuff. The user can show / hide dialogues (subclasses of QDockwidgets) as he chooses using the QMenu and the associated shortcuts (it's a checkable QAction for each individual dialogue).
I have been struggling with showing / hiding the dialogues efficiently. Currently, I'm just initiating them all at start up, hiding those that I don't want to show up in the beginning. This makes triggering the dialogues easy, since I can just dialogue.show() /dialogue.hide() depending on the dialogues current visibility.
But I cannot believe that this is best practice and very efficient.
I have tried (I currently do not have my pyqt environment set up on this computer, so I had to strip down my actual code without being able to test if this runs):
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class InfoPanel(QDockWidget):
def __init__(self, title='Tool Box'):
QDockWidget.__init__(self, title)
self.setFeatures(QDockWidget.DockWidgetFloatable | QDockWidget.DockWidgetMovable | QDockWidget.DockWidgetClosable)
self.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea)
frame = QFrame()
layout = QGridLayout()
self.canvas = QGraphicsView()
self.canvas.setBackgroundBrush(QtGui.QBrush(QtGui.QColor(40, 40, 40)))
layout.addWidget(self.canvas)
frame.setLayout(layout)
self.setWidget(frame)
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.showpanelAct = QAction("&Show Panel", self, enabled=True,checkable=True, shortcut="F10")
self.showpanelAct.triggered.connect(lambda: self.showPanel(0))
self.viewMenu = QMenu("&View", self)
self.viewMenu.addAction(self.showpanelAct)
self.setDockOptions(QMainWindow.AnimatedDocks)
def showPanel(self,i:int = 0): # this is not so smart - should construct and deconstuct to save memory!?
if i == 0: #infopanel
dialogueExists = True
try: self.infoPanel
#except NameError: #does not catch the error
except:
dialogueExists = False
if dialogueExists:
print('destroy')
self.infoPanel.destroy()
else:
print('create')
self.infoPanel = InfoPanel() #init
self.infoPanel.show()
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()
Which works the first time, but after that, it only seems to trigger the destruction of the dialogue (which, surprisingly, does not crash anything it just keeps on going).
Why is that and is there a standard way to approach the showing hiding of dialogues?
I took the exposed MCVE of OP and tried to make it running in my cygwin64 on Windows 10.
At first I had to apply little fixes. (OP stated that he was not able to test it at the time of publishing.)
First, I inserted a “hut” at first line for convenient start in bash:
#!/usr/bin/python3
Second, the self.viewMenu didn't appear. Hence, I inserted a line after
self.viewMenu = QMenu("&View", self)
self.viewMenu.addAction(self.showpanelAct)
to add the viewMenu to main menu bar:
self.menuBar().addMenu(self.viewMenu)
which fixed it.
Third, when clicking the menu item I got:
Traceback (most recent call last):
File "./testQDockPanelShowHide.py", line 27, in <lambda>
self.showpanelAct.triggered.connect(lambda: self.showPanel(0))
File "./testQDockPanelShowHide.py", line 45, in showPanel
self.infoPanel = InfoPanel() #init
File "./testQDockPanelShowHide.py", line 17, in __init__
self.canvas.setBackgroundBrush(QtGui.QBrush(QtGui.QColor(40, 40, 40)))
NameError: name 'QtGui' is not defined
Aborted (core dumped)
I must admit that my Python knowledge is very limited. (I'm the guy who writes the Python bindings in C++ for the colleagues. So, my colleagues are the actual experts. At most, I play a little bit in Python when I test whether new implemented bindings do what's expected.) However, I modified
self.canvas.setBackgroundBrush(QtGui.QBrush(QtGui.QColor(40, 40, 40)))
to:
self.canvas.setBackgroundBrush(QBrush(QColor(40, 40, 40)))
which fixed this issue.
After this, I got the behavior described by OP and did a closer look where I (and OP) suspected the error:
def showPanel(self,i:int = 0): # this is not so smart - should construct and deconstuct to save memory!?
if i == 0: #infopanel
dialogueExists = True
try: self.infoPanel
#except NameError: #does not catch the error
except:
dialogueExists = False
if dialogueExists:
print('destroy')
self.infoPanel.destroy()
else:
print('create')
self.infoPanel = InfoPanel() #init
self.infoPanel.show()
I strongly believe that try: self.infoPanel doesn't do what OP thinks it would.
It tries to access self.infoPanel which isn't existing until the first call of this method. (Please, be aware, the member variable self.infoPanel isn't existing.) So, the except: branch is executed and sets dialogueExists = False which a few lines later causes self.infoPanel = InfoPanel() #init. Now, the member variable self.infoPanel is existing, and the try: self.infoPanel will never fail again until destruction of this MainWindow.
Out of curiosity, I had a look at QWidget.destroy() (to be sure not to tell something wrong):
QWidget.destroy (self, bool destroyWindow = True, bool destroySubWindows = True)
Frees up window system resources. Destroys the widget window if destroyWindow is true.
destroy() calls itself recursively for all the child widgets, passing destroySubWindows for the destroyWindow parameter. To have more control over destruction of subwidgets, destroy subwidgets selectively first.
This function is usually called from the QWidget destructor.
It definitely doesn't destroy the member variable self.infoPanel.
After having understood this, a fix was easy and obvious:
def showPanel(self,i:int = 0): # this is not so smart - should construct and deconstuct to save memory!?
if i == 0: #infopanel
try: self.infoPanel
#except NameError: #does not catch the error
except:
print('create')
self.infoPanel = InfoPanel() #init
if self.infoPanel.isVisible():
self.infoPanel.hide()
else:
self.infoPanel.show()
Btw. I replaced destroy() by hide() which makes a re-creation of the InfoPanel() obsolete.
I tested this by toggling the menu item multiple times – it works as expected now (at least, it looks like).
The complete sample finally:
#!/usr/bin/python3
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class InfoPanel(QDockWidget):
def __init__(self, title='Tool Box'):
QDockWidget.__init__(self, title)
self.setFeatures(QDockWidget.DockWidgetFloatable | QDockWidget.DockWidgetMovable | QDockWidget.DockWidgetClosable)
self.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea)
frame = QFrame()
layout = QGridLayout()
self.canvas = QGraphicsView()
# self.canvas.setBackgroundBrush(QtGui.QBrush(QtGui.QColor(40, 40, 40)))
self.canvas.setBackgroundBrush(QBrush(QColor(40, 40, 40)))
layout.addWidget(self.canvas)
frame.setLayout(layout)
self.setWidget(frame)
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.showpanelAct = QAction("&Show Panel", self, enabled=True,checkable=True, shortcut="F10")
self.showpanelAct.triggered.connect(lambda: self.showPanel(0))
self.viewMenu = QMenu("&View", self)
self.viewMenu.addAction(self.showpanelAct)
self.menuBar().addMenu(self.viewMenu)
self.setDockOptions(QMainWindow.AnimatedDocks)
def showPanel(self,i:int = 0): # this is not so smart - should construct and deconstuct to save memory!?
if i == 0: #infopanel
try: self.infoPanel
#except NameError: #does not catch the error
except:
print('create')
self.infoPanel = InfoPanel() #init
if self.infoPanel.isVisible():
self.infoPanel.hide()
else:
self.infoPanel.show()
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()
After taking a break from coding the solution to my problem was very obvious. Going back to my original code (which did not produce the expected output of creating / destroying the dialogue self.infoPanel on demand):
dialogueExists = True
try: self.infoPanel
#except NameError: #does not catch the error
except:
dialogueExists = False
if dialogueExists:
print('destroy')
self.infoPanel.destroy()
else:
print('create')
self.infoPanel = InfoPanel() #init
self.infoPanel.show()
My main problem was that I confused two separate things. Qt destroyed the widget contained in the object self.infoPanel when I called self.infoPanel.destroy(). But that doesn't mean the object self.infoPanel does not exist (that's exactly what I use try: ... for, to see if the object exists). The simple and obvious way to create and destroy dialogues on demand obviously involves deleting the object from the environment (del self.infoPanel).
The working code is:
dialogueExists = True
try:
self.infoPanel.destroy() #not sure this is needed, but I guess it doesn't hurt
del self.infoPanel #this is the deletion of the actual object
except:
dialogueExists = False
if not dialogueExists :
self.infoPanel = InfoPanel()
Cheers and many thanks for the helpful advice on deciding whether to show / hide dialogues or to create / destroy them!

Is there a way to make an IPython Notebook output interactivly create an input and execute it?

I was wondering if I can make an output interactively run a piece of code. So if for example I had a class (parts in pseudo-code):
import numpy as np
class test(object):
def __init__():
self.a = np.random.randn(10)
print ## Interactive Output: Click me to view data array##
def show():
print a
So when I create a class instance it should output some interactive link (maybe in html) or something like that and when I click it, the show() method should be called. However, I have no idea how to achieve that.
You could use the widgets shipped with the notebook (for jupyter they are an independent package).
Something like this could do what you want (Python 3):
from IPython.html import widgets
from IPython.display import display
import numpy as np
class Test(object):
def __init__(self, arraylen):
self.a = np.random.randn(arraylen)
self.button = widgets.Button(description = 'Show')
self.button.on_click(self.show)
display(self.button)
def show(self, ev = None):
display(self.a)
self.button.disabled = True
test = Test(10)
You create a button widget when you initialise the class widgets.Button(description = 'Show')
Attach an event to it button.on_click(self.show)
And display the button display(self.button)
In the show method I included a way to disable the button functionality once the array is showed self.button.disabled = True. You can comment this line if you want to show more times the array.

How to set value of input(type="file") with QWebElement?

I'm trying to upload a photo to vk.com using QtWebKit module. The problem I'm facing is inability to properly fill input(type="file")'s value. Here's some related code I use:
def upload():
print 'uploading...'
photoInput = web.page().mainFrame().documentElement().findFirst('input[id="photos_upload_input"]')
assert photoInput, 'No input found'
photoInput.setAttribute('value', '/Users/elmigranto/Downloads/stuff.png')
print photoInput.evaluateJavaScript('return this.value;').toString()
It's reasonable to note, that filling value of file input is impossible from Javascript due to browser security policy. However, it should be possible using Qt API, more specifically, QWebElement::setAttribute() method. And that's what I did… with no effect (well, photoInput.attribute('value') returns expected result, but photoInput.evaluateJavaScript('return this.value;').toString() returns empty string, input's onchange handler is also not triggered).
Setting other attributes is no problem, for example, QWebElement::addClass() works like a charm.
Any help would be super great.Thanks.
The setAttribute method might still not work for security reasons.
But you can redefine the function QWebPage::chooseFile that should normally open the upload dialog and return the filename so that it returns a static file name without opening the dialog, and activate that upload by simulating a "return" key press on the input element.
This seems to work:
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from PyQt4.QtWebKit import *
import sys
class WebPage(QWebPage):
def __init__(self, parent = None):
super(WebPage, self).__init__(parent)
self.overrideUpload = None
def chooseFile(self, originatingFrame, oldFile):
if self.overrideUpload is None:
return super(WebPage, self).chooseFile(originatingFrame, oldFile)
result = self.overrideUpload
self.overrideUpload = None
return result
def setUploadFile(self, selector, filename):
button = self.mainFrame().documentElement().findFirst(selector)
self.overrideUpload = filename
# set the focus on the input element
button.setFocus();
# and simulate a keypress event to make it call our chooseFile method
webview.event(QKeyEvent(QEvent.KeyPress, Qt.Key_Enter, Qt.NoModifier))
def upload():
print 'uploading...'
page.setUploadFile('input[id="photos_upload_input"]',
'/Users/elmigranto/Downloads/stuff.png')
# The change seems to be asynchronous, at it isn't visible
# just after the previous call
app = QApplication(sys.argv)
webview = QWebView()
page = WebPage(webview)
webview.setPage(page)
source = '''
<form action="#">
Select a file: <input type="file" id="photos_upload_input">
<input type="submit">
</form>
'''
webview.loadFinished.connect(upload)
webview.show()
webview.setHtml(source)
sys.exit(app.exec_())

Regd: WebDriver on secure Certificate

I have been using WebDriver for past 6 months.
There are couple of issues am facing right now [Version 2.3.1]:
a) when i try to get the element for the override link on the security certificate [https] page in IE through webdriver findElement, its not able to find that element but the selenium RC works fine.
Then i got a fix for tht by using:
webDriver.navigate().to(javascript:document.getElementById('overridelink').click());
Note :
I tried using the below code to fetch the element on security certificate page , but it returns
the body element
WebElement activeElement() [WebElement with focus, or the body element if no element with focus can be detected.], why its not able to pick the element by using findelement ?
b) i connected the remote network through SSL for running the webdriver test, am not able to click the override link on secure certificate [https] page?
c) is it better approach implementing webdriver [currently am using this] directly instead of using any framework like jbehave ?
Please provide your suggestions
Thanks,
Jayaraj A
Thank you for workaround!
For Java, your solution will look just a bit different and it helped me:
//driver is initialised somewhere before, for example, as RemoteWebDriver
driver.navigate().to("javascript:document.getElementById('overridelink').click()");
Yeah, I had similar problems. Webdriver doesn't seem to have complete information on
the Certificate error page for some reason.
I'm on Windows XP SP3, running IE 7 with Python/Webdriver
I'm using this hack to get around the certificate error page:
(Help, I still can't get freeeking Markdown to format a code block...)
#!/c/Python27/python
import win32con
import win32gui
def certificate_continue():
"""
Find the IE Window that has a Certificate Error and try to continue anyway.
We'll use the win32 modules to find the right window & child window,
then write some Javascript into the address bar and execute to continue.
"""
def _enumWindowsCallback(hwnd, windows):
"""
Cannibalized from Gigi Sayfan (WindowMover)
http://www.devx.com/opensource/Article/37773/1954
This appends window information as a 3-tuple to the list
passed into win32gui.EnumWindows()
"""
class_name = win32gui.GetClassName(hwnd)
# apparently win32gui.GetWindowText() only works to get the text
# on a button or a label not really for edit windows.
text = win32gui.GetWindowText(hwnd)
windows.append((hwnd, class_name, text))
def _get_certificate_error_window():
"""
all_windows[] gets filled up with a list of tuples, then loop through
it filtering on class and the window text (title bar text).
Assumes only one 'Certificate Error' window.
"""
all_windows = []
win32gui.EnumWindows(_enumWindowsCallback, all_windows)
for win in all_windows:
class_name = win[1]
title_bar_text = win[2]
if class_name == 'IEFrame' and \
'Certificate Error: Navigation Blocked' in title_bar_text:
return win
def _get_edit_text(hwnd):
"""
This function courtesy of Omar Raviv with huge help from Simon Brunning.
http://www.brunningonline.net/simon/blog/archives/000664.html
"""
buf_size = win32gui.SendMessage(hwnd, win32con.WM_GETTEXTLENGTH, 0, 0)
buf_size += 1 # don't forget that null character boys...
buffer = win32gui.PyMakeBuffer(buf_size)
# odd, we're telling them how big the text is that they're giving
# back to us
win32gui.SendMessage(hwnd, win32con.WM_GETTEXT, buf_size, buffer)
# don't need the null character now for Python
return buffer[:buf_size]
def _get_address_bar(parent_handle):
"""
There appears to be several 'Edit' windows within each browser window.
From Microsoft: If a child window has created child windows of its own,
EnumChildWindows enumerates those windows as well.
"""
childwins = []
win32gui.EnumChildWindows(parent_handle, _enumWindowsCallback,
childwins)
for win in childwins:
child_handle = win[0]
class_name = win[1]
if 'Edit' in class_name:
edit_text = _get_edit_text(child_handle)
if 'http://' in edit_text or 'https://' in edit_text:
return child_handle # then this must be it...
# begin certificate_continue
target_win = _get_certificate_error_window()
try:
cert_err_handle = target_win[0]
except TypeError:
print "OK, no Certificate Error window available"
return(1)
address_bar_handle = _get_address_bar(cert_err_handle)
# any better way to check the handle ?
if not win32gui.IsWindow( address_bar_handle):
print "Choked getting IE edit window"
return(1)
# now, need to send this JavaScript text to the browser Address Bar
javascript_continue = 'javascript: var continue_element = document.getElementById("overridelink"); continue_element.click();'
win32gui.SendMessage(address_bar_handle, win32con.WM_SETTEXT, 0,
javascript_continue)
# OK, and finally, send a carriage return to the address bar
# This last abomination, courtesy of Claudiu
# http://stackoverflow.com/#questions/5080777/
# what-sendmessage-to-use-to-send-keys-directly-to-another-window
win32gui.SendMessage(address_bar_handle, win32con.WM_KEYDOWN,
win32con.VK_RETURN, 0)
return(0)
if __name__ == '__main__':
status = certificate_continue()
exit(status)

Resources