QWidget does not appear in the QMainWindow using PyQt5 - python

What I want is an interface which opens windows under a SINGLE window, that is, I do NOT want the interface to open extra windows.
I will guide you to my error. When I run my code I get a home page, from there I click on View/Edit --> View/Edit Processed Slices. At this point this is what you should get in the MAIN WINDOW:
PICTURE 1
What I want the interface to do is to see the window in picture 2 whenever I click the blue rectangle. I want it to open the new widget IN THE SAME MAIN WINDOW
PICTURE 2
However, when I click it a new window opens and the previous window remains open (PICTURE 3). This is what I want to avoid, I want only 1 window not 2.
PICTURE 3
Here is the code:
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import cv2
import numpy as np
"""
MAIN WINDOW
"""
class CancerSegWindow(QMainWindow):
def __init__(self):
super().__init__()
self.title = 'Cancer Segmentation GUI '
self.initUI()
def initUI(self):
self.central = HOME()
self.setCentralWidget(self.central)
##
# ACTIONS
##
##File
#Exit
exitAct = QAction(QIcon('E:\BEATSON_PROJECT\python\GUI\exit.png'), 'Exit', self) # QAction is an abstraction for actions performed with a menubar, toolbar, or with a custom keyboard shortcut
exitAct.setShortcut('Ctrl+Q')
exitAct.setStatusTip('Exit application') # Status tip at the bottom
exitAct.triggered.connect(self.close) # Triggered signal is emitted. The signal is connected to the close() method of the QMainWindow widget.
#Home
HomeAct = QAction(QIcon('E:\BEATSON_PROJECT\python\GUI\home.png'), 'Home', self)
HomeAct.setStatusTip('Go Home')
HomeAct.triggered.connect(self.Home)
## View and Edit
# Slices
# Processed Slices
ProcessedAct = QAction('View / Edit Processed Slices', self)
ProcessedAct.triggered.connect(self.Processed_Slices)
self.statusBar().showMessage('Home') # First call statusBar creates a status bar at the bottom
# Subsequent calls return the statusbar object
##
# main MENU bar
##
menubar = self.menuBar() # create a menubar
# File
fileMenu = menubar.addMenu('File') # File (menu)
fileMenu.addAction(exitAct) # Exit
# View and Edit
veMenu = menubar.addMenu('View / Edit') # View and Edit (menu)
vesMenu = QMenu('View / Edit Slices',self) # Slices
vesMenu.addAction(ProcessedAct) # Processed
veMenu.addMenu(vesMenu)
##
# ICONS
##
toolbar = self.addToolBar('Exit')
toolbar.addAction(exitAct)
toolbarHome = self.addToolBar('Home')
toolbarHome.addAction(HomeAct)
##
# WINDOW
##
self.setGeometry(0, 30, 1366, 697)
self.setWindowTitle(self.title)
self.setWindowIcon(QIcon('E:\BEATSON_PROJECT\python\GUI\medicine.png'))
self.show()
def Home (self):
self.central = HOME()
self.setCentralWidget(self.central)
def Processed_Slices (self):
self.statusBar().showMessage('Displaying Processed Slices. Click on one Slice to View and Edit it individually')
self.central = ProcessedSlices()
self.setCentralWidget(self.central)
self.setWindowTitle(self.title + self.central.title)
def Pre_Edit_Processed (self, SliceNo=1):
self.statusBar().showMessage('Displaying Automatic Processed Slice' + str(SliceNo) + ' You can redraw it manually or modify the existing contour')
self.central = PreEditProcessed(SliceNo)
self.setCentralWidget(self.central)
self.setWindowTitle(self.title + self.central.title)
"""
HOME WINDOW
"""
class HOME (QWidget):
def __init__(self):
super().__init__()
#self.central = QPixmap("E:\BEATSON_PROJECT\python\GUI\Home_.png")
self.lbl1 = QLabel(self)
#self.lbl1.setPixmap(self.central)
"""
PROCESSED SLICES WINDOW
"""
class ProcessedSlices(QWidget):
def __init__(self):
super().__init__()
self.title = ('- Processed Slices')
self.initUI()
def initUI(self):
##
#CHECKBOXES
##
# Slice 1
#CheckBox
self.cb1 = QCheckBox('Slice 1', self)
self.cb1.move(1270, 115)
self.cb1.toggle()
self.cb1.stateChanged.connect(self.OpenSlice1)
#Pixmap (Image) 1
pixmap1 = QPixmap(310, 330) # Contour
pixmap1.fill(Qt.blue)
#pixmap1 = QPixmap("E:\BEATSON_PROJECT\python\GUI\Processed_Slice_1.png")
self.lbl1 = QLabel(self)
self.lbl1.setGeometry(QRect(QPoint(10,0),QPoint(310,330))) #
self.lbl1.setPixmap(pixmap1)
##
# SET GRID to obtain the mouse position
##
grid = QGridLayout()
self.text = "x: {0}, y: {1}".format(0, 0) # display the x and y coordinates of a mouse pointer in a label widget
self.label = QLabel(self.text, self) # x and y coordinates are displayd in a QLabel widget
grid.addWidget(self.label, 0, 1270, Qt.AlignTop)
self.setLayout(grid)
##
#WINDOW
##
#self.setGeometry(0, 25, 1365, 700)
#self.setWindowTitle('Processed Slices')
self.show()
def OpenSlice1(self, state):
self.lbl1.setVisible(state == Qt.Checked)
def mousePressEvent(self, e): # The e is the event object. it contains data about the event that was triggered
x = e.x() # in our case, a mouse CLICK
y = e.y() # x() and y() methods we determine the x and y coordinates of the mouse pointer
text = "None selected x: {0}, y: {1}"
if ( x >= 10 and x <= 310 and y >= 0 and y <= 330 and self.cb1.isChecked()):
text = "Slice 1 x: {0}, y: {1}".format(x, y)
self.close()
self.CSW = CancerSegWindow()
self.CSW.Pre_Edit_Processed(1)
self.label.setText(text)
"""
PROCESSED SLICES CHECK WINDOW
"""
class PreEditProcessed(QWidget):
def __init__(self, SliceNo=1):
super().__init__()
self.title = ('- Check Processed Slices')
self._SliceNo = SliceNo
self.initUI()
def initUI(self):
#self.draw = Draw(self)
#self.draw._windows = 1
# Button to clear both image and drawing
self.button = QPushButton('Discard and segment MANUALLY ', self)
#self.button.clicked.connect(self.editManually)
# Button to modify contour
self.BmodContour = QPushButton('Modify existing contour ', self)
#self.BmodContour.clicked.connect(self.modContour)
# Button to finish and compare
self.BFinish = QPushButton('Finish ', self)
#self.BFinish.clicked.connect(self.Finish)
# Arrange Layout
self.layout = QVBoxLayout(self)
#self.layout.addWidget(self.draw) # Show Slice
self.layout.addWidget(self.button) # Manually
self.layout.addWidget(self.BmodContour) # Modify contour
self.layout.addWidget(self.BFinish) # Finish and compare
self.setGeometry(0, 25, 1365, 700)
self.setWindowTitle('Check Slices')
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = CancerSegWindow()
sys.exit(app.exec_())
Note that the relevant part of the code is inside class ProcessedSlices:
def mousePressEvent(self, e):
x = e.x()
y = e.y()
text = "None selected x: {0}, y: {1}"
if ( x >= 10 and x <= 310 and y >= 0 and y <= 330 and self.cb1.isChecked()):
text = "Slice 1 x: {0}, y: {1}".format(x, y)
self.close()
self.CSW = CancerSegWindow()
self.CSW.Pre_Edit_Processed(1)

