Qt5 provides two functions to process command line arguments in QCommandLineParser class. The signatures are:
process(const QStringList &arguments)
process(const QCoreApplication &app)
This works fine in C++ but Python has no overloading feature nor signature detection. Apparently,
from PyQt5.QtCore import QCommandLineParser as qlp
qlp.process(("myapp", "-opt", "file"))
(example oversimplified to make the point)
references the process(const QCoreApplication &app) variant and errors out because the argument in no QCoreApplication instance.
At this step, I don't want to instantiate some kind of application object because I don't know yet if I'll run a QCoreApplication or a QGuiApplication, which is determined by parsing the arguments.
How can I force the desired variant?
Alternately, how can I preparse the command line arguments to check if I need a GUI or not? (But this alternative may need as much work as parsing the arguments with QCommandLineParser)
Related
This question already has an answer here:
Declaration of the custom Signals
(1 answer)
Closed 1 year ago.
i am trying to learn pyqt5 take look at the following code
qbtn = QPushButton('Quit', self)
qbtn.clicked.connect(QApplication.instance().quit)
clicked signature in qtwidgets.py
def clicked(self, checked: bool = ...) -> None: ...
where is the implementation of the clicked method ?
how it is not callable i mean it's method right ?
print (qbtn.clicked())
output:
TypeError: native Qt signal is not callable
and finally if it doesn't have any implementation how it effects the QPushButton
?
print(qbtn)
print (qbtn.clicked)
print (qbtn.clicked.connect)
output:
<PyQt5.QtWidgets.QPushButton object at 0x7f963ca88160>
<bound PYQT_SIGNAL clicked of QPushButton object at 0x7f963ca88160>
<built-in method connect of PyQt5.QtCore.pyqtBoundSignal object at 0x7f963ca91cc0>
There are two important aspects that should always be kept in mind:
PyQt (similarly to PySide) is a binding: it is an interface to the Qt framework, and as such provides access to it using standard python functions and methods; in order to create it, a special tool is used, SIP, which actually creates the binding from objects exposed to python to those of Qt and viceversa (for PySide, the tool used is called Shiboken);
signals are not methods, they are interfaces to which callable objects can connect to, and those objects will be called whenever the signal is emitted, provided they have a compatible signature;
The file you're referring to is a pyi file. From What does āiā represent in Python .pyi extension?:
The *.pyi files are used by PyCharm and other development tools to provide more information, such as PEP 484 type hints, than it is able to glean from introspection of extension types and methods. They are not intended to be imported, executed or used for any other purpose other than providing info to the tools. If you don't use use a tool that makes use of .pyi files then you can safely ignore this file.
You mentioned the following line:
def clicked(self, checked: bool = ...) -> None: ...
which is only found in those files, and is just that: an information.
Signals in C++ are declared in headers similarly to functions, having arguments that indicate the signal signature(s), and are then "transformed" into signals when Qt (or the program that declares its own signals) is compiled.[1]
Since PyQt and PySide are created using the aforementioned automated tools, the result is that signals might be listed as methods; notably, the official PySide docs list signals even including def: in PySide2 a specific "Signal" section is used, while in PySide6 they are not even identified as such.
In the python bindings, signals are unbound attributes for classes, but when a signal is referenced as a QObject instance, PyQt automatically binds the instance to the signal to create a bound signal.
>>> hasattr(QtWidgets.QPushButton.clicked, 'emit')
False
>>> hasattr(QtWidgets.QPushButton().clicked, 'emit')
True
You can see that the signal is dynamically bound by using a simple id (which should return an unique and constant value for an object):
>>> b = QtWidgets.QPushButton()
>>> id(b.clicked)
2971296616
>>> id(b.clicked)
2971299208
# or even:
>>> b.clicked == b.clicked
False
So, what you see from some documentation or reference file, is primarily the signature used to create the signal, but also the expected signature for the signal emission/receiver, similarly to what can be done in Python when declaring a new signal (with the difference that it's not possible to define default values, like the checked=False of QAbstractButton).
[1] this is a major oversimplification, I don't have enough knowledge of C++ to explain how exactly signal creation works, but it's just for the sake of explanation.
I don't do a lot of work in GUI's, but I've decided to move from occasionally using PyQt4 to PyQt5. My IDE is giving me warnings about some of the __init__ functions, particularly QWidget and QMainWindow.
If you look at the IntelliSense'd parameters, you'll see that the parentparameter has a default and the flags does not. The IDE tells me that flags is unfilled, but when I don't provide it, nothing happens. Why is this happening?
I'm using Python 3.5.
The correct signature is this:
QMainWindow(parent: QWidget = None,
flags: Union[Qt.WindowFlags, Qt.WindowType] = Qt.WindowFlags())
So it looks like the IntelliSense in your IDE either does not know how to parse type-hints properly, or the PyQt stub files need to be installed. There are only two arguments: parent and flags, both of which have defaults.
(NB: you should never use self.__class__ with super as it can lead to an infinite recursion under certain circumstances. Always pass in the subclass as the first argument - unless you're using Python 3, in which case you can omit all the arguments).
Installing pyQt5-stubs will fix this error.
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?
I'm trying to add a custom signal to a class -
class TaskBrowser(gobject.GObject):
__list_signal__ = (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (<List datatype>,))
__gsignals__ = {'tasks-deleted': __list_signal__}
...
def on_delete_tasks(self, widget=None, tid=None):
...
gobject.idle_add(self.emit, "tasks-deleted", deleted_tasks) #deleted_tasks is of type 'list'
...
...
In the __gsignals__ dict, when I state list as parameter type, I get the following error traceback -
File "/home/manhattan/GTG/Hamster_in_hands/GTG/gtk/browser/browser.py", line 61, in <module>
class TaskBrowser(gobject.GObject):
File "/usr/lib/python2.7/site-packages/gobject/__init__.py", line 60, in __init__
cls._type_register(cls.__dict__)
File "/usr/lib/python2.7/site-packages/gobject/__init__.py", line 115, in _type_register
type_register(cls, namespace.get('__gtype_name__'))
TypeError: Error when calling the metaclass bases
could not get typecode from object
I saw the list of possible parameter types, and there's no type for list
Is there a way I can pass a list as a signal parameter ?
The C library needs to know the C type of the parameters, for Gtk, Gdk, Gio and GLib objects the types in the wrappers will work as they mirror the C types in the Gtk and family libraries.
However, for any other type you need to pass either object or gobject.TYPE_PYOBJECT. What that means is that the on the C side a "python object" type is passed. Every object accessible from a python script is of that type, that pretty much means anything you can pass through your python script will fit an object parameter.
That also means, of course, that this feature doesn't work in python! Python relies on duck typing, that means we figure out if an object is of a type when we do stuff with it and it works. Passing the type of the parameter works for C as a way to make sure the objects passed are of the type the program needs them to be, but in python every object is of the same type in the C side so this feature becomes completely useless on the python side.
But that doesn't means it is completely useless overall. For example, in python int is an object. But not in C. If you are using property bindings, which were coded in the C side of the Gtk library, you will want to specify the appropriate type as bindings of different property types don't work.
Using C side wrapped signal handlers with object parameter types is also bound not to work, since the C side needs a specific type to function.
In pygtk3 this error has been occured for me because import gobject directly.
and fixed this error by from gi.repository import GObject.
you can see details in this link.
Here's the scenario: I am using ctypes to load a C DLL into a Python program. I want to register a callback so that code in the DLL can call a function in Python.
This is all great until I want to have the function be variadic. For example, say I wanted to implement "printf" in Python (for argument's sake, I'll also have it return an integer):
def printf(format, *args):
(...)
return 0
I am creating the callback function pointer using the CFUNCTYPE factory in ctypes:
callback_type = CFUNCTYPE(c_int, c_wchar_p)
Of course, since I only specify one input argument (the format string as a Unicode char array), the callback works but *args is empty.
So, is there any way of making this work with ctypes, or do I need to rewrite my C code to not require a variadic callback function?
No, it's not supported by the underlying libffi. You need indeed to write custom C code.
(Fwiw, CFFI is a ctypes alternative. Also based on libffi, it suffers from the same limitation --- but at least it makes it easy to write custom pieces of C code to do the interfacing.)