PyQtGraph plot incorrectly displayed on Qt MainWindow - qt

So I'm trying to get PyQtGraph to plot a graph in a GUI that I am developing (you can check it here https://github.com/allg18/MuonAquisitionTool , I am using Python3 and PyQt4).
However, when I add the plotwidget to the groupbox in the MainWindow, it always stays tiny. The plotgraph didn't expand to ocuppy the whole of the groupbox.
class MainWindow2(QMainWindow, Ui_MainWindow):
def __init__(self):
super(MainWindow2, self).__init__()
# Set up the user interface from Designer.
self.setupUi(self)
"""Booting up the Graphics"""
self.preview_plot = pg.PlotWidget(self.groupBox_2)
self.preview_plot.adjustSize()
self.data1 = numpy.random.normal(size=300)
self.curve1 = self.preview_plot.plot(self.data1)
In which the groupBox_2 is the place where I want to put it within the MainWindow.
As you can see I also tried to put a "adjust_size" there. I also tried:
self.preview_plot.resize(500, 500)
And this actually worked, it was the only way I got a size different from the one in the image
UPDATE:
I tried the adding the following line, but this did again not change the size of the graph widget...
self.graph.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)

Don't try to position the widgets yourself, this is hard and error prone. Qt has a layout mechanism to do this for you. You can read about it here
I cannot fix your example because it is not complete, I don't have the QMainWindow and Ui_MainWindow classes (in the future please make an MCVE). However, there is probably some groupbox layout to which you can add your plot widget with addWidget.

Related

How to hide unexpected Python "splash" window that displays before my MainWindow in pyside6