Your problem is that you are creating another instance of the class CancerSegWindow() in the function mousePressEvent(self, e).
The best way is use pyqtSignal().
You have to declare the pyqtSignal(int) in ProcessedSlices class:
class ProcessedSlices(QWidget):
#here declare signal
signal = pyqtSignal(int)
def __init__(self):
# more code....
And emit the signal in your mousePressEvent(self, e):
def mousePressEvent(self, e): # The e is the event object. it contains data about the event that was triggered
x = e.x() # in our case, a mouse CLICK
y = e.y() # x() and y() methods we determine the x and y coordinates of the mouse pointer
text = "None selected x: {0}, y: {1}"
if ( x >= 10 and x <= 310 and y >= 0 and y <= 330 and self.cb1.isChecked()):
text = "Slice 1 x: {0}, y: {1}".format(x, y)
self.close()
self.signal.emit(1) # emit signal with SliceNo=1
self.label.setText(text)
Finally, capture it in your Processed_Slices():
def Processed_Slices (self):
self.statusBar().showMessage('Displaying Processed Slices. Click on one Slice to View and Edit it individually')
self.central = ProcessedSlices()
self.central.signal.connect(self.Pre_Edit_Processed) #connect signal
self.setCentralWidget(self.central)
self.setWindowTitle(self.title + self.central.title)

