PySide6 Recommended Way to Use QFileDialog - pyside6

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.

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 comunication between mainwindow and widget

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.

Dart Keyboard Event Command Line

I've been monkeying around with Dart (& Flutter more specifically on mobile) and have become quite interested in trying Flutter on Desktop.
Anyways, for this one app idea, I need the ability to create a key event. From my research, I found this: https://api.dartlang.org/stable/2.2.0/dart-html/KeyEvent-class.html which mentions a KeyEvent however this primarily relates to Dart:HTML (which I presume just means browser only).
Does Dart run in the command line support any ability for generating key events? Like say I wanted an app to type something for a user.
Thanks!
#Isaac has basically explained it in his comment but this is how it looks like in code:
import 'dart:io';
void main(){
stdin.echoMode = false;
stdin.lineMode = false;
while(true){
if(stdin.readByteSync() == 102){ // f
print('You payed respect');
}
else{break;}
}
}

Inject bridge-code in JavaFX WebView before page-load?

I want to load some content or page in a JavaFX WebView and offer a Bridge object to Java so the content of the page can do calls into java.
The basic concept of how to do this is described here: https://blogs.oracle.com/javafx/entry/communicating_between_javascript_and_javafx
Now my question is: When is a good time inject the bridge-object into the WebView so it is available as soon as possible.
One option would be after page load as described here: https://stackoverflow.com/a/17612361/1520422
But is there a way to inject this sooner (before the page content itself is initialized), so the bridge-object is available DURING page-load (and not only after page-load)?
Since no one has answered, I'll tell you how I'm doing it, although it is ugly. This provides the ability for the page to function normally in non-Java environments but receive a Java object in Java environments.
I start by providing an onStatusChanged handler to the WebEngine. It listens for a magic value for window.status. If the magic value is received, the handler installs the Java object. (In my case, it's more complex, because I have some more complex orchestration: I'm executing a script that provides a client-side API for the page and then sets another magic value on window.status to cause the Java object to be sent to an initialization method of the client-side API).
Then in my target page, I have the following code in the first script in the page:
window.status = "MY-MAGIC-VALUE";
window.status = "";
This code is essentially a no-op in a "normal" browser but triggers the initialization when running in the custom JavaFX embedding.
In Java 8, you can trigger event changing from SCHEDULED to RUNNING to inject objects at this time. The objects will present in WebEngine before JavaScript running. Java 7, I see the state machine quite differs in operating, no solution given for Java 7.
webEngine.getLoadWorker().stateProperty().addListener(
new ChangeListener<State>(){
public void changed(ObservableValue<? extends State> ov,
State oldState,
State newState)
{
// System.out.println("old: "+oldState+", new: "+newState);
if(newState == State.RUNNING &&
oldState == State.SCHEDULED){
JSObject window = (JSObject)webEngine.executeScript("window");
window.setMember("foutput", foutput);
}
}
});

Luaj - add JButton action listener from Lua

In the application I'm developing in Java SE I use Luaj to implement functionality (this is a data collector application). The Java app reads a COM port of a device and gives the data to Lua event handlers which are written by the user of the application. Part of the user interface is also constructed from Lua, however, I'm having problems adding ActionListener objects (implemented in Lua as well) to Swing components, like JButton.
The code I'm currenty stuck at:
button = luajava.newInstance("javax.swing.JButton","test")
visuals:getPanel():add(button)
This creates a JButton object and puts it on a JPanel component. I'd like to define the action listener for this button in Lua as well.
Any idea how I can do that?
I tried the following, but it obviously does not work.
al = {}
function al.actionPerformed(ev)
print("test")
end
button.addActionListener(al)
I come a bit late, but for the reference, the swingapp.lua script shows how to handle listeners:
button:addActionListener(luajava.createProxy('java.awt.event.ActionListener',
{
actionPerformed = function (e)
print('Action', e)
end,
}))
Tested with Luaj-jse 3.0-alpha1

Resources