pyside6 comunication between mainwindow and widget - pyside6

I have 2 files (main, widget1), what i need is to be able to communicate between the 2.
edit:
sorry for the mess, i´ll try to put cleaner code.
So I have a main file with a stackedWidget(with Qt designer generated Ui file)and a widget file (also with qt generated Ui file) .
What i need is to be able to access the main file from the widget so i can change page of stackedWidget.
main.py:
from ui_main import Ui_MainWindow
class MainWindow(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.widget1 = W1.Widget1(self)
self.ui.stackedWidget_1.insertWidget(0, self.widget1)
self.ui.stackedWidget_1.setCurrentIndex(0)
def nextpage():
self.ui.stackedWidget_1.setCurrentIndex(0)
self.ui.b0.clicked.connect(nextpage)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec())
widget.py
from PySide6.QtWidgets import *
from ui_widget import Ui_W1
import main
class Widget1(QWidget):
def __init__(self, *args, **kwargs):
QWidget.__init__(self, *args, **kwargs)
self.ui = Ui_W1()
self.ui.setupUi(self)
def widget_next_page():
main.MainWindow().ui.stackedWidget_1.setCurrentIndex(0)
print('0')
self.ui.widget_button.clicked.connect(widget_next_page)
Thanks for any help, i´m trying to understand how to break the code into files so the main file don't get huge...
If there is any better way(not too complicated, cause as you surely know already I'm starting.)

In widget_next_page you're creating a new instance of MainWindow, instead of using the existing one.
The whole concept behind OOP modularity and Qt signals/slots is that an object should just emit a signal and not take actions on other objects on its own, especially if they are outside of its scope.
In your case, the Widget1 should not try to change the page of its parent (the stacked widget of the main window).
Instead, the function changing the stacked widget index should be in the main window, and that function should be connected to whatever signal it should react to. Since you already have that function (nextpage), you can reuse it.
class MainWindow(QMainWindow):
def __init__(self):
# ...
self.widget1.ui.widget_button.clicked.connect(nextpage)
The above also means that you should remove the signal connection of the widget_button inside the __init__ of Widget1.
Note that local functions should be used only when necessary, especially when connected to signals, otherwise it will be it very difficult to disconnect the specific function in case of need, since the reference is then lost when the outer function returns. Besides that, I strongly suggest you to do more research about how objects (specifically classes and instances) work in OOP in general and with Python.

Related

How can I get a custom python type and avoid importing a python module every time a C function is called

I am writing some functions for a C extension module for python and need to import a module I wrote directly in python for access to a custom python type. I use PyImport_ImportModule() in the body of my C function, then PyObject_GetAttrString() on the module to get the custom python type. This executes every time the C function is called and seems like it's not very efficient and may not be best practice. I'm looking for a way to have access to the python custom type as a PyObject* or PyTypeObject* in my source code for efficiency and I may need the type in more than one C function also.
Right now the function looks something like
static PyObject* foo(PyObject* self, PyObject* args)
{
PyObject* myPythonModule = PyImport_ImportModule("my.python.module");
if (!myPythonModule)
return NULL;
PyObject* myPythonType = PyObject_GetAttrString(myPythonModule, "MyPythonType");
if (!myPythonType) {
Py_DECREF(myPythonModule);
return NULL;
}
/* more code to create and return a MyPythonType instance */
}
To avoid retrieving myPythonType every function call I tried adding a global variable to hold the object at the top of my C file
static PyObject* myPythonType;
and initialized it in the module init function similar to the old function body
PyMODINIT_FUNC
PyInit_mymodule(void)
{
/* more initializing here */
PyObject* myPythonModule = PyImport_ImportModule("my.python.module");
if (!myPythonModule) {
/* clean-up code here */
return NULL;
}
// set the static global variable here
myPythonType = PyObject_GetAttrString(myPythonModule, "MyPythonType");
Py_DECREF(myPythonModule);
if (!myPythonType) {
/* clean-up code here */
return NULL;
/* finish initializing module */
}
which worked, however I am unsure how to Py_DECREF the global variable whenever the module is finished being used. Is there a way to do that or even a better way to solve this whole problem I am overlooking?
First, just calling import each time probably isn't as bad as you think - Python does internally keep a list of imported modules, so the second time you call it on the same module the cost is much lower. So this might be an acceptable solution.
Second, the global variable approach should work, but you're right that it doesn't get cleaned up. This is rarely a problem because modules are rarely unloaded (and most extension modules don't really support it), but it isn't great. It also won't work with isolated sub-interpreters (which isn't much of a concern now, but may become more more popular in future).
The most robust way to do it needs multi-phase initialization of your module. To quickly summarise what you should do:
You should define a module state struct containing this type of information,
Your module spec should contain the size of the module state struct,
You need to initialize this struct within the Py_mod_exec slot.
You need to create an m_free function (and ideally the other GC functions) to correctly decref your state during de-initialization.
Within a global module function, self will be your module object, and so you can get the state with PyModule_GetState(self)

PySide6 Recommended Way to Use QFileDialog

The PySide6 QDialog.exec() docs state to avoid using exec():
Avoid using this function; instead, use open(). Unlike , open() is asynchronous, and does not spin an additional event loop. This prevents a series of dangerous bugs from happening (e.g. deleting the dialog’s parent while the dialog is open via ). When using open() you can connect to the finished() signal of QDialog to be notified when the dialog is closed.
open() is a virtual function, but I don't believe it is pure virtual since I can call it directly on any subclass to immediately open the dialog.
However, QFileDialog.open(receiver, member) is a bit of a mystery. It connects either the filesSelected() or fileSelected() signal (depending on the fileMode) to
a slot specified by receiver and member
and
The signal will be disconnected from the slot when the dialog is closed.
Considering the above, is the correct (i.e. recommended) way to use QFileDialog like so:
from qtpy import QtCore, QtWidgets
class MyWindow(QtWidgets.QMainWindow):
def __init__(self) -> None:
QtWidgets.QMainWindow.__init__(self)
self.dialog = QtWidgets.QFileDialog(self)
self.dialog.setFileMode(QtWidgets.QFileDialog.Directory)
self.dialog.setWindowTitle('Open folder...')
self.dialog.finished.connect(self.on_finished)
#QtCore.Slot(QtWidgets.QDialog.DialogCode)
def on_finished(
self,
result: QtWidgets.QDialog.DialogCode,
) -> None:
if result == QtWidgets.QDialog.Accepted:
print('Accepted')
else: # QtWidgets.QDialog.Rejected
print('Rejected')
if __name__ == '__main__':
app = QtWidgets.QApplication([])
window = MyWindow()
window.show()
window.dialog.open()
app.exec()
or is QFileDialog.open(receiver, member) supposed to be used? If so, how does one use receiver and member?
NOTE: I'm aware the slot decorator isn't strictly necessary in PySide6, but I add it since it allows me to see at a glance which of my methods are slots vs. just methods.
TL;DR: exec(), open(), and static functions can all be used. Which one you choose depends on your use case, which primarily has to do with whether the dialog is application or window modal. exec() is application modal and open() window modal, the former being subject to bugs if the dialog is able to delete its parent while the dialog is open. To use receiver and member requires old-style signal/slot syntax, which is demonstrated below.
"Correct" way to use QFileDialog?
Per #musicamante's comment, there is no "correct" way and using either exec() or the static functions are acceptable. For example, the PySide6 QFileDialog docs state
The easiest way to create a QFileDialog is to use the static functions.
and then again, the PySide6 docs also state
The most common way to display a modal dialog is to call its exec() function.
The docs include examples that use exec(), and in fact, if you review the QFileDialog C++ source code, you will see that most of these static methods ultimately call exec().
Hence, how QFileDialog is used depends on one's needs:
On Windows and MacOS, static functions return a native file dialog, which keeps the look and feel consistent across OS's, but limits you to native functionality unless you pass DontUseNativeDialog, in which case a QFileDialog is returned:
By default, a platform-native file dialog will be used if the platform has one. In that case, the widgets which would otherwise be used to construct the dialog will not be instantiated, so related accessors such as layout() and itemDelegate() will return null. Also, not all platforms show file dialogs with a title bar, so be aware that the caption text might not be visible to the user. You can set the DontUseNativeDialog option to ensure that the widget-based implementation will be used instead of the native dialog.
You must decide whether or not you want the dialog to be modal (application or window) or non-modal and if you need to do things like delete the parent widget during execution of the dialog. On Windows, the static functions spin a blocking modal event loop that does not dispatch QTimers. Quite literally, open() behaves in a window modal fashion, and the docs corroborate this.
Thus, I argue the most important functional characteristic to consider when choosing whether to use exec() or open() is the modality of the dialog and whether or not widgets can be deleted/closed when it is open.
open(): How Does One Use receiver and member?
As mentioned in the question, the wording of the QFileDialog.open docs is confusing:
The specific signal depends is filesSelected() if fileMode is ExistingFiles and fileSelected() if fileMode is anything else.
This is saying receiver is passed either a list of files or a single file (depending on the dialog file mode), but the receiver/member nomenclature may feel odd to those of us who do not recall the old-style signal/slot syntax, which more closely mirrors how signals and slots are connected in C++ (see C++ GUI Programming with Qt4, Ch. 2, section "Signals and Slots in Depth"):
connect(sender, SIGNAL(signal), receiver, SLOT(slot));
Indeed, all QFileDialog.open() does is
create a signal containing the file or files to send,
connect the signal to the slot specified by receiver and member,
set the signal and slot to be disconnected when the dialog is closed, and
call QDialog.open()
Hence, using QFileDialog.open() with receiver and member requires the old-style slot/signal syntax with the SLOT macro and #Slot decorator. Without the macro, Qt will issue the warning:
qt.core.qobject.connect: QObject::connect: Use the SLOT or SIGNAL macro to connect MyWindow::on_finished()
Without the decorator, Qt will complain:
qt.core.qobject.connect: QObject::connect: No such slot MyWindow::on_finished()
Example:
from __future__ import annotations
from qtpy import QtCore, QtWidgets
class MyWindow(QtWidgets.QMainWindow):
def __init__(self) -> None:
QtWidgets.QMainWindow.__init__(self)
self.dialog = QtWidgets.QFileDialog(self)
self.dialog.setFileMode(QtWidgets.QFileDialog.Directory)
self.dialog.setWindowTitle('Open folder...')
#QtCore.Slot()
def on_finished(self) -> None:
for path in self.dialog.selectedFiles():
print(path)
if __name__ == '__main__':
app = QtWidgets.QApplication([])
window = MyWindow()
window.show()
window.dialog.open(window, QtCore.SLOT('on_finished()'))
app.exec()
Since QFileDialog.open(receiver, member) calls QDialog.open() under the hood, you can use that instead. The benefit is this does not require the old-style syntax, with the caveat that you are responsible for connecting signals and properly disconnecting them when the dialog closes.
It should be used in situations where the receiver is not the parent of the dialog. In this case, the dialog will be deleted when the receiver is deleted, and the signal will be disconnected automatically.
The below example will crash with error:
from PySide6.QtWidgets import QApplication, QPushButton, QFileDialog
from PySide6.QtCore import Slot
app = QApplication([])
button = QPushButton("Click me")
dialog = QFileDialog()
#Slot()
def on_button_clicked():
dialog.open()
button.clicked.connect(on_button_clicked)
button.show()
app.exec()
The reason is that the dialog is deleted when the button is deleted, but the signal is still connected to the slot.
The solution is to use the second form of open() and pass the button as the receiver:
from PySide6.QtWidgets import QApplication, QPushButton, QFileDialog
from PySide6.QtCore import Slot
app = QApplication([])
button = QPushButton("Click me")
dialog = QFileDialog()
#Slot()
def on_button_clicked():
dialog.open(button, "deleteLater")
button.clicked.connect(on_button_clicked)
button.show()
app.exec()
Another useful situation is in lambda functions:
from PySide6.QtWidgets import QApplication, QPushButton, QFileDialog
from PySide6.QtCore import Slot
app = QApplication([])
button = QPushButton("Click me")
dialog = QFileDialog()
#Slot()
def on_button_clicked():
dialog.open(lambda: button.deleteLater())
button.clicked.connect(on_button_clicked)
button.show()
app.exec()
There are other situations that the second form of open() can be used, but the above two are the most common.

Jena read hook not invoked upon duplicate import read

My problem will probably be explained better with code.
Consider the snippet below:
// First read
OntModel m1 = ModelFactory.createOntologyModel();
RDFDataMgr.read(m1,uri0);
m1.loadImports();
// Second read (from the same URI)
OntModel m2 = ModelFactory.createOntologyModel();
RDFDataMgr.read(m2,uri0);
m2.loadImports();
where uri0 points to a valid RDF file describing an ontology model with n imports.
and the following custom ReadHook (which has been set in advance):
#Override
public String beforeRead(Model model, String source, OntDocumentManager odm) {
System.out.println("BEFORE READ CALLED: " + source);
}
Global FileManager and OntDocumentManager are used with the following settings:
processImports = true;
caching = true;
If I run the snippet above, the model will be read from uri0 and beforeRead will be invoked exactly n times (once for each import).
However, in the second read, beforeRead won't be invoked even once.
How, and what should I reset in order for Jena to invoke beforeRead in the second read as well?
What I have tried so far:
At first I thought it was due to caching being on, but turning it off or clearing it between the first and second read didn't do anything.
I have also tried removing all ignoredImport records from m1. Nothing changed.
Finally got to solve this. The problem was in ModelFactory.createOntologyModel(). Ultimately, this gets translated to ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM_RDFS_INF,null).
All ontology models created with the static OntModelSpec.OWL_MEM_RDFS_INF will have their ImportsModelMaker and some of its other objects shared, which results in a shared state. Apparently, this state has blocked the reading hook to be invoked twice for the same imports.
This can be prevented by creating a custom, independent and non-static OntModelSpec instance and using it when creating an OntModel, for example:
new OntModelSpec( ModelFactory.createMemModelMaker(), new OntDocumentManager(), RDFSRuleReasonerFactory.theInstance(), ProfileRegistry.OWL_LANG );

My code using user_data_dir does not work and it gives an error saying 'self' is not defined. Can someone explain to me why?

The following is a snippet of my code. When I tried running this, the error states that 'self' is not defined. I copied this code online because I don't really know how to use the function user_data_dir. I know that it works (I'm using Windows to write this) with just store = JsonStore('user.json') but I've been reading that using the function user_data_dir would be useful because it is a general function that creates a writeable path for various systems. Would be great if someone could help explain this!
from kivy.storage.jsonstore import JsonStore
from os.path import join
data_dir = getattr(self, 'user_data_dir')
store = JsonStore(join(data_dir,'user.json'))
class Welcome(Screen):
pass
data_dir = getattr(self, 'user_data_dir')
When you copied this line, it was somewhere inside some class's function:
class Some:
def func(self):
data_dir = getattr(self, 'user_data_dir')
Method located inside class in Python receives self as first parameter.
But not every object has user_data_dir attribute: as inclement noticed above it's App objects attribute. You should do something like:
class MyApp(App):
def build(self):
data_dir = getattr(self, 'user_data_dir')
store = JsonStore(join(data_dir,'user.json'))
# ...
Upd:
You can store path to json file inside app class and access to app instance to get this path with App.get_running_app():
class MyApp(App):
#property # see https://www.programiz.com/python-programming/property
def storage(self):
return join(self.user_data_dir, 'user.json')
and later in any place you want:
class SomeClass():
def some_func(self):
print('here\'s our storage:', App.get_running_app().storage)