Related

Run Method on Widget Move

I am attempting to design a label class that inherits from the PyQt5 base QLabel class that is able to track another widget. Here is the current code for my class:
class AttachedLabel(QLabel):
def __init__(self, attachedTo, *args, side="left", ** kwargs):
super().__init__(*args, **kwargs) # Run parent initialization
# Define instance variables
self.attached = attachedTo
self.side = side
# Update label position
self.updatePos()
def updatePos(self):
# Get "attached widget" position and dimensions
x = self.attached.geometry().x()
y = self.attached.geometry().y()
aWidth = self.attached.geometry().width()
aHeight = self.attached.geometry().height()
# Get own dimensions
width = self.geometry().width()
height = self.geometry().height()
if self.side == "top": # Above of attached widget
self.setGeometry(x, y-height, width, height)
elif self.side == "bottom": # Below attached widget
self.setGeometry(x, y+height+aHeight, width, height)
elif self.side == "right": # Right of attached widget
self.setGeometry(x + width + aWidth, y, width, height)
else: # Left of attached widget
self.setGeometry(x - width, y, width, height)
I want to be able to instantiate the label like so:
AttachedLabel(self.pushButton, self.centralwidget)
where self.pushButton is the widget it is supposed to be following. The issue is that I don't know how to detect when the widget moves in order to run my updatePos() function. I would ideally only update the label position when the other widget moves, but I want to refrain from havign to add extra code to the class of the widget that is being tracked. I have tried overriding the paintEvent, but that only triggers when the object itself needs to be redrawn, so it doesn't even function as a sub-optimal solution.
Is there some built-in method I can use/override to detect when the widget moves or when the screen itself is updated?
You have to use an eventFilter intersecting the QEvent::Move event and you should also track the resize through the QEvent::Resize event.
from dataclasses import dataclass, field
import random
from PyQt5 import QtCore, QtWidgets
class GeometryTracker(QtCore.QObject):
geometryChanged = QtCore.pyqtSignal()
def __init__(self, widget):
super().__init__(widget)
self._widget = widget
self.widget.installEventFilter(self)
#property
def widget(self):
return self._widget
def eventFilter(self, source, event):
if self.widget is source and event.type() in (
QtCore.QEvent.Move,
QtCore.QEvent.Resize,
):
self.geometryChanged.emit()
return super().eventFilter(source, event)
#dataclass
class TrackerManager:
widget1: field(default_factory=QtWidgets.QWidget)
widget2: field(default_factory=QtWidgets.QWidget)
alignment: QtCore.Qt.Alignment = QtCore.Qt.AlignLeft
enabled: bool = True
valid_alignments = (
QtCore.Qt.AlignLeft,
QtCore.Qt.AlignRight,
QtCore.Qt.AlignHCenter,
QtCore.Qt.AlignTop,
QtCore.Qt.AlignBottom,
QtCore.Qt.AlignVCenter,
)
def __post_init__(self):
self._traker = GeometryTracker(self.widget1)
self._traker.geometryChanged.connect(self.update)
if not any(self.alignment & flag for flag in self.valid_alignments):
raise ValueError("alignment is not valid")
def update(self):
if not self.enabled:
return
r = self.widget1.rect()
p1 = r.center()
c1 = r.center()
if self.alignment & QtCore.Qt.AlignLeft:
p1.setX(r.left())
if self.alignment & QtCore.Qt.AlignRight:
p1.setX(r.right())
if self.alignment & QtCore.Qt.AlignTop:
p1.setY(r.top())
if self.alignment & QtCore.Qt.AlignBottom:
p1.setY(r.bottom())
p2 = self.convert_position(p1)
c2 = self.convert_position(c1)
g = self.widget2.geometry()
g.moveCenter(c2)
if self.alignment & QtCore.Qt.AlignLeft:
g.moveRight(p2.x())
if self.alignment & QtCore.Qt.AlignRight:
g.moveLeft(p2.x())
if self.alignment & QtCore.Qt.AlignTop:
g.moveBottom(p2.y())
if self.alignment & QtCore.Qt.AlignBottom:
g.moveTop(p2.y())
self.widget2.setGeometry(g)
def convert_position(self, point):
gp = self.widget1.mapToGlobal(point)
if self.widget2.isWindow():
return gp
return self.widget2.parent().mapFromGlobal(gp)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.button = QtWidgets.QPushButton("Press me", self)
self.label = QtWidgets.QLabel(
"Tracker\nLabel", self, alignment=QtCore.Qt.AlignCenter
)
self.label.setAttribute(QtCore.Qt.WA_TransparentForMouseEvents, True)
self.label.setFixedSize(200, 200)
self.label.setStyleSheet(
"background-color: salmon; border: 1px solid black; font-size: 40pt;"
)
self.resize(640, 480)
self.manager = TrackerManager(
widget1=self.button,
widget2=self.label,
alignment=QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter,
)
self.move_button()
def move_button(self):
pos = QtCore.QPoint(*random.sample(range(400), 2))
animation = QtCore.QPropertyAnimation(
targetObject=self.button,
parent=self,
propertyName=b"pos",
duration=1000,
startValue=self.button.pos(),
endValue=pos,
)
animation.finished.connect(self.move_button)
animation.start(QtCore.QAbstractAnimation.DeleteWhenStopped)
def main():
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()

