Adding QFileDialog as a widget inside another QDialog - python

I'm attempting to create a dialog which contains two child widgets: on the left side a QFileDialog instance so users can select files, and on the right side a separate widget which will be used to show a preview of the selected file if it is of a certain type.
The problem is that the dialog opens up and I can see the "preview" widget just fine, but the QFileDialog is not showing up at all.
This short example demonstrates my problem:
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
app = QApplication([])
main_dialog = QDialog()
main_dialog.setWindowTitle('My Dialog')
layout = QHBoxLayout(main_dialog)
file_dialog = QFileDialog(main_dialog, Qt.Widget)
file_dialog.setOption(QFileDialog.DontUseNativeDialog)
layout.addWidget(file_dialog)
preview = QLabel('Preview', main_dialog)
layout.addWidget(preview)
main_dialog.show()
app.exec_()
Some things that I've tried:
Add file_dialog.show() before/after main_dialog.show(): this shows up the QFileDialog, but in a different window; I want the file dialog to appear inside main_dialog, not as a separate window;
Do not pass Qt.Widget to the QFileDialog constructor, to no effect;
Do not pass main_dialog as parent to QFileDialog, again no effect;
Change main_dialog to a QWidget just to see if it changed anything, it did not;
I've searched the docs but did not find a suitable solution.
Any hints? Also, suggestions on how to accomplish the task of allowing the user to select a file and display a preview of the file in the same window are welcome.
Context: this is a port of an old application written for Qt3. Qt3's QFileSystem dialog had this "preview" functionality built-in; I'm trying to reproduce the same functionality in Qt5.
Versions
Python 2.7
PyQt 5.5.1
I've also tried with Python 3.6 (from conda-forge) but obtained the same behavior.

You need to turn off the Qt.Dialog flag in the file dialog's windowFlags...
file_dialog.setWindowFlags(file_dialog.windowFlags() & ~Qt.Dialog)
Otherwise the QFileDialog will always be created as a top level window. Works for me anyway.

Related

PyQt5 MainWindow with flag instantly goes out of scope

I created UI using Qt Designer. Then I converted the ui file to a .py file (pyuic -x) - it works fine if launched directly. Then I tried to subclass my ui in a separate file to implement additional logic. And this is where things start to go wrong. Inheriting from QMainWindow and my Qt Designer file works OK with no issues, as expected. However, the moment I set any WindowFlag for my QMainWindow (any flag - I tried these: StaysOnTop, FramelessWindowHint) and run the file, the window appears and instantly disappears. The program continues to run in a console window, or as a PyCharm process, but the window is gone. It looks to me like it is getting out of scope - but why setting a simple flag would make any difference to the garbage collector? Could someone explain this behaviour?
Minimum code required to reproduce this phenomenon:
from ui import Ui_MainWindow
from PyQt5 import QtCore, QtWidgets, QtGui
import sys
class Logic(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self):
QtWidgets.QMainWindow.__init__(self)
self.setupUi(self)
self.show()
# self.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint)
# self.setWindowFlags(QtCore.Qt.FramelessWindowHint)
self.setAttribute(QtCore.Qt.WA_NoSystemBackground, True)
self.setAttribute(QtCore.Qt.WA_TranslucentBackground, True)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
window = Logic()
sys.exit(app.exec_())
The window should appear and stay on the screen until one (or more) of the flags are uncommented. I use Python 3.8 (32-bit) with PyQt5. Run environment provided by PyCharm. Windows 10.
From the documentation of setWindowFlags():
Note: This function calls setParent() when changing the flags for a window, causing the widget to be hidden. You must call show() to make the widget visible again..
So, just move self.show() after setting the flags, or call it from outside the __init__ (after the instance is created), which is the most common and suggested way to do so, as it's considered good practice to show a widget only after it has been instanciated.

PyQt setText not rendering text properly in MacOs