Plone 4 : How to customize a method in Archetypes content types?

I have tried, under Plone 4.3.3, to customize a class method of an archetype content type in one of my products.
I have a product bsw.produit_1 with a content type MyContent defined as follows:
class MyContent(base.ATCTContent):
implements(IMyContent)
meta_type = "MyContent"
schema = MyContent`
def ma_fonction(self):
......
return res
I want to modify the code of my function ma_fonction in another product. I have tried using an adapter and following the plone docs, but without success.
The class where I wish to customize the function:
class CustomClass(object):
""" """
implements(IMyContent)
adapts(IMyContent)
def at_post_payment_script(self, obj_transaction):
""" """
......
# My new code
return res
The configure.zcml where I declared my adapter:
<adapter for="bsw.produit_1.content.mycontent.MyContent"
provides="bsw.produit_1.interfaces.IMyContent"
factory=".customclass.CustomClass" />
In my zcml declaration, I've also tried putting archetypes.schemaextender.interfaces.ISchemaExtender as provides or putting the interface IMyContent for for instead of the class.
None of these worked, every time, the customized code is not executed. Does anybody have a solution for this?
The solution you need depends on what you want to achieve.
But archetypes.schemaextender is the wrong solution.
schemaextender is there to modify the schema, this includes:
fields order
field/widget attributes
schemata
setter/getter of a field
new fields
override fields
To implement your own adaptera is definitely the right approach.
First you need to implement a adapter for the default behavior.
Second, you need to adapt the context and the request. The request is important, since that's a way to define a more specific adapter if your other product is installed.
Python code for the default implementation (adapter.py):
from zope.component import adapts
from zope.interface import Interface
from zope.interface import implements
class IBehavior(Interface):
def __init__(context, request)
"""Adapts context and request"""
# ... more ...
class DefaultBehavior(object):
implements(IBehavior)
adapts(IMyContent, Interface) # IMPORTAN two discriminators
def __init__(self, context, request):
self.context = context
self.request = request
def __call__(self):
# your default implementation goes here.
Register the adapter with zcml:
<adapter factory=".adapter.DefaultBehavior" />
Your now able to call the default adapter in ma_fonction
from zope.component import getMultiAdapter
class MyContent(base.ATCTContent)
def ma_fonction(self):
adapter = getMultiAdapter((self, self.REQUEST), IDefaultBehavior)
return adapter()
Now you can implement a more specific adapter in your other product using a browserlayer. Check documentation, how to register a browserlayer
In your otherpackage you can now register a adapter which implements the same IBehavior interface, but also adapts your browserlayer.
from other.package.interfaces import IOtherPackageLayer
from zope.component import adapts
from zope.interface import implements
class DifferenBehavior(object):
implements(IBehavior)
adapts(IMyContent, IOtherPackageLayer) # IMPORTAN adapt the browserlayer not Interface
def __init__(self, context, request):
self.context = context
self.request = request
def __call__(self):
# your different implementation goes here.
Register also with zcml:
<adapter factory=".adapter.DifferenBehavior" />
Your ma_fonctionnow calls the default adapter, if the other package is not installed. And the different adapter if the other package is installed.
The simplest method you can use (although not politically correct!) is monkey-patching.
Take a look at collective.monkeypatcher, you simply need a configuration like that (in your 3rd party product):
<monkey:patch
description=""
class="your.package.MyContent"
original="ma_fonction"
replacement=".monkeys.new_ma_fonction"
/>
Then in your package create also a monkeys.py module with the new method inside:
def new_ma_fonction(self):
# do stuff
return res

Resources