How to pass changing/updating data to another pop-up window?

I got the following code for Python GUI. I found I cannot update the data running in the main window to the pop-up window.
In this example I use some random data to mimic the video frames and use a timer to update the data (the updateData() funciton).
I want to see the green curve in the pop-up window changes... But I cannot find an effective way to pass the changing data to the pop-up window.
Should I use the pyqtSignal() to define a custom signal? I read this page. Should I define the updateData() slot function as a custom signal, then connect to the other slot function plot_data() ?
"""
1/15/2021 Yuanhang Zhang
"""
from PyQt5.QtWidgets import QMainWindow,QApplication,QGridLayout,QWidget, QPushButton,QDockWidget
from PyQt5.QtCore import Qt,QTimer
from PyQt5.QtGui import QPixmap,QFont
import numpy as np
import pyqtgraph as pg
from pyqtgraph import GradientWidget
class power_window(QMainWindow):
# this window has no parent. It will appear as a free-floating window as we want.
def __init__(self,parent):
super().__init__()
self.setWindowTitle("Total power in the frame")
self.main_widget = QWidget()
self.main_layout = QGridLayout()
self.main_widget.setLayout(self.main_layout)
self.setCentralWidget(self.main_widget)
self.plot_plt = pg.PlotWidget() # this is our plot canvas
self.plot_plt.showGrid(x=True,y=True) # show grid
self.main_layout.addWidget(self.plot_plt, 1, 0, 3, 3)
# self.plot_plt.setYRange(max=100,min=0)
self.data_list = []
parent.timer.timeout.connect(self.plot_data) # update plot
# parent.updateData.change.connect(self.plot_data) # not working, may need to use pyqtSignal for custom signals
self.frame_sum_data = parent.updateData()
self.plot_data()
def plot_data(self):
self.data_list.append(self.frame_sum_data)
self.plot_plt.plot().setData(self.data_list,pen='g') # change the color of the pen
class CameraGUI(QMainWindow):
def __init__(self):
super().__init__()
## Create some random data to mimic video data
# scale: Standard deviation (spread or “width”) of the distribution.
# loc: Mean (“centre”) of the distribution.
self.data = np.random.normal(size=(15, 30, 30), loc=1024, scale=200).astype(np.uint16)
self.i = 0
self.power_gui = None # for the pop up window
self.initializeUI()
def initializeUI(self):
"""
Initialize the window and display its contents to the screen
"""
self.setGeometry(100,100,1000,800) # (x, y, width, height)
self.setWindowTitle('Camera GUI')
self.main_widget = pg.GraphicsLayoutWidget()
self.setCentralWidget(self.main_widget) # set the default widget of the MainWindow
self.createCameraWidgets()
self.show()
def updateLUT(self): ## change colormap (color look up table)
lut = self.gradient.getLookupTable(256)
return lut
def action_on_button_clicked(self): # for the pop-up windown
# https://www.learnpyqt.com/tutorials/creating-multiple-windows/
if self.power_gui is None:
# self.power_gui = power_window(np.sum(self.data[self.i]))
self.power_gui = power_window(self)
self.power_gui.show()
def createCameraWidgets(self):
"""
Setup widgets using QGridLayout
"""
self.gradient = GradientWidget(parent = self.main_widget,orientation='top')
self.gradient.sigGradientChanged.connect(self.updateLUT)
self.dock_widget = QDockWidget(self)
self.dock_widget.setAllowedAreas(Qt.AllDockWidgetAreas)
# set initial location of dock widget in main window
self.addDockWidget(Qt.TopDockWidgetArea,self.dock_widget)
self.button = QPushButton('Power',self)
self.button.clicked.connect(self.action_on_button_clicked) ## --> not updating the data ??
self.dock_widget.setWidget(self.button)
## add view box
self.view1 = self.main_widget.addViewBox(row = 1, col = 0)
self.view2 = self.main_widget.addViewBox(row = 1, col = 1)
## lock the aspect ratio so pixels are always square
self.view1.setAspectLocked(True)
self.view2.setAspectLocked(True)
## Create image item
self.img1 = pg.ImageItem(border='w')
self.img2 = pg.ImageItem(border='w')
self.view1.addItem(self.img1)
self.view2.addItem(self.img2)
# timer of the main window
self.timer = QTimer()
self.timer.setInterval(200) # every 200 ms will emit timeout signal
self.timer.timeout.connect(self.updateData) # when timeout signal is emit, it will run updatedata() function
self.timer.start()
print(np.sum(self.data[self.i])) # this will only run once
print(self.i)
def updateData(self):
## Display the data
self.img1.setImage(self.data[self.i],lut = self.updateLUT()) # when i changes, set to display in img1
self.data2 = np.log(np.abs(np.fft.fft2(self.data[self.i]))) # calculate data2 beased on data[i]
self.i = (self.i+1) % self.data.shape[0] # update i
self.img2.setImage(self.data2,lut = self.updateLUT())
# print(self.i)
print(np.sum(self.data[self.i])) # this will keep running every 200 ms
return np.sum(self.data[self.i])
## run the program
if __name__ =="__main__":
import sys
app = QApplication(sys.argv)
window = CameraGUI()
window.updateData()
sys.exit(app.exec_())
I did some changes to your power_window class:
class power_window(QMainWindow):
# this window has no parent. It will appear as a free-floating window as we want.
def __init__(self,parent):
super().__init__()
self.setWindowTitle("Total power in the frame")
self.main_widget = QWidget()
self.main_layout = QGridLayout()
self.main_widget.setLayout(self.main_layout)
self.setCentralWidget(self.main_widget)
self.plot_plt = pg.PlotWidget() # this is our plot canvas
self.plot_plt.showGrid(x=True,y=True) # show grid
self.main_layout.addWidget(self.plot_plt, 1, 0, 3, 3)
# self.plot_plt.setYRange(max=100,min=0)
self.parent = parent
self.data_list = []
parent.timer.timeout.connect(self.plot_data) # update plot
# parent.updateData.change.connect(self.plot_data) # not working, may need to use pyqtSignal for custom signals
self.plot_data()
def plot_data(self):
self.frame_sum_data = self.parent.updateData()
self.data_list.append(self.frame_sum_data)
self.plot_plt.plot().setData(self.data_list,pen='g') # change the color of the pen
I think the problem is because your program calls the updateData() on the __init__ so it only gets the first value and never again updates that value.
Tell me if it does what you want.
Above is what the demo looks like. The pop-up window shows the 'real-time' sum-up values in the left view. The right view shows the FFT of the left.

