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.
Related
I am defining a function that gets pdf in bytes, so I wrote:
def documents_extractos(pdf_bytes: bytes):
pass
When I call the function and unfortunately pass a wrong type, instead of bytes let's say an int, why I don't get an error? I have read the documentation regarding typing but I don't get it. Why is the purpose of telling the function that the variable shoudl be bytes but when you pass and int there is no error? This could be handle by a isinstance(var, <class_type>) right? I don't understand it =(
Type hints are ignored at runtime.
At the top of the page, the documentation that you've linked contains a note that states (emphasis mine):
The Python runtime does not enforce function and variable type annotations. They can be used by third party tools such as type checkers, IDEs, linters, etc.
The purpose of type hints is for static typechecking tools (e.g. mypy), which use static analysis to verify that your code respects the written type hints. These tools must be run as a separate process. Their primary use is to ensure that new changes in large codebases do not introduce potential typing issues (which can eventually become latent bugs that are difficult to resolve).
If you want explicit runtime type checks (e.g. to raise an Exception if a value of a wrong type is passed into a function), use isinstance().
By default python ignores type hints at runtime, however python preserves the type information when the code is executed. Thanks to this library authors can implement runtime type checking packages such as typeguard, pydantic or beartype.
If you don't want to use isinstance checks yourself, you can use one of those libraries.
Typeguard example:
main.py:
from typeguard import importhook
importhook.install_import_hook('mypack')
import mypack
mypack.documents_extractos("test")
mypack.py
def documents_extractos(pdf_bytes: bytes):
pass
When you run python3 main.py you will get error TypeError: type of argument "pdf_bytes" must be bytes-like; got str instead
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.
import util
class C():
save = util.save
setattr(C, 'load', util.load)
C.save is visible to the linter - but C.load isn't. There's thus some difference between assigning class methods from within the class itself, and from outside. Same deal for documentation builders; e.g. Sphinx won't acknowledge :meth:C.load - instead, need to do :func:util.load, which is misleading if load is meant to be C's method. An IDE (Spyder) also fails to "go to" method via self.load code.
The end-goal is to make linter (+docs & IDE) recognize load as C's method just like C.save is, but class method assignment needs to be dynamic (context). Can this be accomplished?
Note: the purpose of dynamic assignment is to automatically pull methods from modules (e.g. util) instead of having to manually update C upon method addition / removal.
Disclaimer: This solution does not work in all use cases, see comments. I leave it here, since it might still be useful under some circumstances.
I don't know about the linter and Sphinx, but an IDE like PyCharm will recognize the method if you declare it upfront using type hinting. In the code below, without the line load: Callable[[], None], I get the warning 'Unresolved attribute reference', but with the line there are no warnings in the file. Check the docs for more information about type hinting.
Notes:
Even with the more general load: Callable, the type checker is satisfied.
If you don't always declare a callable, a valid declaration is load: Optional[Callable] = None. This means that the default value is None. If you then call it without setting it, you will get an error, but you got that already anyway, that's unrelated to this typing.
p.s. I don't have your utils, so I defined some functions in the file itself.
from typing import Callable
def save():
pass
def load():
pass
class C:
load: Callable[[], None]
save = save
setattr(C, 'load', load)
C.load()
I am writing a python3 + webkit2 + gtk3 based GUI. Everything works fine, but when I tried to use WebKit2.WebView.run_javascript_finish() in a callback function, getting the return (a WebKit2.JavascriptResult objetc), e extrat the value with WebKit2.JavascriptResult.get_value(), I received a "TypeError: Couldn't find foreign struct converter for 'JavaScriptCore.Value'".
I have a "from gi.repository import JavaScriptCore" on the imports, and I dont know why this error is occuring.
ps: Sorry about my terrible english.
Unfortunately, using the JavaScriptCore library isn't supported in Python.
The definitions in gi.repository.JavaScriptCore only include opaque definitions for JavaScriptCore.Value and JavaScriptCore.GlobalContext in order to provide type information for functions that use those types; as you can verify by looking in /usr/share/gir-1.0/JavaScriptCore-4.0.gir. But as you've discovered, you can't actually call those functions because there's no information on how to convert those types to native Python objects.
I recommend writing a function in C which takes in your WebKit2.JavaScriptResult and fetches whatever information you need from it, and then exporting that function in its own private GIR library. So you would not use any JavaScriptCore types in your Python code. This is what it might look like:
from gi.repository import MyAppPrivate
# ...
def on_javascript_finish(obj, res):
result = obj.run_javascript_finish(res)
my_real_results = MyAppPrivate.process(result)
I am using a program where Python is the native scripting language. Unfortunately, they have a native function that uses the name bytes. This causes a problem when I am trying to use the actual bytes built-in function, and it thinks I am referencing that built-in variable. I will show you what I mean, one object as the following built-in code:
def receive(row, table, message, bytes):
#This is defined in the GUI
So, row, table, message, and bytes are all passed in as arguments, effectively overwriting the name bytes. So if I were to say bytes(something).decode() I get a TypeError: 'bytes' object is not callable
Is there any way to get out of this jam?
Use a different name for the fourth parameter (if you can change the signature of the function)
def receive(row, table, message, bytes_):
#This is defined in the GUI
Your problem is similar to this one. Just from builtins import bytes as _bytes; this will let you do _bytes(something).decode().
Although renaming the fourth argument is a better solution.