I want to make a GUI with QtDesigner.
There is a QFrame for the PyVista plot but the plot doesn't fill the full frame.
Here is my code:
from PySide6.QtWidgets import QApplication, QMainWindow
from QT_Designer.Validation import Ui_MainWindow
from pyvistaqt import QtInteractor, MainWindow
class Frm_main(QMainWindow, Ui_MainWindow):
def __init__(self):
super().__init__()
self.setupUi(self)
# Window (Plotter or Frame)
self.plotter = QtInteractor(self.f_window)
I tried to set the plotter to full_screen or change the layout of the frame but neither fixes the issue.
I looked at a view examples and find now a solution:
from PySide6.QtWidgets import QApplication, QMainWindow, QVBoxLayout
from QT_Designer.Validation import Ui_MainWindow
from pyvistaqt import QtInteractor, MainWindow
class Frm_main(QMainWindow, Ui_MainWindow):
def __init__(self):
super().__init__()
self.setupUi(self)
vlayout = QVBoxLayout()
# Window (Plotter or Frame)
self.plotter = QtInteractor(self.f_window)
vlayout.addWidget(self.plotter.interactor)
self.f_window.setLayout(vlayout)
Now the plotter is in the full frame :)
Related
I have in my application a button which after being clicked opens up another window (which is a separate python file).
I want to execute a function after this second window has been closed. Is there a way I can capture that window's 'closed' signal or something like that?
Here is my code: (the main window)
from PyQt5 import QtWidgets, QtCore
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QPainter, QColor
from PyQt5.QtWidgets import QApplication, QWidget, QHBoxLayout, QVBoxLayout
from PyQt5.QtWidgets import QPushButton
import second_dialog # the second window I am importing
from sys import exit as sysExit
class Marker(QWidget):
def __init__(self):
QWidget.__init__(self)
self.resize(350, 250)
self.openbtn = QPushButton('open')
self.openbtn.clicked.connect(self.open_dialog)
self.label_1 = QtWidgets.QLabel()
self.label_1.setGeometry(QtCore.QRect(10, 20, 150, 31))
self.label_1.setObjectName("label_1")
self.label_1.setText("HEy")
HBox = QHBoxLayout()
HBox.addWidget(self.openbtn)
HBox.addWidget(self.label_1)
HBox.addStretch(1)
VBox = QVBoxLayout()
VBox.addLayout(HBox)
VBox.addStretch(1)
self.setLayout(VBox)
def open_dialog(self):
self.dialog = QtWidgets.QWidget()
self.box = second_dialog.Marker()
self.box.show()
def do_something(self):
self.label_1.setText("Closed")
def paintEvent(self, event):
p = QPainter(self)
p.fillRect(self.rect(), QColor(128, 128, 128, 128))
if __name__ == "__main__":
MainEventThred = QApplication([])
MainApp = Marker()
MainApp.show()
MainEventThred.exec()
Here is the code for the second window:
from PyQt5 import QtWidgets
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QPainter, QColor
from PyQt5.QtWidgets import QApplication, QWidget, QHBoxLayout, QVBoxLayout
from PyQt5.QtWidgets import QPushButton
import first_window
from sys import exit as sysExit
class Marker(QWidget):
def __init__(self):
QWidget.__init__(self)
self.resize(350, 250)
self.openbtn = QPushButton('close')
self.openbtn.clicked.connect(self.Close)
HBox = QHBoxLayout()
HBox.addWidget(self.openbtn)
HBox.addStretch(1)
VBox = QVBoxLayout()
VBox.addLayout(HBox)
VBox.addStretch(1)
self.setLayout(VBox)
def Close(self):
TEST_draggable.Marker.do_something(first_window.Marker)
self.close()
def paintEvent(self, event):
p = QPainter(self)
p.fillRect(self.rect(), QColor(128, 128, 128, 128))
if __name__ == "__main__":
MainEventThred = QApplication([])
MainApp = Marker()
MainApp.show()
MainEventThred.exec()
The code does not run and throws this error:
Traceback (most recent call last):
File "some/path/second_dialog.py", line 28, in Close
TEST_draggable.Marker.do_something(first_window.Marker)
File "some/path/first_window.py", line 39, in do_something
self.label_1.setText("clicked")
AttributeError: type object 'Marker' has no attribute 'label_1'
[1] 7552 abort (core dumped) /usr/local/bin/python3
How can I fix this? I've looked up a lot of forums suspecting circular imports as the culprit but I'm not sure. Please help.
The problem has nothing to do with imports, but with the fact that you're trying to run an instance method on a class.
The "culprit" is here:
TEST_draggable.Marker.do_something(first_window.Marker)
first_window.Marker will be used for the self argument in do_something, but since it is the class, it has no label_1 attribute (only the instances of that class have that attribute). I suggest you to do some research on how classes and instances work in general, and how to deal with them in Python.
The easiest solution is to create your own signal for the second window and connect that signal on the function that will "do_something". Instead of connecting to a new function, then, we subclass the closeEvent() and send the signal from there, so that we can capture the close even when the user clicks the dedicated button on the title bar. Note that if you want to change the behavior of close you can just override it and then call the base implementation (super().close()) instead of using another function name (which should not have a capitalized name, by the way, as they should only be used for classes and constants).
This is the second class; be aware that using identical names for different classes is a very bad idea.
from PyQt5.QtCore import pyqtSignal
class Marker(QWidget):
closed = pyqtSignal()
def __init__(self):
QWidget.__init__(self)
self.resize(350, 250)
self.openbtn = QPushButton('close')
self.openbtn.clicked.connect(self.close)
# ...
def closeEvent(self, event):
self.closed.emit()
Then, in the first:
class Marker(QWidget):
# ...
def open_dialog(self):
self.dialog = QtWidgets.QWidget()
self.box = second_dialog.Marker()
self.box.closed.connect(self.do_something)
self.box.show()
My code was created with PyQt4 and I want to convert it to PyQt5.
I have tried some scripts to convert the code; but, nothing changed except the name.
What do I need to change manually in order to make the code work with PyQt5?
Here is the first part of my code:
import sys
from pymaxwell import *
from numpy import *
from PyQt4 import QtGui, QtCore, uic
from PyQt4.QtGui import QMainWindow, QApplication
from PyQt4.QtCore import *
from PyQt4.phonon import Phonon
from ffmpy import FFmpeg
import os
import app_window_dark
import about
uifile = 'Ui/app_window_dark.ui'
aboutfile = 'Ui/about.ui'
Ui_MainWindow, QtBaseClass = uic.loadUiType(uifile)
Ui_Dialog= uic.loadUiType(uifile)
class About(QtGui.QMainWindow, about.Ui_Dialog):
def __init__(self, parent=None):
super(About, self).__init__()
QtGui.QMainWindow.__init__(self, parent)
Ui_Dialog.__init__(self)
self.setWindowModality(QtCore.Qt.ApplicationModal)
point = parent.rect().bottomRight()
global_point = parent.mapToGlobal(point)
self.move(global_point - QPoint(395, 265))
self.setupUi(self)
class MyApp(QtGui.QMainWindow, app_window_dark.Ui_MainWindow):
def __init__(self):
super(MyApp, self).__init__()
QtGui.QMainWindow.__init__(self)
self.ui = Ui_MainWindow.__init__(self)
self.setupUi(self)
self.about_btn.clicked.connect(self.popup)
#prev next
self.btn_next.clicked.connect(self.renderSet)
self.btn_prev.clicked.connect(self.renderSet)
and also this code:
if __name__ == "__main__":
app = QApplication(sys.argv)
#style = QApplication.setStyle('plastique')
window = MyApp()
window.setFixedSize(750, 320)
window.show()
sys.exit(app.exec_())
The main change from Qt4 to Qt5 and hence from PyQt4 to PyQt5 is the rearrangement of certain classes so that the Qt project is scalable and generates a smaller executable.
The QtGui library was divided into 2 submodules: QtGui and QtWidgets, in the second only the widgets, namely QMainWindow, QPushButton, etc. And that is the change you must make:
[...]
from PyQt5 import QtGui, QtCore, uic, QtWidgets
from PyQt5.QtWidgets import QMainWindow, QApplication
from PyQt5.QtCore import *
[...]
Ui_MainWindow, QtBaseClass = uic.loadUiType(uifile)
Ui_Dialog= uic.loadUiType(uifile)
class About(QtWidgets.QMainWindow, about.Ui_Dialog):
def __init__(self, parent=None):
QtWidgets.QMainWindow.__init__(self, parent)
self.setupUi(self)
self.setWindowModality(QtCore.Qt.ApplicationModal)
point = parent.rect().bottomRight()
global_point = parent.mapToGlobal(point)
self.move(global_point - QPoint(395, 265))
class MyApp(QtWidgets.QMainWindow, app_window_dark.Ui_MainWindow):
def __init__(self):
QtWidgets.QMainWindow.__init__(self)
self.setupUi(self)
self.about_btn.clicked.connect(self.popup)
#prev next
self.btn_next.clicked.connect(self.renderSet)
self.btn_prev.clicked.connect(self.renderSet)
Note: Phonon does not exist in PyQt5, you must use QtMultimedia, an accurate solution you can find it in the following answer: Phonon class not present in PyQt5
I want to write a notepad in python with PyQt5 for persian language.
how can I align the thext in QPlainTextEdit to right?
this is my code:
from PyQt5.QtWidgets import QApplication, QMainWindow, QPlainTextEdit
from PyQt5.QtCore import Qt, QLocale
class TextBox(QPlainTextEdit):
def persian(self):
self.setFixedSize(640, 480)
self.setLayoutDirection(Qt.RightToLeft)
self.setLocale(QLocale(QLocale.Persian, QLocale.Iran))
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.GUI()
def GUI(self):
self.setWindowTitle("My title")
self.setFixedSize(640, 480)
self.text = TextBox(self)
self.text.persian()
app = QApplication([])
window = MainWindow()
window.show()
app.exec_()
You can use a QTextEdit instead of QPlainTextEdit and use setAlignment(Qt.AlignRight), e.g.
from PyQt5.QtWidgets import QTextEdit
from PyQt5.QtCore import Qt
class TextBox(QTextEdit):
def persian(self):
self.setFixedSize(640, 480)
self.setLayoutDirection(Qt.RightToLeft)
self.setLocale(QLocale(QLocale.Persian, QLocale.Iran))
# set text alignment to AlignRight
self.setAlignment(Qt.AlignRight)
This example draws an ellipse in the main window. I would like to draw the ellipse in the secondary window as soon as I click the pushbutton in the main window. This, however, without having to create another class. I tried but I can't.
Another question: Is it possible to insert a geometric figure in a Box Layout?
#!/usr/bin/python3.6
# -*- coding: utf-8 -*-
import sys
from PyQt5.QtWidgets import QMainWindow, QApplication, QPushButton, QDialog
from PyQt5 import QtGui
from PyQt5.QtGui import QPainter,QBrush,QPen
from PyQt5 import QtCore
class Window(QMainWindow):
def __init__(self):
super().__init__()
title = "Main Window";left=100;top=100;width=1200;height=800
self.setWindowTitle(title)
self.setGeometry(left,top,width,height)
self.Create_Button()
self.show()
def Create_Button(self):
button = QPushButton("Click", self)
button.setGeometry(30,100,200,80)
button.clicked.connect(self.SecondWin)
def paintEvent(self,event):
painter = QPainter(self)
painter.setPen(QPen(QtCore.Qt.red,6,QtCore.Qt.SolidLine))
painter.setBrush(QBrush(QtGui.QColor(45,171,200),QtCore.Qt.SolidPattern))
painter.drawEllipse(550,150,300,600)
def SecondWin(self,event):
mydialog = QDialog(self)
mydialog.setWindowTitle("Second Window")
mydialog.setGeometry(400,200,600,300)
mydialog.show()
App = QApplication(sys.argv)
window = Window()
sys.exit(App.exec_())
I am trying to muddle my way through embedding a matplotlib figure inside of a Qt GUI created using Qt Designer. I am already able to create the figure I want just through Python. I've created a basic GUI with widgets to allow me to select/load input files, and plot the data in those files in a matplotlib figure that is embedded in the GUI. I accomplish this by adding a blank widget to the GUI called plotwidget, and then calling the GraphInit class with this widget as input.
The problem I am currently facing is that while my plot shows up and updates as desired inside the GUI, it doesn't seem to pay attention to the size policy defined for it in either my Python code (where the FigureCanvas is created) or for the plotWidget widget in Qt Designer. I have been using this demo, among others, as a starting point for this project. However, all of these examples have generated the GUI entirely from within Python. I suspect I am doing something wrong in assigning the matplotlib figure to the widget, but I haven't been able to figure out what.
Code (import & plotting code removed, but the core where figure is added is there):
import sys
import os
import matplotlib
matplotlib.use('Qt5Agg')
from PyQt5 import QtCore, QtGui
from PyQt5.QtWidgets import QApplication, QMessageBox, QFileDialog, QPushButton, QLabel, QRadioButton, QDoubleSpinBox, QSpinBox, QWidget, QSizePolicy, QMainWindow
import PyQt5.uic
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
import json
from datetime import datetime
import numpy as np
class LogViewer(QApplication):
def __init__(self):
QApplication.__init__(self, sys.argv)
self.ui = UI()
#staticmethod
def main():
vwr = LogViewer()
vwr.run()
def run(self):
self.ui.win.show() # Show the UI
self.ui.run() # Execute the UI run script
self.exec_()
class Graph_init(FigureCanvas):
def __init__(self, parent=None):
fig = Figure()
self.axes = fig.add_subplot(111)
self.compute_initial_figure()
self.axes.grid()
FigureCanvas.__init__(self, fig)
self.setParent(parent)
FigureCanvas.setSizePolicy(self, QSizePolicy.Expanding, QSizePolicy.Expanding)
FigureCanvas.updateGeometry(self)
def compute_initial_figure(self):
pass
class UI(object):
def __init__(self):
ui_path = os.path.dirname(os.path.realpath(__file__))
self.win = PyQt5.uic.loadUi(ui_path + '\\logview.ui')
self.filename = ui_path + '\\test.txt'
self.plotWin = Graph_init(self.win.plotWidget)
def run(self):
self.init_ui() # get initial values from the controllers
self.attach_ui_connections()
def init_ui(self):
w = self.win
w.txtLogFilename.setText(self.filename.split('/')[-1]) # just the file (no path)
def attach_ui_connections(self):
w = self.win
if __name__ == '__main__':
LogViewer.main()
GUI is available here. Any suggestions appreciated!
I checked your ui file and plotWidget has no layout at all. Try to give it one, I suggest a grid layout.
Then, after parenting the canvas
self.setParent(parent)
try adding it to the parent layout:
parent.layout().addWidget(self)