Qt - Show widget or label above all widget

I want to display a loading screen every time a user presses a button (a process that takes a few seconds runs).
I want something like this
QSplashScreen does not help me because that is only used before opening the application and a QDialog is not useful for me because I want that by dragging the window the application will move along with the message Loading...
What do I have to use?
The only (safe) way to achieve this is to add a child widget without adding it to any layout manager.
The only things you have to care about is that the widget is always raised as soon as it's shown, and that the geometry is always updated to the parent widget (or, better, the top level window).
This is a slightly more advanced example, but it has the benefit that you can just subclass any widget adding the LoadingWidget class to the base classes in order to implement a loading mechanism.
from random import randrange
from PyQt5 import QtCore, QtGui, QtWidgets
class Loader(QtWidgets.QWidget):
def __init__(self, parent):
super().__init__(parent)
self.gradient = QtGui.QConicalGradient(.5, .5, 0)
self.gradient.setCoordinateMode(self.gradient.ObjectBoundingMode)
self.gradient.setColorAt(.25, QtCore.Qt.transparent)
self.gradient.setColorAt(.75, QtCore.Qt.transparent)
self.animation = QtCore.QVariantAnimation(
startValue=0., endValue=1.,
duration=1000, loopCount=-1,
valueChanged=self.updateGradient
)
self.stopTimer = QtCore.QTimer(singleShot=True, timeout=self.stop)
self.focusWidget = None
self.hide()
parent.installEventFilter(self)
def start(self, timeout=None):
self.show()
self.raise_()
self.focusWidget = QtWidgets.QApplication.focusWidget()
self.setFocus()
if timeout:
self.stopTimer.start(timeout)
else:
self.stopTimer.setInterval(0)
def stop(self):
self.hide()
self.stopTimer.stop()
if self.focusWidget:
self.focusWidget.setFocus()
self.focusWidget = None
def updateGradient(self, value):
self.gradient.setAngle(-value * 360)
self.update()
def eventFilter(self, source, event):
# ensure that we always cover the whole parent area
if event.type() == QtCore.QEvent.Resize:
self.setGeometry(source.rect())
return super().eventFilter(source, event)
def showEvent(self, event):
self.setGeometry(self.parent().rect())
self.animation.start()
def hideEvent(self, event):
# stop the animation when hidden, just for performance
self.animation.stop()
def paintEvent(self, event):
qp = QtGui.QPainter(self)
qp.setRenderHints(qp.Antialiasing)
color = self.palette().window().color()
color.setAlpha(max(color.alpha() * .5, 128))
qp.fillRect(self.rect(), color)
text = 'Loading...'
interval = self.stopTimer.interval()
if interval:
remaining = int(max(0, interval - self.stopTimer.remainingTime()) / interval * 100)
textWidth = self.fontMetrics().width(text + ' 000%')
text += ' {}%'.format(remaining)
else:
textWidth = self.fontMetrics().width(text)
textHeight = self.fontMetrics().height()
# ensure that there's enough space for the text
if textWidth > self.width() or textHeight * 3 > self.height():
drawText = False
size = max(0, min(self.width(), self.height()) - textHeight * 2)
else:
size = size = min(self.height() / 3, max(textWidth, textHeight))
drawText = True
circleRect = QtCore.QRect(0, 0, size, size)
circleRect.moveCenter(self.rect().center())
if drawText:
# text is going to be drawn, move the circle rect higher
circleRect.moveTop(circleRect.top() - textHeight)
middle = circleRect.center().x()
qp.drawText(
middle - textWidth / 2, circleRect.bottom() + textHeight,
textWidth, textHeight,
QtCore.Qt.AlignCenter, text)
self.gradient.setColorAt(.5, self.palette().windowText().color())
qp.setPen(QtGui.QPen(self.gradient, textHeight))
qp.drawEllipse(circleRect)
class LoadingExtension(object):
# a base class to extend any QWidget subclass's top level window with a loader
def startLoading(self, timeout=0):
window = self.window()
if not hasattr(window, '_loader'):
window._loader = Loader(window)
window._loader.start(timeout)
# this is just for testing purposes
if not timeout:
QtCore.QTimer.singleShot(randrange(1000, 5000), window._loader.stop)
def loadingFinished(self):
if hasattr(self.window(), '_loader'):
self.window()._loader.stop()
class Test(QtWidgets.QWidget, LoadingExtension):
def __init__(self):
super().__init__()
layout = QtWidgets.QGridLayout(self)
# just a test widget
textEdit = QtWidgets.QTextEdit()
layout.addWidget(textEdit, 0, 0, 1, 2)
textEdit.setMinimumHeight(20)
layout.addWidget(QtWidgets.QLabel('Timeout:'))
self.timeoutSpin = QtWidgets.QSpinBox(maximum=5000, singleStep=250, specialValueText='Random')
layout.addWidget(self.timeoutSpin, 1, 1)
self.timeoutSpin.setValue(2000)
btn = QtWidgets.QPushButton('Start loading...')
layout.addWidget(btn, 2, 0, 1, 2)
btn.clicked.connect(lambda: self.startLoading(self.timeoutSpin.value()))
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
test = Test()
test.show()
sys.exit(app.exec_())
Please check Qt::WindowFlags. The Qt::SplashScreen flag will give you splash screen experience without usage QSplashScreen (you can use it with all widget as show) or, better, use QDialog with this flag.
For moving, probably fine solution is not available but you can just use parent moveEvent to emmit signal. For example:
Main window:
moveEvent -> signal moved
Dialog:
signal move -> re-center window.
Its look as not hard.
By the way, I think block all GUI during application run is not the best solution. You you think use QProgressBar?
You can use this slot: void QWidget::raise().
Raises this widget to the top of the parent widget's stack.
After this call the widget will be visually in front of any overlapping sibling widgets.

