How to add a minimize hint to QDialog? - pyqt5

I have a dialog that is non-modal and appears on-top of other widgets in the application. But it's hidden when another window from a different application covers it. This is exactly what I'm after.
However, I'm struggling to add a minimize button to its title bar.
Here's a basic example with my initial attempt:
from PySide2 import QtCore, QtWidgets
class TestDialog(QtWidgets.QDialog):
def __init__(self, parent=None):
super(TestDialog, self).__init__(parent)
self.resize(200, 100)
self.setWindowFlags(self.windowFlags() |
QtCore.Qt.WindowMinimizeButtonHint)
dialog = TestDialog()
dialog.show()
(But it doesn't add the minimize hint)
Here's an example that adds the minimize hint:
class TestDialog(QtWidgets.QDialog):
def __init__(self, parent=None):
super(TestDialog, self).__init__(parent)
self.resize(200, 100)
self.setWindowFlags(QtCore.Qt.WindowMinimizeButtonHint |
QtCore.Qt.WindowCloseButtonHint)
(But the widget is hidden when I click somewhere else in the application)
And here's an example where it stays on top:
class TestDialog(QtWidgets.QDialog):
def __init__(self, parent=None):
super(TestDialog, self).__init__(parent)
self.resize(200, 100)
self.setWindowFlags(QtCore.Qt.WindowMinimizeButtonHint |
QtCore.Qt.WindowCloseButtonHint |
QtCore.Qt.WindowStaysOnTopHint)
(But this stays on-top of all windows, even from other applications)
Is there a way to add the minimize button without introducing undesirable side effects?
Thanks

Related

How to set a hover event handler on a QPushButton in PyQt5

I'm trying to set a hover event on a button to open an options menu implemented as a QDialog. Everything works so far except the hovering: the menu opens when the button is pressed and disappears if any of the options is selected or the mouse is moved away from the dialog.
Now I want to open the window without clicking the button but rather by hovering over it.
I've seen PyQt5 mouse hover functions and How to detect mouse hover event in PySide6 widget but i wasn't able to make it work this way.
My code looks like this:
class ElementWidget(QWidget):
def __init__ (self, p, element):
super().__init__(p)
layout = QHBoxLayout()
label = QLabel(element)
label.setFixedSize(200,39)
self.btn = QPushButton("btn")
self.btn.clicked.connect(self._openOptionsMenu)
self.btn.setFixedSize(50,39)
layout.addWidget(label)
layout.addWidget(self.btn)
self.setLayout(layout)
self.setFixedSize(250,60)
def _openOptionsMenu(self):
self.dlg = selfClosingDialog(self.closeOptionsMenu, parent = self)
self.dlg.setLayout(ElementOptionsLayout(self.closeOptionsMenu))
self.dlg.setWindowFlag(Qt.FramelessWindowHint)
self.dlg.setGeometry(QCursor.pos().x(), QCursor.pos().y() ,100,100)
self.dlg.show()
def closeOptionsMenu(self):
self.dlg.close()
if __name__ == "__main__":
app = QApplication([])
window = QMainWindow()
window.resize(500,400)
wid = ElementWidget(window,"Parabola_0")
window.show()
app.exec_()
with the custom dialog:
class selfClosingDialog(QDialog):
def __init__(self, closeFunc, parent=None):
super().__init__(parent)
self.closeFunc = closeFunc
def leaveEvent(self, event):
self.closeFunc()
The perfect solution would be to replace the clicked-event by some kind of an onHover
I found the answer.
It is not a signal but an event enterEvent that needs to be reimplemented by a subclass of QWidget
class HoverOpenBtn(QPushButton):
def __init__(self, text, openOtionsFunc, parent=None):
super().__init__(text, parent)
self.openFunc = openOtionsFunc
def enterEvent(self, event):
self.openFunc()

PyQT5 & QtabWidget - changing values in different tabs

I've build a small PyQt5 application with a QtabWidget as my CentralWidget. Because I wanted to bring some structure into the code I create new tabs by using different classes. This works pretty fine so far.
class Main_window(QtWidgets.QMainWindow):
"""Main Window"""
def __init__(self, parent=None):
"""Initializer"""
super(Main_window, self).__init__(parent)
self.setGeometry(50, 50, 1100, 750)
# Create menu
self.qtMenu3()
self.tabWidget = QtWidgets.QTabWidget()
# sets the tabWidget as the central widget inside the QMainWindow
self.setCentralWidget(self.tabWidget)
self.tab_first = QtWidgets.QWidget()
self.tabWidget.addTab(FirstTab(), 'First')
self.tab_second = QtWidgets.QWidget()
self.tabWidget.addTab(SecondTab(), 'Second')
My SecondTab class looks like this and creates a GroupBox and two QTextEdits
class SecondTab(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setWindowTitle("Groupbox")
layout = QGridLayout()
self.setLayout(layout)
groupbox = QGroupBox("GroupBox Example")
# groupbox.setCheckable(True)
layout.addWidget(groupbox)
# Layout manager QVBox (vertical)
vbox = QVBoxLayout()
groupbox.setLayout(vbox)
# radiobutton = QRadioButton("Radiobutton 1")
# vbox.addWidget(radiobutton)
textEdit_input = QTextEdit()
vbox.addWidget(textEdit_input)
textEdit_output = QTextEdit()
vbox.addWidget(textEdit_output)
The point where I struggle now is that I want to load a txt file and the text should update the empty textEdit_input in my second tab. Because the function should work for multiple tabs I don't want to attach it to my SecondTab class.
How can I properly address QTextEdit in my second tab?
Thanks for the input musicamante, changed the code according to your suggestions and it works.
self.tab_first = FirstTab()
self.tabWidget.addTab(self.tab_first,"Tab 1")
self.tab_second = SecondTab()
self.tabWidget.addTab(self.tab_second,"Tab 2")
and with self.tab_first.textEdit_input I can now access the needed fields.

How to separate between python generated ui and our apps logic in pyqt5? [duplicate]

I'm using Qt Designer for design GUI to use in python, after designing my desired UI in Qt Designer, convert it to python code and then I changed generated code to do some action in my python code, but if I changed the UI with Qt Designer and convert it to python code again, I lost my previous changes on my code.
how can I solve the problem?
can we Spreading a Class Over Multiple Files in python to write code in other files?
To avoid having these problems it is advisable not to modify this file but to create a new file where we implement a class that uses that design.
For example, suppose you have used the MainWindow template in the design.ui file, then convert it to Ui_Design.py like to the following structure:
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
[...]
def retranslateUi(self, MainWindow):
[...]
Then we will create a new file that we will call logic.py where we will create the file that handles the logic and that uses the previous design:
class Logic(QMainWindow, Ui_MainWindow):
def __init__(self, *args, **kwargs):
QMainWindow.__init__(self, *args, **kwargs)
self.setupUi(self)
So even if you modify the design and generate the file again .py you will not have to modify the file of the logic.
To generalize the idea we must have the following rules but for this the logic class must have the following structure:
class Logic(PyQtClass, DesignClass):
def __init__(self, *args, **kwargs):
PyQtClass.__init__(self, *args, **kwargs)
self.setupUi(self)
PyQtClass: This class depends on the design chosen.
Template PyQtClass
─────────────────────────────────────────────
Main Window QMainWindow
Widget QWidget
Dialog with Buttons Bottom QDialog
Dialog with Buttons Right QDialog
Dialog with Without Buttons QDialog
DesignClass: The name of the class that appears in your design.
The advantage of this implementation is that you can implement all the logic since it is a widget, for example we will implement the solution closing pyqt messageBox with closeevent of the parent window :
class Logic(QMainWindow, Ui_MainWindow):
def __init__(self, *args, **kwargs):
QMainWindow.__init__(self, *args, **kwargs)
self.setupUi(self)
def closeEvent(self, event):
answer = QtWidgets.QMessageBox.question(
self,
'Are you sure you want to quit ?',
'Task is in progress !',
QtWidgets.QMessageBox.Yes,
QtWidgets.QMessageBox.No)
if answer == QtWidgets.QMessageBox.Yes:
event.accept()
else:
event.ignore()
The easiest way is to use the *.ui file directly in the python code, you don't need convert to *.py file every time you change the ui.
you can use this pseudo code in your project.
# imports
from PyQt5 import uic
# load ui file
baseUIClass, baseUIWidget = uic.loadUiType("MainGui.ui")
# use loaded ui file in the logic class
class Logic(baseUIWidget, baseUIClass):
def __init__(self, parent=None):
super(Logic, self).__init__(parent)
self.setupUi(self)
.
.
.
.
def main():
app = QtWidgets.QApplication(sys.argv)
ui = Logic(None)
ui.showMaximized()
sys.exit(app.exec_())

How to set the order of widgets in QGridLayout

I am trying to put a QPushButton on top of a PlotWidget. My current code always has the plot widget on top of the button. How can I bring the button to the front of the QGridLayout?
Or is there a better way in PyQt5 to overlay widgets?
I have tried using .raise__() and .lower__() neither worked as expected.
As far as I found there is no way in PyQt5 to set a z value
Changing the order of adding the widgets to the QGridLayout had no effect
The PlotWindow class is used in a Stacked widget in a Window controller class as well as some other classes
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.central_widget = QtWidgets.QStackedWidget()
self.setCentralWidget(self.central_widget)
self.plot = PlotWindow(self)
self.central_widget.addWidget(self.plot)
self.central_widget.setCurrentWidget(self.plot)
self.show()
class PlotWindow(QtWidgets.QWidget):
def __init__(self, parent=None):
super(PlotWindow, self).__init__(parent)
plot_frame = pyqtgraph.PlotWidget()
self.connect_btn = QtWidgets.QPushButton("Connect", self)
plot_wrapper_layout = QtWidgets.QGridLayout()
plot_wrapper_layout.addWidget(plot_frame, 0, 0, 12, 12)
plot_wrapper_layout.addWidget(self.connect_btn, 11, 11, 1, 1)
self.setLayout(plot_wrapper_layout)
Expecting: Button to be visible on top of the graph in the bottom right corner
Result: The connect button is hidden behind the plot widget
plot_frame = pyqtgraph.PlotWidget(self). That puts the graph widget before the push button in PlotWindow.children() which changes the order in which they are rendered.
Heikie's comment fixed the issue

Matplot lib does not plot with PyQt

I'm having a problem with PyQt and Mathplotlib.
Here you can find a pseudocode of what I am doing: I have a class "MainWindow" that creates a main window with a menu and an empty mathplotlib graph. When I click on the menu Item, the method "Select" is executed that opens a new Dialog. There is also a method that plots on the grah the content of the global variable Data.
import TeraGui
Data = []
class MainWindow(QMainWindow, TeraGui.Ui_MainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.setupUi(self)
self.actionSelect.triggered.connect(self.Select)
# Create the frame with the graphs
self.create_main_frame()
#Plot empty graphs
self.axes.clear()
self.canvas.draw()
def create_main_frame(self):
self.main_frame = QWidget()
# Create the mpl Figure and FigCanvas objects.
# 5x4 inches, 100 dots-per-inch
#
self.dpi = 100
self.fig = Figure((5.0, 4.0), dpi=self.dpi)
self.canvas = FigureCanvas(self.fig)
self.canvas.setParent(self.main_frame)
#
self.axes = self.fig.add_subplot(111)
# Create the navigation toolbar, tied to the canvas
#
self.mpl_toolbar = NavigationToolbar(self.canvas, self.main_frame)
#
# Layout with box sizers
#
vbox = QVBoxLayout()
vbox.addWidget(self.canvas)
vbox.addWidget(self.mpl_toolbar)
self.main_frame.setLayout(vbox)
self.setCentralWidget(self.main_frame)
def Plotting(self):
""" Redraws the figure
"""
print "I am here"
time = Data[0]
sig = Data[]
plot(time, sig)
# clear the axes and redraw the plot anew
#
self.axes.clear()
self.axes.plot(time, sig)
self.canvas.draw()
def Select(self):
dialog = Dialog(self)
dialog.exec_()
Now, if I add in the init method of the MainWindow class these lines:
Global Data
Data = [[1,2,3],[4,5,6]]
self.Plotting()
"I am here" is printed and the plot is correctly displayed into the graph, BUT if I don't add these lines and i try to call Plotting from the Dialog class it doesn't work. "I am here" is plotted but the plot stays empty. In the Dialog class, method "accept" is caled when the "ok" button of a button box is pressed:
class Dialog(QDialog, TeraGui.Ui_SelectFiles):
def __init__(self, parent=None):
super(Dialog, self).__init__(parent)
self.setAttribute(Qt.WA_DeleteOnClose)
self.setupUi(self)
def accept(self):
global Data
Data = [[1,2,3],[4,5,6]]
MainWindow().Plotting()
The Plotting method draws also a separate plot by means of the command "plot(time,sig)". This plot is always showed correctly regardless the way used to call Plotting.
These are my fist tries with PyQt and matplotlib and I am not able to identify the mistake.
The problem is with the line
MainWindow().Plotting()
When you write MainWindow() you are actually creating a new instance of the MainWindow class and calling its Plotting() function, not the one of your existing MainWindow instance. This window is never shown, and since you don't save a reference to it, is subsequently deleted when accept() returns. The only evidence of its existence is the 'i am here' message it writes to the console. This is why you don't see the plot.
In this case, you are setting your MainWindow instance as the parent of dialog through dialog = Dialog(self), so you could access it though a call to parent().
self.parent().Plotting()
You should also consider adding another parameter to the Plotting() function so you can pass your data directly to it instead of having to declare globals everywhere.
def Plotting(self, Data):
...