How to react on a click in PyQt4 QTreeWidget - python

I am new to Pyhthon and Qt, hence this basic question. What I want is that when I click an item in a QTreeWidget an event handler is called which tells me which item has been clicked. The code I tried is:
self.dir_tree = QTreeWidget ()
self.dir_tree.setColumnCount (3)
self.dir_tree.setHeaderLabels (("File", "Type", "Size"))
self.dir_tree.connect (dir_tree, SIGNAL ("itemClicked (QTreeWidgetItem*, int)"), self.onClickItem)
def onClickItem (self, column):
print (column)
This does not run, the error code is:
TypeError: arguments did not match any overloaded call:
QObject.connect(QObject, SIGNAL(), QObject, SLOT(),Qt.ConnectionType=Qt.AutoConnection): argument 1 has unexpected type 'function'
QObject.connect(QObject, SIGNAL(), callable, Qt.ConnectionType=Qt.AutoConnection): argument 1 has unexpected type 'function'
QObject.connect(QObject, SIGNAL(), SLOT(), Qt.ConnectionType=Qt.AutoConnection): argument 1 has unexpected type 'function'
What am I doing wrong? And a question related to this: how can I figure out which item was clicked?
I could not find a tutorial for this, any suggestion is welcome.
Thanks for any help.

Question asked too early, I found the answer after a lot of experimenting. The code was wrong in not mentioning self.dirtree and connecting from self.dir_tree instead of self. So the correct code should be:
self.connect (self.dir_tree, SIGNAL ("itemClicked(QTreeWidgetItem*, int)"), self.onClickItem)
And some experimenting led to the following callback:
def onClickItem (self, item, column):
print (item, column)
Item refers to the clicked QTreeWidgetItem itself (in my case it is a derived class with extra information: still works fine) and column to the clicked column.
The last question still stands. I have still not a good grasp of signals/slots. Any pointer to a good tutorial is welcome.

Related

PyQt5 dbus: Force type signature of signal argument to be array of string

I'm writing an MPRIS player, which communicates with clients over
dbus. I need to emit a signal when my playback state changes. However,
the signal requires a format of (sa{sv}as), and my code is producing
(sa{sv}av). Here's the important part:
self.signal = QDBusMessage.createSignal(
"/org/mpris/MediaPlayer2",
"org.freedesktop.DBus.Properties",
"PropertiesChanged"
)
self.signal.setArguments(
[interface, {property: values}, ['']]
)
The problem is the third item in the list given to setArguments. It is
an empty string in a list because I need to produce a type of 'array
of string' (as) but pyqt5 translates that into 'array of variant' (av).
I never need to put any actual data in that list, I just need the type
signature to be correct.
Is there any way to do this in PyQt5? Perhaps using QDBusArgument?
I... I got it working. Wow. That was a campaign.
I don't know what is going wrong exactly, I wasn't able to dig out of the PyQt5 source where exactly the conversion happens. However, I did look into QDbusArgument(). There is no documentation for this in python, and the C++ docs are worthless due to major differences, so I took to the source code. in sip/QtDbus/qdbusargument.sip, I discovered a completely undocumented new method called qdbusargument_add. This maps to QDbusArgument().add() in python. It is used to add arguments with an explicit type id to a QDbusArgument. And it has a special case for QStringList!
From then I just bruteforced every possibility I could think of with arguments to QDbusArgument(), and finally got the following:
def PropertiesChanged(self, interface, property, values):
"""Sends PropertiesChanged signal through sessionBus.
Args:
interface: interface name
property: property name
values: current property value(s)
"""
emptyStringListArg = QDBusArgument()
emptyStringListArg.add([""], QMetaType.QStringList)
self.signal.setArguments([interface, {property: values}, emptyStringListArg])
QDBusConnection.sessionBus().send(self.signal)
It'd be great if the add() function could be documented, but I can't seem to send messages to the PyQt5 mailing list. Do I have to register first?

addAttr method on pymel objects