I am creating an application using PyQt5 (pythyon 3.7 , MacOs X)
When I modify the text in a textbox using the instruction
self.line_main.setText(final_text)
from a function (which is connected to a push button)
The new text does not render properly in the textbox (see screenshot) and both the old and new text are overlapping in a strange way.
An over-simplified code to illustrate the problem is this one:
import sys
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtWidgets import QMainWindow, QWidget, QLabel, QLineEdit
from PyQt5.QtWidgets import QPushButton
from PyQt5.QtCore import QSize
class MainWindow(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.setMinimumSize(QSize(200, 200))
self.setWindowTitle("PyQt test")
self.line_main = QLineEdit(self)
self.line_main.move(20,20)
bt_upperCase = QPushButton('Upper Case', self)
bt_upperCase.move(20, 60)
bt_upperCase.clicked.connect(self.click_upCase)
def click_upCase(self):
final_text=self.line_main.text().upper()
self.line_main.setText(final_text)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
mainWin = MainWindow()
mainWin.show()
sys.exit( app.exec_() )
screenshot of mini-app, with 're' as input as 'RE' as output
The same thing may happen for labels, although at times, only the old text is visible, and I need to select the text with the mouse or resize the main window to 'refresh' the textbox and see the new value.
The problem does not happen if I run the code in a PC, only in MacOs (tested in High Sierra and Mojave with identical results)
Some weird behaviours are:
If position the textbox at the (0,0) position in the main window, which is not very practical, then the setText functions correctly. In some other locations of the box, too, but only if it is very close to the top-left corner, and no other element is near.
If I modify the textbox value at the initialization of the main window, then it renders correctly in all cases, The problem appears when you try to do it from a function (after clicking a button, for instance), like in the code attached.
The problem does not happen if I run the code in a PC, only in MacOs (tested in High Sierra and Mojave with identical results)
If position the textbox at the (0,0) position in the main window, (which is not very practical), then the setText functions correctly. In some other locations of the box, too, but only if it is very close to the top-left corner, and no other element is near.
Does anyone have a guess why this may happen, and how could it be solved?
UPDATE.
The problem dissapears if i run the code with python 3.6 in a conda installation (PyQt version 5.9.2). Still very intrigued to know what caused the problem.

PyQt : Linking buttons to functions in my program

So lets say i have this ui that has 2 empty text spots to fill and a 'Run' button.
I want to make it that the 2 empty text spots go to some values in the program and the run button will basically run the python 'Main' program ...
how can i do that ?
In PyQt5 the QWidgets module provides a set of UI elements to create classic desktop-style user interfaces. Widgets can display data and status information, receive user input, and provide a container for other widgets that should be grouped together. A widget that is not embedded in a parent widget is called a window.A parent widget containing various child widgets.So first you start to write a code for your window as
window=QtWidgets.QWidget()
(The QWidget class is the base class of all user interface objects).Once your window is created you need to set a layout for your UI window. There are many classes for layouts in Qt but most common are QVBoxLayout(to line up widgets vertically.) and QHBoxLayout(to line up widgets horizontally.) and many a times they both are used to make a custom layout. Now create your QVBoxLayout as
vbox=QWidgets.QVBoxLayout()
(note here that vbox is just a variable name).The next thing is to place widgets inside the window which can be done as
text_1=QtWidgets.QLineEdit()
text_2=QtWidgets.QLineEdit()
run_btn=QtWidgets.QPushButton("run")
text_3=QtWidgets.QLineEdit()
Note that in QPushButton we can give the name of the button as its argument(ex-run in this case). Now is the time for events and signals.
To connect the PushButton to a function , we write btn.clicked.connect(function_name) here btn is our PushButton.Note here that the function_name is without parenthesis which means that we have not called the function , just connected the button to the function (when the user will click the button the function gets executed).Foramlly this can be written as
run_btn=QtWidgets.QPushButton("run")
def main():
data_1=text_1.text()
data_2=text_2.text()
text_3.setText(str(int(data_1)+int(data_2)))
Now in our main function we first collected the data from text_1 and text_2 (there is a text() method for QLineEdit to get the data from QLineEdit as a str).So our main function takes the values of both text_1 and text_2 and adds them (it will raise an error if the values entered cannot be converted into integers) and sets that value to the text_3 by setText() method.
Now you have to pack the widgets in our vbox which we created earlier as
vbox.addWidget(text_1)
vbox.addWidget(text_2)
vbox.addWidget(run_btn)
vbox.addWidget(text_3)
And now set the layout of our window as
window.setLayout(vbox)
And to show the window as
window.show()
By now one thing is missing and that's the line
app=QtWidgets.QApplication(sys.argv)
This line is necessary as every PyQt5 application must create an application object. The sys.argv parameter is a list of arguments from a command line.
Now we have to create the mainloop of the application. The event handling starts from this point. The app.exec_() method runs our application and then provide a clean exit.
Now putting all together :
import sys
from PyQt5 import QtWidgets
app=QtWidgets.QApplication(sys.argv)
window=QtWidgets.QWidget()
vbox=QtWidgets.QVBoxLayout()
text_1=QtWidgets.QLineEdit()
text_2=QtWidgets.QLineEdit()
run_btn=QtWidgets.QPushButton("run")
text_3=QtWidgets.QLineEdit()
def main():
data_1=text_1.text()
data_2=text_2.text()
text_3.setText(str(int(data_1)+int(data_2)))
run_btn.clicked.connect(main)
vbox.addWidget(text_1)
vbox.addWidget(text_2)
vbox.addWidget(run_btn)
vbox.addWidget(text_3)
window.setLayout(vbox)
window.show()
sys.exit(app.exec_())
This will make a UI window like this :
Hope it helps.
Please do comment in case of some problem .
Happy coding!

Prevent QMainWindow from staying on top on Windows

I have a problem with a QMainWindow initialized with another QMainWindow as parent.
On Debian 8 (PyQt4), it behaves as I want it to: the child QMainWindow can go underneath the other one.
On Windows (PyQt5), however, the child window wants to stay on top.
Here is piece of code to reproduce the issue:
from silx.gui import qt
print(qt.PYQT_VERSION_STR)
app = qt.QApplication([])
mw1 = qt.QMainWindow()
mw1.show()
mw1.setWindowTitle("1")
mw2 = qt.QMainWindow(mw1)
mw2.show()
mw2.setWindowTitle("2")
app.exec_()
Is there a way to tell a QMainWindow to not stay on top of its parent? A flag ?

not displaying old value when editing cell in a QTableWidget

I have a basic QTableWidget, created with this python code:
from silx.gui import qt
app = qt.QApplication([])
qtw = qt.QTableWidget()
qtw.show()
qtw.setColumnCount(8)
qtw.setRowCount(7)
app.exec_()
The from silx.gui import qt line is just a wrapper that finds out the installed qt wrapper (PyQt4, PyQt5 or PySide) and flattens the qt namespace.
The resulting table has a strange behavior when I edit a cell: as expected, the old text is highligted when I double-click the cell, but the unusual behavior is that the old text remains visible and the new text overlaps with the old one while I'm typing it, until I press enter or I click another cell.
I would like the old text to disappear as soon as I start typing the new one. I know it's possible, because I have an example of program that features a qTableWidget with the behavior I would like to have.
But I cannot find where in that program the cell editing behavior is altered. How can I do this?
Example of "spam" and "eggs" overlayed.
[
EDIT: the code sample without the wrapper business
from PyQt5.Qt import QApplication, QTableWidget, qVersion
app =QApplication([])
print(qVersion())
qtw = QTableWidget()
qtw.show()
qtw.setColumnCount(8)
qtw.setRowCount(7)
app.exec_()
With PyQt4, use this import (also remove the print(qVersion()) line):
from PyQt4.QtGui import QApplication, QTableWidget
My method:
class MyDelegate(QItemDelegate):
def setEditorData(self,editor,index):
editor.setAutoFillBackground(True)
editor.setText(index.data().toString())
Generally, edit behavior is controlled via QItemDelegates. Typically, this is done to provide more advanced editing, or to filter input data or perform some side effects (like update a database) when edits are made. But you can also use it to just clear the editor presented to the user when editing.
class MyDelegate(QItemDelegate):
def setEditorData(self, editor, index):
# Normally, this would set the text of the editor to the current
# value of the cell. If you do nothing here, it will be blank.
editor.clear()
qtw = QTableWidget()
delegate = MyDelegate(qtw)
qtw.setItemDelegate(delegate)
In my case, above problem comes when I set background color of QWidgetTable to transparent. When I remove the setting, there is no old data overlays the new one anymore.
Hope it helps.
You could try connecting the signal emited by QTableWidget cellClicked(int row, int column) with a slot created for clearing the entry. http://pyqt.sourceforge.net/Docs/PyQt4/new_style_signals_slots.html#connecting-disconnecting-and-emitting-signals

Categories

Resources