Before my QMainWindow appears on screen, a smaller window titled "Python" opens up for about half a second.
I tried adding self.hide() at the top of the MainWindow init (just after super().init()), but that had no effect. I also tried covering it up with a QSplashScreen, but the window appears on top of the splash screen. Is there any way to prevent this unexpected "Python" window from appearing? Unexpected "Python" window screenshot
Here's my main file splashtest.py:
import sys
from modules.gui.main_window import *
def main():
app = QApplication(sys.argv)
#pixmap = QPixmap('./breadware-splash.png')
#splash = QSplashScreen(pixmap)
#splash.show()
window = Window()
#splash.finish(window)
app.exec()
if __name__ == "__main__":
main()
Here is main_window.py:
from PySide6.QtWidgets import QApplication, QMainWindow, QTabWidget, QSplashScreen
from PySide6.QtGui import QPixmap
from PySide6.QtCore import Qt, QSize, QThread, QObject, Signal
from modules.gui.test_tab import *
class Window(QMainWindow):
def __init__(self):
parent = super().__init__()
self.resize(800, 600)
self.setWindowTitle('Breadware Test')
# Create a tab widget
self.tw = QTabWidget()
self.testtab = self.tw.addTab(TestTab(self), "Test Device")
self.tw.show()
self.setCentralWidget(self.tw)
self.show()
And here is test_tab.py:
from PySide6.QtWidgets import QWidget, QTextEdit, QGridLayout
class TestTab(QWidget):
def __init__(self, parent):
super().__init__()
self.parent = parent
grid = QGridLayout()
self.infoarea = QTextEdit(self)
self.infoarea.setReadOnly(True)
self.infoarea.setStyleSheet("background-color: black; border: 0;")
grid.addWidget(self.infoarea, 5, 1, 1, 2)
self.setLayout(grid)
self.show()
self.infoarea.insertHtml("<span style=\"font-size:12pt; font-weight:800; color:white;\" >Hello There</span><br>")
tl;dr
Remove self.tw.show() from Window and self.show() in TestTab.
Explanation
As a rule of thumb, self.show() should never be called in the __init__() of a widget, especially for widgets that are going to be "reparented" (it becomes a child of another widget).
In your case, you are doing that mistake twice:
the first time, implicitly, in the __init__() of TestTab as written above;
then, again, in the __init__() of Window, before setting it as a central widget, which has the same result, because you are showing it before reparenting it;
Note: calling show() (just like setVisible(True)) more than once within the same function call does not change the result; with your current code, even using just one of the calls above would have created the issue, as you experience showed, based on your comment); such calls are not immediate, the create an extended queue of events that eventually result in what the user can see and interact with on the screen. If the widget is already visible, any further call is a no-op, unless another change in the widget status has happened in the meantime, such as changing the window flags.
The reason is simple: if a widget is created without a parent, it is always considered a top level one (a window). If a widget that has no parent calls self.show(), it will show itself as a separate window.
Your code would not have created the issue if:
you did not call self.tw.show() before self.setCentralWidget();
most importantly, you properly used the parent argument of __init__() by calling super().__init__(parent) (because you used TestTab(self));
Calling show() on a child widget will (obviously) not show it if the parent is not shown, it will only "mark" it as visible and make it as such as soon as the parent is shown. This is extremely important for child widgets created after the parent is being shown: in that case, calling show() is mandatory (unless the widget is added to the layout manager of the parent, see below).
This means that the only cases for which calling self.show() in the init is considerable acceptable is:
when you are completely sure that the widget will always be a top level window;
when the parent argument is properly used in the super call and the parent is actually valid:
class ProperShowCallWidget(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
if parent is not None:
self.show()
The above will make the widget automatically visible when it's created with a parent, even if it is not going to be added to a layout manager.
Be aware that, actually, you should always use layouts managers (unless you really know what you are doing), so calling show() is pointless, as a child widget is implicitly shown when added to a widget layout, unless setVisible(False) (or hide()) is explicitly called.
So, not only you should remove those show() calls as written at the beginning of this post, but you should also remove self.show() in the __init__() of Window and move it into your "main" code:
def main():
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec()) # <- you should use sys.exit()
Do not underestimate the documentation
I strongly advise you to take your time to carefully study the related documentation for:
the visible property;
isHidden() (which is not the same as not isVisible());
isVisibleTo();
window() and isWindow();
the Qt.WindowFlags (notably, Qt.Window, automatically set for any widget created without a parent);
parentWidget(), QObject.parent() and the concepts behind Qt object trees and ownership;
the whole documentation about QWidget and the inherited QObject type, starting from their detailed description, going through all their functions, and then doing the same for all the related classes used by those functions;
Finally, consider that there are always exceptions. There are widgets that, even if created with a parent, are still shown as top level widgets. The most common case is QDialog (and all its subclasses, such as QMessageBox), which is always considered a top level window by default, even if it has a parent widget. That is because they are modal windows, and the parent widget is used as a reference to know to what window they are considered modal (blocking any input to those windows until the dialog is closed). Other similar cases are: menus, tooltips, floating tool bars/dock widgets and splash screens (see the Qt.WindowFlags above: they all use a window flag that uses the Qt.Window enum).

Pyqtgraph ImageView problem: ImageItem gets displayed outside of visible space of ViewBox

I am working on a GUI that at one point should display a long horizontal ROI of a camera feed. For that I am writing a widget consisting of an ImageView that is using a ViewBox and an ImageItem and then add that widget to a constrained space on a layout of the GUI. My problem is that the image from the camera is not visible upon opening the window but 'hidden' outside of the field of view of the widget (a little bit down). Upon resizing the window, it appears centered.
This is how it looks after resizing before that it is where the arrow indicates
This is a minimal version of the display widget in question:
class CameraViewerWidget(QWidget):
def __init__(self, parent=None):
super().__init__(parent=parent)
self.viewport = GraphicsLayoutWidget()
self.view = self.viewport.addViewBox(lockAspect=False, enableMenu=True)
self.img = pg.ImageItem()
self.view.addItem(self.img)
self.imv = pg.ImageView(view=self.view, imageItem=self.img)
layout = self.layout()
layout.addWidget(self.imv)
def update_image(self, image, auto_range=True, auto_histogram_range=False):
self.imv.setImage(image, autoRange=auto_range, autoHistogramRange=auto_histogram_range)
It is then added to the GUI via addWidget() and update_image() is called repeatedly using a QTimer.
Obviously the auto_range argument does not help here. I also tried to use the setLimits() and setRange() methods of the ViewBox but that also did not change the situation. I am suspecting that as I add this Widget to a contrained size on the layout of the main GUI (maximumSize = 100) the ImageView does not know how much space it has but I am not sure how to test that and a bit confused by the different coordinate systems used here. Did anyone encounter similar issues or is able to see my error here?