Mouse tracking over canvas not working - PyQT

I want to enable mouse tracking over a figure (plot), however the mouse tracking is only working for the area NOT INCLUDING the figure/canvas.
The minimum code to show the issue:
class PlotWorstRegion(QtWidgets.QWidget):
def __init__(self, parent = MatplotlibWidget):
QtWidgets.QWidget.__init__(self, parent)
self.initUI()
def initUI(self):
self.canvas = FigureCanvas(Figure())
self.canvas.axes = self.canvas.figure.add_subplot(111)
grid = QtWidgets.QGridLayout()
x = 0
y = 0
self.text = "x: {0}, y: {1}".format(x, y)
self.label = QtWidgets.QLabel(self.text, self)
grid.addWidget(self.label,0,0)
grid.addWidget(self.canvas)
self.setGeometry(10, 240, 200, 300)
self.setWindowTitle('Event object')
self.setLayout(grid)
self.show()
self.setMouseTracking(True)
def mouseMoveEvent(self, e):
x = e.x()
y = e.y()
text = "x: {0}, y: {1}".format(x, y)
self.label.setText(text)
When I comment out grid.addWidget(self.canvas), this provides tracking in the grid, except when hovering over/around the label widget.
When I add self.label.setMouseTracking(True) in InitUI, the tracking is enabled for everywhere in the grid.
HOWEVER, when I uncomment grid.add_widget(self.canvas) AND add self.canvas.setMouseTracking(True), the tracking is 'blocked' by the canvas and the tracking is only enabled just outside the borders of the canvas.
Any help getting the mouse tracking to apply to the canvas as well would be greatly appreciated!

