So I'm relatively new at Python, and I've been trying to learn PyQt. I wanted to create a menu dynamically based on the contents of a list. I found an example which I adapted and it looked like this:
for someText in myList:
entry = QAction(someText,MainWindow)
self.myMenu.addAction(entry)
entry.triggered.connect(lambda menuItem=someText: self.doStuff(menuItem))
entry.setText(someText)
The menu was created but when a menu item was chosen doStuff() always got passed a value of False. So I changed the above to:
for someText in myList:
entry = QAction(someText,MainWindow)
self.myMenu.addAction(entry)
entry.triggered.connect(lambda bVal, menuItem=someText: self.doStuff(bVal,menuItem))
entry.setText(someText)
and sure enough everything now works as I'd like. I still get the False in bVal which I just ignore.
I've tried looking at the PyQt documentation but the reference section links to the C++ documentation and it's not obvious to me from that what's going on.
I'd like to understand what the boolean value is and why, in my case, it's always False. I've tried changing various things but I haven't managed to find a scenario where it's True.
Thanks
PyQT5.4, Python 3.4.2 on Windows.
The C++ documentation for the triggered signal shouldn't be too hard to understand:
void QAction::triggered(bool checked = false)
...
If the action is checkable, checked is true if the action is checked, or false if the action is unchecked.
So the signal is emitted with a boolean parameter which indicates the action's 'checked' state, and this parameter overwrote the default value for your menuItem argument.
Related
Sorry for the "I found this code, (how) does it work?" question but I promise it has content.
I came across the following:
button = driver.find_by_css_selector("some selector")
assert button is not None
First of all, I would state that the assert is not even necessary. if the button is not found, it will throw an uncaught exception, and the rest of the code doesn't matter.
for assert button is not None, how could is not None be helpful? Obviously if the button is none it will fail the assertion anyways... I would say we are better off with assert button so we at least check for a truthy value here. That is provided I am wrong that the whole assert is even necessary.
Am I going wrong here? Please let me know if I am misunderstanding anything from Python/Selenium.
I would say we are better off with assert button so we at least check for a truthy value here.
When it comes to checking if a value if None, using is not None is actually more efficient than the other.
From if A vs if A is not None:, the statement if button: will call button.__nonzero__(), whereas, if button is not None: compares only the reference button with None to see whether it is the same or not.
Your question heading :
If we set a variable to “find an element”, what are all the possible
return values?
think about this as a positive and negative returns, that is :-
if found, A web element will be returned.
if Not found, there can be multiple exception raised such as NoSuchElement, etc.
Now coming to assert part. First question that comes in my mind is, What is assert and Why do we need assert in automation, I believe you've the same thinking here.
An assert is there to help you, by alerting you to errors that must
never occur in the first place, that must be fixed before the product
can be shipped. Errors that do not depend on user input, but on your
code doing what it is supposed to do.
reference of this can be found here
additionally they can be good
Checks if a particular block of code is reachable or not.
Checks if the assumptions made by the developer are correct.
They can also check the state of an object.
and may be more..
Now, coming to actual question :-
assert button is not None, how could is not None be helpful?
May be it does not look helpful in this context.
But think, about a situation where a customer expect a table visibility, when they click on this button. and in production environment, due to some glitch it's not loading or showing any content, with the help of assertion you can print or show a message to the customer, or can handle an uncaught exception. I know they are other ways as well, but for me personally in automation if I have assertion at place, and if the condition is met or does not meet, I could see the exact execution status in my test report. So it's beneficial for debugging as well.
I have a spinbox that has a values ranges from 0-366, it means it only allowed a numeric data type and a backspace. Whenever a user type a character, it automatically deleted if it not a number type. I'm from a C# background and this is my first attempt in Python language. Here is my code.
def validate(event):
charPress=event.keysym
val=sbDays.get() #previous values
if not charPress.isdigit():
sbDays.config(textvariable=StringVar(windows).set(val))
sbDays=tk.Spinbox(frame,from_=0,to=366,borderwidth=sbBorderWidth)
sbDays.place(relx=initialX,rely=yDistance,relwidth=sbWidth)
sbDays.config(validate='all',validatecommand=(windows.register(validate),'% P'))
sbDays.update()
sbDays.bind('<Key>',validate)
From the code above, when I run it, it returns nothing. Since I'm from C# background. This is what I actually need. This is the C# keypress event
public static void TextBox_KeyPress_NumberBackspace(object sender, KeyPressEventArgs e)
{
char keyChar = e.KeyChar;
if (char.IsNumber(keyChar) || char.IsControl(keyChar))
e.Handled = false;
else
e.Handled = true;
}
tbDays.KeyPress += TextBox_KeyPress_NumberBackspace;
You're not using the validatecommand attribute properly. The function should never directly change the value. The job of the validatecommand function is to return either True or False. If it returns True the input will be allowed, otherwise the input will be rejected.
If it returns anything else besides True or False (including None) or if the function attempts to change the value, the validation function will be disabled.
You can configure tkinter to send the information you need to determine whether the data is valid or not. You should not be relying on the value returned by get(), since that will only return the previous value rather than the value being input from the user.
Since you're only wanting to allow integers, and you want to limit the value to a max of 366, the best solution would be to have tkinter pass in the value if the edit is allowed (using %P), which you can then use to determine whether or not it is valid.
You also should not bind to the <Key> event, it's unnecessary for the validation to function, and in fact would get in the way of it working.
The function would look something like this:
def validate(new_value):
if new_value == "":
# allow blank entry so user doesn't get frustrated
# when trying to delete the first character
return True
try:
value = int(float(new_value))
except ValueError:
return False
return value >= 0 and value <= 366
You should configure the validatecommand like the following (notice there's no space between % and P):
sbDays.configure(validate='all',validatecommand=(root.register(validate), '%P'))
You will probably need to add a little extra code elsewhere to handle the edge case where the user deletes everything in the widget and doesn't enter anything else. For example, you could use something like value=sbDays.get() or 0.
I am trying to set the options in the following call:
bool QTextEdit::find(const QString &exp, QTextDocument::FindFlags options = QTextDocument::FindFlags())
But the signature of option is complicated for a Python programmer. I tried the following:
option = 0
option = option | QTextDocument.FindBackward
# continue to check other checkboxes and build up the option this way
Unfortunately, the error is that 'int' is unexpected. I understand that since the option=0, then the following OR operation probably didn't yield an int type as well. But how to get a the proper starting null/unset/zero value?
If you would have default value let this parameter unfilled:
doc = QTextDocument()
doc.find("aaa")
If you would like to use flag, do not read value from documentation, but use
QTextDocument.FindBackward
QTextDocument.FindCaseSensitively
QTextDocument.FindWholeWords
If you would like to have or use | operator:
QTextDocument.FindWholeWords | QTextDocument.FindBackward
If you have default value in function signature, you not need to provide this argument.
The error is caused by a minor bug that occasionally appears in PyQt. If you update to the latest version, the error will probably go away. However, if you can't update, or if you want to bullet-proof your code against this problem, a work-around is to initialise the variable like this:
>>> option = QTextDocument.FindFlag(0)
>>> option = option | QTextDocument.FindBackward
This will now guarantee that option has the expected type. The correct flag to use can be found by explicitly checking the type of one of the enum values:
>>> print(type(QTextDocument.FindBackward))
<class 'PyQt5.QtGui.QTextDocument.FindFlag'>
Or you can just look up the relevant enum in the docs: QTextDocument.
I am using Office 2007.
I found if I would like to show the legend overlapping the chart in office2007.
The XML should be as the following.
`-<c:legend>
<c:overlay val="1"/>`
But no matter I use the API from python-pptx 'chart.legend.include_in_layout = True' or I leave it as the default. The generated XML would always be as the following.
`-<c:legend>
<c:overlay/>`
Without the val=1, then office2007 won't show the format properly.
What can I do to force the python-pptx to write the val=1? thanks.
Explanation
In short, the True value is not explicitly set (in contrast to False) because True corresponds to the default value of overlay's val attribute.
To explain it in more detail - you can follow the python-pptx hierarchy as follows: overlay is mapped to CT_Boolean (all overlay oxml elements are instantiated from CT_Boolean). The actual val parameter is then mapped via OptionalAttribute and is defined with the default value of True:
class CT_Boolean(BaseOxmlElement):
"""
Common complex type used for elements having a True/False value.
"""
val = OptionalAttribute('val', XsdBoolean, default=True)
Now, when setting the optional attribute to its default value, it is actually skipped/deleted, as you can see here if value == self._default:
class OptionalAttribute(BaseAttribute):
"""
Defines an optional attribute on a custom element class. An optional
attribute returns a default value when not present for reading. When
assigned |None|, the attribute is removed.
"""
#property
def _setter(self):
def set_attr_value(obj, value):
if value == self._default:
if self._clark_name in obj.attrib:
del obj.attrib[self._clark_name]
return
str_value = self._simple_type.to_xml(value)
obj.set(self._clark_name, str_value)
return set_attr_value
Fix - provide custom CT_Boolean class
Add these lines somewhere before you need to use the overlay. It will overwrite python-pptx overlay mapping with the custom CT_Boolean_NoDefault class:
from pptx.oxml import register_element_cls
from pptx.oxml.xmlchemy import BaseOxmlElement, OptionalAttribute
from pptx.oxml.simpletypes import XsdBoolean
class CT_Boolean_NoDefault(BaseOxmlElement):
"""
Common complex type used for elements having a True/False value with no
default value.
"""
val = OptionalAttribute('val', XsdBoolean)
register_element_cls('c:overlay', CT_Boolean_NoDefault)
This worked for me and finally I got:
<c:legend>
<c:overlay val="1"/>
</c:legend>
Fix - modify python-pptx permanently
This is not recommended but you might want to modify python-pptx instead of adding the solution from above for each script you run.
First, add the following to pptx/oxml/chart/shared.py which defines a new bool class without a default value:
class CT_Boolean_NoDefault(BaseOxmlElement):
"""
Common complex type used for elements having a True/False value.
"""
val = OptionalAttribute('val', XsdBoolean)
Second, modify pptx/oxml/__init__.py to add the new bool class:
from .chart.shared import (
CT_Boolean, CT_Double, CT_Layout, CT_LayoutMode, CT_ManualLayout,
CT_NumFmt, CT_Tx, CT_UnsignedInt, CT_Boolean_NoDefault
)
Third, modify pptx/oxml/__init__.py to change the mapping of the overlay element to the new bool class:
register_element_cls('c:overlay', CT_Boolean_NoDefault)
Better solution
In case you have time, please submit a ticket here so it might become a permanent fix. In case #scanny finds some time, he will read this. Perhaps there is some better solution for this, too, and I've completely missed something.
#pansen 's analysis is spot-on. Here's an alternative way to get this working in your case that might be a little lighter weight:
def include_in_layout(legend):
legend_element = legend._element
overlay = legend_element.get_or_add_overlay()
overlay.set('val', '1')
This appears to be a localized non-conformance of that version of PowerPoint with the ISO/IEC 29500 spec. As pansen rightly points out, a missing val attribute is to be interpreted the same as val=1 (True). I'd be interested to discover how extensive this non-conformance goes, i.e. what other elements exhibit this same behavior. The CT_Boolean type is used quite frequently in PowerPoint, for things like bold, italic, varyColors, smooth, and on and on. So a "compensating" fix would need to be applied carefully to avoid reporting incorrect results for other elements.
I think I'll take pansen's cue and use a specialized element class for this element only. It will still report True for an element without the val attribute, which will be inconsistent with the observed behavior on this version of PowerPoint; but assuming other versions behave correctly (according to the spec), the inconsistency will be localized and at least assigning True to that property will make the legend show up the way you want.
I'm working on a dynamic Traits UI where I can select the class to use for certain instances. I've got it working nicely using an InstanceEditor with a "values" argument containing InstanceFactoryChoice instances.
My problem appears when I want to specify a view to use for the selected instance. Using the "view" argument works if I omit the "values" argument, but with it I get the default view instead. The relevant part of the view looks like this:
Item('item',
show_label=False,
editor=InstanceEditor(
view=item_view,
values=[InstanceFactoryChoice(klass=k) for k in classes],
editable=True),
style='custom')
What's more confusing to me is that it also works as expected (i.e. uses the "item_view" view to display the instance) when I use the "simple" style instead of "custom". However, then the view appears in a new window, I want it to be inline.
Am I missing something here? I'm on TraitsUI 4.3.
OK, after some source-diving I found that adding the "view" argument to the InstanceFactoryChoice call instead seems to do what I want. Still, it seems there's an inconsistency in there somewhere...
InstanceFactoryChoice(klass=k, view=item_view)