How to vertically center a single-line in a QTextEdit instance (PySide/PyQt)?

I have a line editor that inherits from QTextEdit, and I am using it to edit view items that show rich text. The second parameter for QTextEdit.setAlignment is `QtAligntment' and the docs say:
Valid alignments are Qt.AlignLeft, Qt.AlignRight, Qt.AlignJustify and
Qt.AlignCenter (which centers horizontally).
That is, there is no native support for vertical alignment. Is there an indirect way to vertically center the text in QTextEdit?
Related link
Center the Text of QTextEdit horizontally and vertically : Unfortunately, the accepted answer uses QLineEdit which won't work for me.
A clue?
At the following I found a clue about how to do it in C++/Qt. I am almost able to follow it, but not quite, as it is for c++:
http://www.qtcentre.org/threads/26003-Vertical-centering-of-a-QTextEdit
I will hack at it on my own for a couple of days and try to answer it myself, but wanted to post this now in case someone has already cracked it already or done it in a different/better way.
For a single-line edit centred vertically, you just need to calculate a correct fixed height.
Using the example delegate from your previous question, it can be achieved like this:
class RichTextLineEdit(QtGui.QTextEdit):
def __init__(self, parent=None):
...
margin = 1
self.document().setDocumentMargin(margin)
fontMetrics = QtGui.QFontMetrics(self.font())
height = fontMetrics.height() + (margin + self.frameWidth()) * 2
self.setFixedHeight(height)
(NB: the reimplemented sizeHint and minimumSizeHint methods are probably redundant in the original example).
While the accepted answer works for default font size, it breaks when I change the font size or vertical margins (see comments). The text line edit class below centers the text vertically, for all font sizes and vertical margins that I've tested.
It sets up the editor using QTextDocument which is then assigned to the QTextEdit instance. QTextDocuments provide the back-end containers for QTextEdits anyway, and have built-in functionality for handling font sizes and margins, and give an additional layer of control over the editor.
In practice, I found using QTextDocument let me solve the problem in a more intuitive way without having, you don't have to delve into the nitty-gritty mechanics of frame widths, font metrics, and all that, which we did when working solely using native QTextEdit methods.
Note it uses setViewportMargins() instead of setContentMargins() (which is what you might expect it to use) because the latter is for setting margins for something that is inserted into a layout. The following editor is a standalone widget, not put into any layout, so setContentMargins() won't do anything.
import sys
from PySide import QtGui, QtCore
class TextLineEdit(QtGui.QTextEdit):
topMarginCorrection = -4 #not sure why needed
returnPressed = QtCore.Signal()
def __init__(self, fontSize = 10, verticalMargin = 2, parent = None):
QtGui.QTextEdit.__init__(self, parent)
self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
self.setLineWrapMode(QtGui.QTextEdit.NoWrap)
self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.setFontPointSize(fontSize)
self.setViewportMargins(-verticalMargin, self.topMarginCorrection , 0, 0) #left, top, right, bottom
#Set up document with appropriate margins and font
document = QtGui.QTextDocument()
currentFont = self.currentFont()
currentFont.setPointSize(fontSize)
document.setDefaultFont(currentFont)
document.setDocumentMargin(verticalMargin)
self.setFixedHeight(document.size().height())
self.setDocument(document)
def keyPressEvent(self, event):
'''stops retun from returning newline'''
if event.key() in (QtCore.Qt.Key_Enter, QtCore.Qt.Key_Return):
self.returnPressed.emit()
event.accept()
else:
QtGui.QTextEdit.keyPressEvent(self, event)
def main():
app = QtGui.QApplication(sys.argv)
myLine = TextLineEdit(fontSize = 15, verticalMargin = 8)
myLine.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()

QGraphicsView / QGraphicsScene size matching

How do you make a QGraphicsScene with a specified size and a QGraphicsView to monitor that scene with the same size?
This sounds like a stupid question but consider the following test code [1]
import sys
from PyQt4 import QtGui, QtCore
class MainWindow(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.scene = QtGui.QGraphicsScene(0, 0, 200, 200, self)
self.view = QtGui.QGraphicsView(self.scene, self)
#self.view.setMaximumSize(200, 200)
self.setCentralWidget(self.view)
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
win = MainWindow()
win.show()
sys.exit(app.exec_())
When I run this I get a little window with the QGraphicsView and no scroll bars. If I shrink the window scroll bars appear because I've made the view smaller than the specified size of the scene. If I enlarge the window the bars go away and the view resizes with the window. All of this makes sense.
Now I run the same code again but with the commented line (preceded by #) un-commented. The Window appears with the view inside, but the view has scroll bars. When I shrink the window the view also shrinks, but when I enlarge the window the view only enlarges to a certain size. This is not surprising. What is surprising is that with the view at it's maximum size, the scroll bars are still there. I don't understand this because the maximum size of the view is explicitly matched to the size of the scene in the code.
Why does this happen?
I am aware that other questions have explained how to force the scene and view to fit together but I want to understand what size view and scene I have actually made, hence my attempt to use explicitly specified sizes.
[1] I'm using PyQt, C++ users just read "self" as "this" and "import" as "#include"
EDIT: The accepted answer below is absolutely correct. I would just like to add for others who read this that if your view is in a layout you have to account for the layout margins as well. These can be explicitly set in the QtDesigner etc.
Ok, I worked it out for you!
The QGraphicsView (which subclasses QFrame) has a border. If you add the below line to your init method:
self.view.setFrameShape(QtGui.QFrame.NoFrame)
then you remove the 1px frame and it works as you expect!

PyQt/Qt: How do I temporarily override the paintEvent() method for a QTextEdit? (What am I doing wrong??)

I am trying to temporarily display a QWebPage in the viewport of a QTextEdit. Here is the pertinent code from my program:
class Editor(QTextEdit):
def __init__(self):
super(Editor, self).__init__()
self.webpage = QWebPage(self)
self.paintwebpage = False
def showWebPage(self, html):
self.webpage.mainFrame().setHtml(html)
self.paintwebpage = True
self.update()
def hideWebPage(self):
self.paintwebpage = False
self.update()
def paintEvent(self, event):
if self.paintwebpage:
self.webpage.setViewportSize(self.viewport().size())
painter = QPainter(self.viewport())
self.webpage.mainFrame().render(painter)
painter.end()
else:
super(Editor, self).paintEvent(event)
And it never works. I have tried many variations, including telling it exactly the rectangle to paint, or first painting to an image and then painting the image, etc. It always displays maybe a line or two of the webpage, in strange positions in the viewport. Why can't I get it to overwrite the entire viewport from 0,0? Where am I going wrong?
[I want to display a preview of html code from the editor, without the need for multiple space taking widgets; for example, while the user holds down a certain button, the view will temporarily flip to the preview of the html near the cursor.]
Rather than overloading the paintEvent, you could use a QStackedWidget and just swap the widgets over.
This may be a simpler solution to the problem, as it is only a single call to setCurrentIndex to swap the visible widget.

Resources