In looking at the crosshair example for pyqtgraph, crosshair.py, the crosshair is always visible, and is moved to the position of the mouse when the mouse is over the plot. This means that the crosshair will get stuck at the corners when the cursor exits the plot. Is there a robust way to turn the crosshair on and off when the mouse is over the plot? There are these sigMouseMoved and sigMouseHover signals, but it's possible to move your mouse out of the window fast enough that sigMouseHover won't fire with an empty argument, so I don't think it's hooked up to mouseLeave.
To get mouseLeave event use leaveEvent of PlotWidget which inherits from QWidget.
LeaveEvent is always fired when cursor leaves, even with fast moving mouse cursor.
Opposite to this function is enterEvent, which we can use to show crosshair again.
Here is working example with hiding / showin crosshair.
import sys
import numpy as np
import pyqtgraph as pg
from PyQt5.QtWidgets import QApplication
class CrosshairPlotWidget(pg.PlotWidget):
def __init__(self, parent=None, background='default', plotItem=None, **kargs):
super().__init__(parent=parent, background=background, plotItem=plotItem, **kargs)
self.vLine = pg.InfiniteLine(angle=90, movable=False)
self.hLine = pg.InfiniteLine(angle=0, movable=False)
self.addItem(self.vLine, ignoreBounds=True)
self.addItem(self.hLine, ignoreBounds=True)
self.hLine.hide()
self.vLine.hide()
def leaveEvent(self, ev):
"""Mouse left PlotWidget"""
self.hLine.hide()
self.vLine.hide()
def enterEvent(self, ev):
"""Mouse enter PlotWidget"""
self.hLine.show()
self.vLine.show()
def mouseMoveEvent(self, ev):
"""Mouse moved in PlotWidget"""
if self.sceneBoundingRect().contains(ev.pos()):
mousePoint = self.plotItem.vb.mapSceneToView(ev.pos())
self.vLine.setPos(mousePoint.x())
self.hLine.setPos(mousePoint.y())
if __name__ == "__main__":
app = QApplication(sys.argv)
x = [0, 1, 2, 3, 4, 5]
y = np.random.normal(size=6)
plot = CrosshairPlotWidget()
plot.plot(x, y)
plot.show()
app.exec()
Related
I'm trying to create a video player similar to the looks of the default GUI for mpv. I'm using a QGraphicsVideoItem inside a QGraphicsView along with a custom ControlBar widget as the OSC.
I want the OSC to be 100px high and video.width()px wide, and always flush with the bottom edge of the QGraphicsView widget. I can't seem to do either of those requirements.
MRE:
from PySide6 import QtWidgets as qtw
from PySide6 import QtGui as qtg
from PySide6 import QtCore as qtc
from PySide6 import QtMultimedia as qtm
from PySide6 import QtMultimediaWidgets as qtmw
class ControlBar(qtw.QWidget):
def __init__(self, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)
self.setStyleSheet("background: red")
class View(qtw.QGraphicsView):
def __init__(self) -> None:
super().__init__()
self.setMouseTracking(True)
self.setRenderHints(qtg.QPainter.RenderHint.SmoothPixmapTransform | qtg.QPainter.RenderHint.Antialiasing)
self.setViewportMargins(-2, -2, -2, -2) # `QGraphicsView` has hard coded margins.
self.setFrameStyle(qtw.QFrame.Shape.NoFrame)
self._scene = qtw.QGraphicsScene()
self._video_item = qtmw.QGraphicsVideoItem()
self._control_bar = ControlBar()
self._media_player = qtm.QMediaPlayer()
self._scene.addItem(self._video_item)
self._proxy_control_bar = self._scene.addWidget(self._control_bar)
self._proxy_control_bar.setFlag(qtw.QGraphicsItem.GraphicsItemFlag.ItemIgnoresTransformations)
self.setScene(self._scene)
self._media_player.setVideoOutput(self._video_item)
self._media_player.setSource("video")
self._media_player.mediaStatusChanged.connect(self._media_player.play)
def showEvent(self, event) -> None:
qtc.QTimer.singleShot(100, lambda: self.fitInView(self._video_item, qtc.Qt.AspectRatioMode.KeepAspectRatio))
def resizeEvent(self, event) -> None:
self._proxy_control_bar.setGeometry(0, 0, self.viewport().width(), 100)
pos = qtc.QPoint(0, self.height() - self._proxy_control_bar.size().height())
self._proxy_control_bar.setPos(0, self.mapToScene(pos).y())
self.fitInView(self._video_item, qtc.Qt.AspectRatioMode.KeepAspectRatio)
app = qtw.QApplication()
view = View()
view.show()
app.exec()
I've been able to set the height of the widget to 100px, but using control_area.setGeometry(..., ..., self.viewport().width(), ...) sets the width to be a bit more than the video's width. And, for some reason, adding self._control_bar to the scene creates all this extra empty space around the two items, I have no idea why.
My questions are,
is there no way to get the actual size (specifically the width) of the video item after a fitInView call?
Because calling item.size() even after a fitInView call just returns the original size of the item, which I guess makes sense since only the view's view of the item was "fit in view" and the item itself is still the same.
How do I set the position of the control_bar to be where I want it to?
As seen in one of the videos below, the way I'm doing it right now does not accomplish it at all.
What's up with all the extra empty space?
How it looks:
Video with self._proxy_control_bar lines left in.
Video with self._proxy_control_bar lines commented out.
Good night.
I have seen some programs with new borderless designs and still you can make use of resizing.
At the moment I know that to remove the borders of a pyqt program we use:
QtCore.Qt.FramelessWindowHint
And that to change the size of a window use QSizeGrip.
But how can we resize a window without borders?
This is the code that I use to remove the border of a window but after that I have not found information on how to do it in pyqt5.
I hope you can help me with an example of how to solve this problem
from PyQt5.QtWidgets import QMainWindow,QApplication
from PyQt5 import QtCore
class Main(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.setWindowFlags(QtCore.Qt.FramelessWindowHint)
app = QApplication([])
m = Main()
m.show()
m.resize(800,600)
app.exec_()
If you use a QMainWindow you can add a QStatusBar (which automatically adds a QSizeGrip) just by calling statusBar():
This function creates and returns an empty status bar if the status bar does not exist.
Otherwise, you can manually add grips, and their interaction is done automatically based on their position. In the following example I'm adding 4 grips, one for each corner, and then I move them each time the window is resized.
class Main(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.setWindowFlags(QtCore.Qt.FramelessWindowHint)
self.gripSize = 16
self.grips = []
for i in range(4):
grip = QSizeGrip(self)
grip.resize(self.gripSize, self.gripSize)
self.grips.append(grip)
def resizeEvent(self, event):
QMainWindow.resizeEvent(self, event)
rect = self.rect()
# top left grip doesn't need to be moved...
# top right
self.grips[1].move(rect.right() - self.gripSize, 0)
# bottom right
self.grips[2].move(
rect.right() - self.gripSize, rect.bottom() - self.gripSize)
# bottom left
self.grips[3].move(0, rect.bottom() - self.gripSize)
UPDATE
Based on comments, also side-resizing is required. To do so a good solution is to create a custom widget that behaves similarly to QSizeGrip, but for vertical/horizontal resizing only.
For better implementation I changed the code above, used a gripSize to construct an "inner" rectangle and, based on it, change the geometry of all widgets, for both corners and sides.
Here you can see the "outer" rectangle and the "inner" rectangle used for geometry computations:
Then you can create all geometries, for QSizeGrip widgets (in light blue):
And for custom side widgets:
from PyQt5 import QtCore, QtGui, QtWidgets
class SideGrip(QtWidgets.QWidget):
def __init__(self, parent, edge):
QtWidgets.QWidget.__init__(self, parent)
if edge == QtCore.Qt.LeftEdge:
self.setCursor(QtCore.Qt.SizeHorCursor)
self.resizeFunc = self.resizeLeft
elif edge == QtCore.Qt.TopEdge:
self.setCursor(QtCore.Qt.SizeVerCursor)
self.resizeFunc = self.resizeTop
elif edge == QtCore.Qt.RightEdge:
self.setCursor(QtCore.Qt.SizeHorCursor)
self.resizeFunc = self.resizeRight
else:
self.setCursor(QtCore.Qt.SizeVerCursor)
self.resizeFunc = self.resizeBottom
self.mousePos = None
def resizeLeft(self, delta):
window = self.window()
width = max(window.minimumWidth(), window.width() - delta.x())
geo = window.geometry()
geo.setLeft(geo.right() - width)
window.setGeometry(geo)
def resizeTop(self, delta):
window = self.window()
height = max(window.minimumHeight(), window.height() - delta.y())
geo = window.geometry()
geo.setTop(geo.bottom() - height)
window.setGeometry(geo)
def resizeRight(self, delta):
window = self.window()
width = max(window.minimumWidth(), window.width() + delta.x())
window.resize(width, window.height())
def resizeBottom(self, delta):
window = self.window()
height = max(window.minimumHeight(), window.height() + delta.y())
window.resize(window.width(), height)
def mousePressEvent(self, event):
if event.button() == QtCore.Qt.LeftButton:
self.mousePos = event.pos()
def mouseMoveEvent(self, event):
if self.mousePos is not None:
delta = event.pos() - self.mousePos
self.resizeFunc(delta)
def mouseReleaseEvent(self, event):
self.mousePos = None
class Main(QtWidgets.QMainWindow):
_gripSize = 8
def __init__(self):
QtWidgets.QMainWindow.__init__(self)
self.setWindowFlags(QtCore.Qt.FramelessWindowHint)
self.sideGrips = [
SideGrip(self, QtCore.Qt.LeftEdge),
SideGrip(self, QtCore.Qt.TopEdge),
SideGrip(self, QtCore.Qt.RightEdge),
SideGrip(self, QtCore.Qt.BottomEdge),
]
# corner grips should be "on top" of everything, otherwise the side grips
# will take precedence on mouse events, so we are adding them *after*;
# alternatively, widget.raise_() can be used
self.cornerGrips = [QtWidgets.QSizeGrip(self) for i in range(4)]
#property
def gripSize(self):
return self._gripSize
def setGripSize(self, size):
if size == self._gripSize:
return
self._gripSize = max(2, size)
self.updateGrips()
def updateGrips(self):
self.setContentsMargins(*[self.gripSize] * 4)
outRect = self.rect()
# an "inner" rect used for reference to set the geometries of size grips
inRect = outRect.adjusted(self.gripSize, self.gripSize,
-self.gripSize, -self.gripSize)
# top left
self.cornerGrips[0].setGeometry(
QtCore.QRect(outRect.topLeft(), inRect.topLeft()))
# top right
self.cornerGrips[1].setGeometry(
QtCore.QRect(outRect.topRight(), inRect.topRight()).normalized())
# bottom right
self.cornerGrips[2].setGeometry(
QtCore.QRect(inRect.bottomRight(), outRect.bottomRight()))
# bottom left
self.cornerGrips[3].setGeometry(
QtCore.QRect(outRect.bottomLeft(), inRect.bottomLeft()).normalized())
# left edge
self.sideGrips[0].setGeometry(
0, inRect.top(), self.gripSize, inRect.height())
# top edge
self.sideGrips[1].setGeometry(
inRect.left(), 0, inRect.width(), self.gripSize)
# right edge
self.sideGrips[2].setGeometry(
inRect.left() + inRect.width(),
inRect.top(), self.gripSize, inRect.height())
# bottom edge
self.sideGrips[3].setGeometry(
self.gripSize, inRect.top() + inRect.height(),
inRect.width(), self.gripSize)
def resizeEvent(self, event):
QtWidgets.QMainWindow.resizeEvent(self, event)
self.updateGrips()
app = QtWidgets.QApplication([])
m = Main()
m.show()
m.resize(240, 160)
app.exec_()
to hide the QSizeGrip on the corners where they shouldn't be showing, you can just change the background color of the QSizeGrip to camouflage them to the background. add this to each of the corners of musicamante's answer:
self.cornerGrips[0].setStyleSheet("""
background-color: transparent;
""")
Could be a either a Qt bug, or a bug in my app, I am hoping someone can help me diagnose this.
I am building a PyQt5 application with an interface built using qtdesigner and pyuic5.
I have to QSpinBox widgets in my window, which I connect to two methods like this:
self.img1IndexBox.valueChanged.connect(self.changeImage1)
self.img2IndexBox.valueChanged.connect(self.changeImage2)
Everything seems to work fine in the application, except for one thing: If I scroll the mouse over the spinbox, I can make it increment the value. If I change the value with text, all works fine. If I use keyboard arrows on the spinbox, it works fine. But if I click on either the up or down arrows from the spinbox, I get get two changeValue events, a double increment. Like I clicked twice. In fact, it even looks from the animation that it is creating an event for the downpress, and another when the button goes back up.
Could this be just a library bug, or what could be causing this in my program? How could I debug this?
You might be able to prevent that double thing by setting spinbox enable to false.
then make it enable to true after processing large data.
Upon clicking the arrow up/down
on_valuechanged
ui->spinbox->setEnabled(false);
then set to true before the function on_valuechanged ends.
ui->spinbox->setEnabled(true);
Apparently the problem is the event is triggering a very long routine, this delays the "button release" event, and it is enough time to make the system think the user is actually holding the button, generating more events... But I would still be interested in learning what would be a good walk-around. Would there be a nice pyqt-onic way to start a thread for that method?
http://www.qtcentre.org/archive/index.php/t-43078.html
Instead of using valueChanged.connect use editingFinished.connect it will make sure the function is called only after value is provided.
PyQt
import sys
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QDoubleSpinBox, QApplication, QMainWindow, QWidget, QVBoxLayout, QLabel, QSpinBox
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.initUI()
# def valueChanged(self, value):
# print(value)
def valueChanged(self):
print(f"Value changed new value is : {self.spinBox.value()}")
def initUI(self):
self.setGeometry(0, 0, 200, 100)
self.layout = QVBoxLayout()
self.spinBox = QSpinBox()
self.spinBox.setAlignment(Qt.AlignCenter)
self.spinBox.setRange(0, 1000)
# self.spinBox.valueChanged.connect(self.valueChanged)
self.spinBox.editingFinished.connect(self.valueChanged)
self.layout.addWidget(self.spinBox)
self.setLayout(self.layout)
if __name__ == "__main__":
app = QApplication(sys.argv)
main = MainWindow()
main.show()
sys.exit(app.exec())
PySide
import sys
from PySide6.QtWidgets import QDoubleSpinBox, QApplication, QMainWindow, QWidget, QVBoxLayout, QLabel, QSpinBox
from PySide6.QtGui import Qt
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.initUI()
# def valueChanged(self, value):
# print(value)
def valueChanged(self):
print(f"Value changed new value is : {self.spinBox.value()}")
def initUI(self):
self.setGeometry(0, 0, 200, 100)
self.layout = QVBoxLayout()
self.spinBox = QSpinBox()
self.spinBox.setAlignment(Qt.AlignCenter)
self.spinBox.setRange(0, 1000)
# self.spinBox.valueChanged.connect(self.valueChanged)
self.spinBox.editingFinished.connect(self.valueChanged)
self.layout.addWidget(self.spinBox)
self.setLayout(self.layout)
if __name__ == "__main__":
app = QApplication(sys.argv)
main = MainWindow()
main.show()
sys.exit(app.exec())
I am working on a simple app for a tea timer with Python3 and PyQt5 on OSX. For aesthetic reasons I decided to remove the window frame. In order to still keep the window moveable I overloaded the mousePressEvent() and mouseMoveEvent() handlers.
However I run into a problem when the window is being moved while the mouse is over one of the buttons (QPushButton()). The cursor then jumps to the last click position and the window is moved from there. Does anyone have an idea as to why this is happening? Interestingly the same issue does not appear when clicking and moving on a QLabel() object...
If you want to try it out, here is an example code:
#!/usr/bin/python3
# -*- coding: utf-8 -*-
## ===============================================================
## IMPORTS
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
## ===============================================================
## CONSTANTS
WINDOW_WIDTH = 690
WINDOW_HEIGHT = 435
## ===============================================================
## CLASSES
class Form(QWidget):
def __init__(self, parent=None):
super().__init__()
self.windowPos = QPoint() # Maybe not necessary when button click and window movement are seperated
# Declare and specify UI elements
self.timerLabel = QLabel("00:00") # Might have to change data type here
self.timerLabel.setObjectName("timerLabel")
self.infoLabel = QLabel("No tea selected")
self.infoLabel.setObjectName("infoLabel")
self.teaOneButton = QPushButton("Tea One")
self.teaOneButton.setObjectName("teaOneButton")
self.teaTwoButton = QPushButton("Tea Two")
self.teaTwoButton.setObjectName("teaTwoButton")
# Arrange UI elements in a layout
grid = QGridLayout()
self.setLayout(grid) # Set the QGridLayout as the window's main layout
grid.setSpacing(0) # Spacing between widgets - does not work if window is resized
grid.setContentsMargins(4, 4, 4, 4)
grid.addWidget(self.timerLabel, 0, 0, 1, -1, Qt.AlignHCenter) # http://doc.qt.io/qt-5/qgridlayout.html#addWidget
grid.addWidget(self.infoLabel, 1, 0, 1, -1, Qt.AlignHCenter)
grid.addWidget(self.teaOneButton, 2, 0)
grid.addWidget(self.teaTwoButton, 2, 1)
self.resize(WINDOW_WIDTH, WINDOW_HEIGHT)
# Arranging window in center of the screen by overloading showEvent method
def showEvent(self, QShowEvent):
self.centerOnScreen()
def centerOnScreen(self):
screen = QDesktopWidget()
screenGeom = QRect(screen.screenGeometry(self))
screenCenterX = screenGeom.center().x()
screenCenterY = screenGeom.center().y()
self.move(screenCenterX - self.width() / 2,
screenCenterY - self.height() / 2)
# Overload mouseEvent handlers to make window moveable
def mousePressEvent(self, QMouseEvent):
self.windowPos = QMouseEvent.pos()
self.setCursor(QCursor(Qt.SizeAllCursor))
def mouseReleaseEvent(self, QMouseEvent):
self.setCursor(QCursor(Qt.ArrowCursor))
def mouseMoveEvent(self, QMouseEvent):
# print (self.childAt(QMouseEvent.pos()) == QLabel)
pos = QPoint(QMouseEvent.globalPos())
self.window().move(pos - self.windowPos)
## ===============================================================
## MAIN LOOP
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
screen = Form()
screen.setWindowFlags(Qt.FramelessWindowHint)
screen.show()
sys.exit(app.exec_())
Well guys I think I might have found a way to circumvent the problem.
It seemed to me, that the problem might be that QPushButtons handle mouseMoveEvents differently than, for example QLabel objects. To me it makes sense intuitively since a button object might need different mouse event handling than other unclickable objects.
So what I did was try to implement a subclass for QPushButton, overloading the mouseMoveEvent handler. At first I thought I might set the handler to ignore the event in order to delegate it up to the parent widget. That did not change anything. It actually might be something that has been going on all along. Because when I implement some sort of code into the event handling function, the button does no longer function as a source for moving the window. See for yourself:
#!/usr/bin/python3
# -*- coding: utf-8 -*-
## ===============================================================
## IMPORTS
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
## ===============================================================
## CONSTANTS
WINDOW_WIDTH = 690
WINDOW_HEIGHT = 435
## ===============================================================
## CLASSES
class UnmoveableButton(QPushButton):
def __init__(self, text=""):
super().__init__(text)
# This event function is overloaded in order to avoid the widget from delegating the event up to the parent.
# This way, the pre-existing functionality is skipped, i.e. the window can no longer be moved while hovering over a button.
def mouseMoveEvent(self, QMouseEvent):
pass
class Form(QWidget):
def __init__(self, parent=None):
super().__init__()
self.windowPos = QPoint() # Maybe not necessary when button click and window movement are seperated
# Declare and specify UI elements
self.timerLabel = QLabel("00:00") # Might have to change data type here
self.timerLabel.setObjectName("timerLabel")
self.infoLabel = QLabel("No tea selected")
self.infoLabel.setObjectName("infoLabel")
self.teaOneButton = UnmoveableButton("Tea One")
self.teaOneButton.setObjectName("teaOneButton")
self.teaTwoButton = UnmoveableButton("Tea Two")
self.teaTwoButton.setObjectName("teaTwoButton")
# Arrange UI elements in a layout
grid = QGridLayout()
self.setLayout(grid) # Set the QGridLayout as the window's main layout
grid.setSpacing(0) # Spacing between widgets - does not work if window is resized
grid.setContentsMargins(4, 4, 4, 4)
grid.addWidget(self.timerLabel, 0, 0, 1, -1, Qt.AlignHCenter) # http://doc.qt.io/qt-5/qgridlayout.html#addWidget
grid.addWidget(self.infoLabel, 1, 0, 1, -1, Qt.AlignHCenter)
grid.addWidget(self.teaOneButton, 2, 0)
grid.addWidget(self.teaTwoButton, 2, 1)
self.resize(WINDOW_WIDTH, WINDOW_HEIGHT)
# Arranging window in center of the screen by overloading showEvent method
def showEvent(self, QShowEvent):
self.centerOnScreen()
def centerOnScreen(self):
screen = QDesktopWidget()
screenGeom = QRect(screen.screenGeometry(self))
screenCenterX = screenGeom.center().x()
screenCenterY = screenGeom.center().y()
self.move(screenCenterX - self.width() / 2,
screenCenterY - self.height() / 2)
# Overload mouseEvent handlers to make window moveable
def mousePressEvent(self, QMouseEvent):
self.windowPos = QMouseEvent.pos()
self.setCursor(QCursor(Qt.SizeAllCursor))
def mouseReleaseEvent(self, QMouseEvent):
self.setCursor(QCursor(Qt.ArrowCursor))
def mouseMoveEvent(self, QMouseEvent):
# print (self.childAt(QMouseEvent.pos()) == QLabel)
pos = QPoint(QMouseEvent.globalPos())
self.window().move(pos - self.windowPos)
## ===============================================================
## MAIN LOOP
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
screen = Form()
screen.setWindowFlags(Qt.FramelessWindowHint)
screen.show()
sys.exit(app.exec_())
I think it might not be the most elegant or correct solution for that matter, but it works fine for me right now. If someone has an idea on how to improve upon this, let me know :)
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
Are you sure there is no namespace clash from imported members?
I want to make:
a semi-transparent fullscreen window (rgba(0,0,0,180)).
while moving mouse, display absolute position on label.
user can press on it to get the absolute position of the mouse.
However I cannot achieve the second one. When moving mouse on it, label won't update mouse's position. But I found when moving out of label (after removing layout.setMargin(0) and layout.setSpacing(0)), it works.
# -*- coding: utf-8 -*-
import sys, os, math
from PyQt4 import QtCore, QtGui
class ScreenPositionLabel(QtGui.QWidget):
def __init__(self):
super(ScreenPositionLabel, self).__init__()
self.setStyleSheet("background-color:rgba(0, 0, 0, 180); color:#fff;")
self.setAttribute(QtCore.Qt.WA_TranslucentBackground)
#self.setAttribute(QtCore.Qt.WA_OpaquePaintEvent, False)
#self.setStyleSheet("QMainWindow{opacity:0.5;}")
self.label = QtGui.QLabel("Please click on screen")
self.label.setAlignment(QtCore.Qt.AlignCenter)
layout = QtGui.QHBoxLayout()
layout.addWidget(self.label)
# remove margin and padding
layout.setMargin(0)
layout.setSpacing(0)
self.setLayout(layout)
self.setMouseTracking(True)
self.installEventFilter(self)
self.label.show()
self.show()
def eventFilter(self, source, event):
if (event.type() == QtCore.QEvent.MouseMove and
event.buttons() == QtCore.Qt.NoButton):
pos = event.pos()
self.label.setText('Please click on screen. ( %d : %d )' % (pos.x(), pos.y()))
elif event.type() == QtCore.QEvent.MouseButtonPress:
pos = event.pos()
print('( %d : %d )' % (pos.x(), pos.y()))
self.close()
return QtGui.QWidget.eventFilter(self, source, event)
app = QtGui.QApplication(sys.argv)
main_window = ScreenPositionLabel()
app.exec_()
Any way to solve this problem? Thanks!
Your 4 questions:
1) I want to make: a semi-transparent fullscreen window (rgba(0,0,0,180)).
Yes, you can. Please use QWidget.setWindowOpacity (self, float level).
2) I want to make: while moving mouse, display absolute position on label.
I recommend using QWidget.mouseMoveEvent (self, QMouseEvent) to get current position your mouse and enable QWidget.setMouseTracking (self, bool enable) for track all mouse movement.
QWidget.setMouseTracking (self, bool enable)
QWidget.mouseMoveEvent (self, QMouseEvent)
3) I want to make: user can press on it to get the absolute position of the mouse.
Using QWidget.mousePressEvent (self, QMouseEvent) to track when mouse press.
4) However I cannot achieve the second one. When moving mouse on it, label won't update mouse's position. But I found when moving out of label (after removing layout.setMargin(0) and layout.setSpacing(0)), it works.
Because in default layout height of QLabel has spacing & margin, then real area isn't all area widget solve it is your solution is OK.
Full example for your solution:
import sys
from PyQt4 import QtGui, QtCore
class QCustomLabel (QtGui.QLabel):
def __init__ (self, parent = None):
super(QCustomLabel, self).__init__(parent)
self.setMouseTracking(True)
self.setTextLabelPosition(0, 0)
self.setAlignment(QtCore.Qt.AlignCenter)
def mouseMoveEvent (self, eventQMouseEvent):
self.setTextLabelPosition(eventQMouseEvent.x(), eventQMouseEvent.y())
QtGui.QWidget.mouseMoveEvent(self, eventQMouseEvent)
def mousePressEvent (self, eventQMouseEvent):
if eventQMouseEvent.button() == QtCore.Qt.LeftButton:
QtGui.QMessageBox.information(self, 'Position', '( %d : %d )' % (self.x, self.y))
QtGui.QWidget.mousePressEvent(self, eventQMouseEvent)
def setTextLabelPosition (self, x, y):
self.x, self.y = x, y
self.setText('Please click on screen ( %d : %d )' % (self.x, self.y))
class QCustomWidget (QtGui.QWidget):
def __init__ (self, parent = None):
super(QCustomWidget, self).__init__(parent)
self.setWindowOpacity(0.7)
# Init QLabel
self.positionQLabel = QCustomLabel(self)
# Init QLayout
layoutQHBoxLayout = QtGui.QHBoxLayout()
layoutQHBoxLayout.addWidget(self.positionQLabel)
layoutQHBoxLayout.setMargin(0)
layoutQHBoxLayout.setSpacing(0)
self.setLayout(layoutQHBoxLayout)
self.showFullScreen()
myQApplication = QtGui.QApplication(sys.argv)
myQTestWidget = QCustomWidget()
myQTestWidget.show()
myQApplication.exec_()