I can't seem to utilize the addAttr method correctly. I'm using the same arguments as when I call from pymel.core but it's not giving me the same results.
I'm trying to add a custom message attribute so I can easily search for certain types of objects later. When I do it from pymel.core and include the same object reference as an argument, it works fine.
#get object reference
test_object = pm.ls(sl=1)[0]
#this one spits out an error
test_object.addAttr(longName = 'custom', attributeType = 'message')
#this one works fine
pm.addAttr(test_object, longName = 'custom', attributeType = 'message')
I keep getting this error
Error: TypeError: file line 1: addAttr() takes exactly 2 arguments (1 given)
What additional argument is it looking for when I use it this way? I am clearly missing something obvious about how methods work but I can't figure it out.
The addAttr method exposed for DG nodes in Maya PyMel has following signature.
addAttr(attr, **kwargs)
Here attr is an positional argument representing the attribute name. The kwargs can be supplied with all other relevant flags used in pm.addAttr() method. So you have to pass the attribute name as first argument.
node.addAttr('custom', attributeType='message')
Hope this will help.
from cgsociety thread
pCube.addAttr('timeBasedAttr', keyable=True, attributeType='float', min=0.0, max=1.0)
you should write :
test_object.addAttr('custom', attributeType = 'message')
Ive tried and it doesn't output error.

Tkinter button linked to function with argument gives TypeError

I'm trying to run a function called password() with argument "t" on pressing a button in Tkinter.
Problem is that this gives me a TypeError even though the code seems alright.
self.Button5.configure(command=lambda t='restart': password(t))
Error:
TypeError: 'str' object is not callable
(here is a link to whole code if necessary, error is on line 372: https://codeshare.io/G8VW6A)
In line 103 of your code you create global variable 'password', which has the same name as your function. Just change the variable names so they don't clash.

PyQt5 create error upon failed dbus request

Using PyQt5's D-Bus, how do I create an error? For example, if someone sends an unknown command in a chat program, I'd like to be able to let the client know something like as follows:
error = QDBusMessage.createError(QDBusError.UnknownProperty, "unknown command")
QDBusConnection.systemBus().send(error)
However, this fails with the message:
*** TypeError: arguments did not match any overloaded call:
createErrorReply(self, str, str): first argument of unbound method must have type 'QDBusMessage'
createErrorReply(self, QDBusError): first argument of unbound method must have type 'QDBusMessage'
createErrorReply(self, QDBusError.ErrorType, str): first argument of unbound method must have type 'QDBusMessage'
I can't make heads no tail of this error, since as far as I can tell I'm using it exactly as described. The first arg must be QDBusMessage, since that's what's before the dot. The second arg is of the right type, being <class 'PyQt5.QtDBus.QDBusError.ErrorType'> as returned by type(QDBusError.UnknownProperty). And the quotes mean it's a string, which is what str is.
I also tried sendErrorReply(), but that doesn't seem to exist in PyQt5. At least, I can't find it - it's not beside systemBus's send(), and it's not found in QDBusMessage.
Neither of the examples in PyQt5's examples/dbus/chat folder emit errors. There is no Python documentation available at http://pyqt.sourceforge.net/Docs/PyQt5/QtDBus.html.
I came across your question when I had the same problem as you, a lack of sendErrorReply in PyQt5.
Here's how I got it to work in my Bluetooth application:
#pyqtSlot(QDBusMessage)
def AuthorizeService(self, message):
print("rejecting service authorization")
error = message.createErrorReply(QDBusError.AccessDenied, "Failed")
self._bus.send(error)
The trick is to create the error reply from the message that was received.

Calling function in different class in Tkinter GUI (Python 3.4)

I have a tkinter GUI that I structured based on Bryan Oakley's post here. I have a container class and some other classes. I have a button in my navigation class that is trying to call a function in the container class. For some reason I cant get self.parent... to work and have to type in Container... to get the function to work. The first line of code below works but the second line gets the error you see below.
self.Clear.bind("<Button-1>", lambda event: self.parent.combine_funcs(self.parent.ChangeRange(F_Clear), Container.UpdateToolBar(self, F_Clear)))
self.Clear.bind("<Button-3>", lambda event: self.parent.combine_funcs(self.parent.ChangeRange(F_Clear), self.parent.UpdateToolBar(self, F_Clear)))
TypeError: UpdateToolBar() takes 2 positional arguments but 3 were given
It only has a problem with the UpdateToolBar def...
def UpdateToolBar(self, file):
self.parent.ToolBar1.itemconfigure('toolbar', text=('CalcTime: '+str(self.parent.total)[:5]))
If I change the code to:
self.parent.UpdateToolBar(F_Clear)))
I get:
AttributeError: 'Container' object has no attribute 'parent'
I'm hoping the answer to this will give me more insight into interacting with code in different classes.

Categories