eventFilter on a QWidget with PyQt4

I have a QMainWindow which contains a DrawingPointsWidget. This widget draws red points randomly. I display the mouse coordinates in the QMainWindow's status bar by installing an event filter for the MouseHovering event using self.installEventFilter(self) and by implementing the eventFilter() method . It works. However I want to get the mouse coordinates on this red-points widget, and not on the QMainWindow. So I want the status bar to display [0, 0] when the mouse is at the top-left corner of the points widget, and not of the QMainWindow. How do I do that? I tried self.installEventFilter(points) but nothing happens.
You wil find below a working chunck of code.
EDIT 1
It seems that if I write points.installEventFilter(self), the QtCore.Event.MouseButtonPressed event is detected, only the HoverMove is not. So the HoverMove event is not detected on my DrawingPointsWidget which is a QWidget.
Surprisingly, the HoverMove event is detected on the QPushButton which is a QAbstractButton which is a QWidget too! I need to write button.installEventFilter(self)
import sys
import random
from PyQt4 import QtGui, QtCore
from PyQt4.QtGui import *
class MainWindow(QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.__setUI()
def __setUI(self, appTitle="[default title]"):
self.statusBar()
mainWidget = QWidget()
vbox = QVBoxLayout()
button = QPushButton("Hello")
vbox.addWidget( button )
points = DrawingPointsWidget()
vbox.addWidget(points)
mainWidget.setLayout(vbox)
self.setCentralWidget(mainWidget)
self.installEventFilter(self)
def eventFilter(self, object, event):
if event.type() == QtCore.QEvent.HoverMove:
mousePosition = event.pos()
cursor = QtGui.QCursor()
self.statusBar().showMessage(
"Mouse: [" + mousePosition.x().__str__() + ", " + mousePosition.y().__str__() + "]"
+ "\tCursor: [" + cursor.pos().x().__str__() + ", " + cursor.pos().y().__str__() + "]"
)
return True
elif event.type() == QtCore.QEvent.MouseButtonPress:
print "Mouse pressed"
return True
return False
class DrawingPointsWidget(QWidget):
""
def __init__(self):
super(QWidget, self).__init__()
self.__setUI()
def __setUI(self):
self.setGeometry(300, 300, 280, 170)
self.setWindowTitle('Points')
self.show()
def paintEvent(self, e):
"Re-implemented method"
qp = QtGui.QPainter()
qp.begin(self)
self.drawPoints(qp)
qp.end()
def drawPoints(self, qp):
qp.setPen(QtCore.Qt.red)
"Need to get the size in case the window is resized -> generates a new paint event"
size = self.size()
for i in range(1000):
x = random.randint(1, size.width()-1 )
y = random.randint(1, size.height()-1 )
qp.drawPoint(x, y)
def main():
app = QApplication(sys.argv)
#window = WidgetsWindow2()
window = MainWindow()
window.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
Firstly, the event filter needs to be set by the object you want to watch:
points.installEventFilter(self)
Secondly, the event you need to listen for is MouseMove not HoverMove:
if event.type() == QtCore.QEvent.MouseMove:
Finally, you need to enable mouse-tracking on the target widget:
class DrawingPointsWidget(QWidget):
def __init__(self):
super(QWidget, self).__init__()
self.setMouseTracking(True)

Categories