I am populating a QlistWidget with icons and I notice there is a lag when the window is loading. I wondered if there is a way to generate half resolution icons, or some other way to speed up the window generation time?
texture_item = QtWidgets.QListWidgetItem(texture)
texture_pixmap = QtGui.QPixmap(image_path)
texture_icon = QtGui.QIcon()
self.list_widget_left.setIconSize(QtCore.QSize(105,105))
texture_item.setFont(QtGui.QFont('SansSerif', 10))
texture_icon.addPixmap(texture_pixmap)
texture_item.setIcon(texture_icon)
self.list_widget_left.addItem(texture_item)
texture_item.setTextAlignment(Qt.AlignBottom)
There are several aspects that can generate the delay:
The images weigh a lot then the solution is to use less heavy icons
You have many images that iterate in the same loop, then a possible solution is to give a small delay so that the image is loaded little by little avoiding the visual delay that you indicate.
import os
from PySide2 import QtCore, QtGui, QtWidgets
import shiboken2
def for_loop_files(path, interval=100, extensions=(), parent=None, objectName=""):
timer = QtCore.QTimer(parent=parent, singleShot=True, interval=interval)
if objectName:
timer.setObjectName(objectName)
loop = QtCore.QEventLoop(timer)
timer.timeout.connect(loop.quit)
timer.destroyed.connect(loop.quit)
for root, dirs, files in os.walk(path):
for name in files:
base, ext = os.path.splitext(name)
if extensions:
if ext in extensions:
if shiboken2.isValid(timer):
timer.start()
loop.exec_()
yield os.path.join(root, name)
else:
yield os.path.join(root, name)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.list_widget = QtWidgets.QListWidget()
self.list_widget.setViewMode(QtWidgets.QListView.IconMode)
self.list_widget.setIconSize(QtCore.QSize(128, 128))
self.list_widget.setResizeMode(QtWidgets.QListView.Adjust)
self.list_widget.setFlow(QtWidgets.QListView.TopToBottom)
self.setCentralWidget(self.list_widget)
self.resize(640, 480)
QtCore.QTimer.singleShot(0, self.load_icons)
#QtCore.Slot()
def load_icons(self):
for path in for_loop_files(".", extensions=(".png", "jpg"), parent=self, objectName="icon_timer", interval=30):
it = QtWidgets.QListWidgetItem()
it.setIcon(QtGui.QIcon(path))
self.list_widget.addItem(it)
def closeEvent(self, event):
timer = self.findChild(QtCore.QTimer, "icon_timer")
if timer is not None:
timer.deleteLater()
super(MainWindow, self).closeEvent(event)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
from PySide2 import QtCore, QtGui, QtWidgets
import shiboken2
def for_loop_files(paths, interval=100, parent=None, objectName=""):
timer = QtCore.QTimer(parent=parent, singleShot=True, interval=interval)
if objectName:
timer.setObjectName(objectName)
loop = QtCore.QEventLoop(timer)
timer.timeout.connect(loop.quit)
timer.destroyed.connect(loop.quit)
for path in paths:
if shiboken2.isValid(timer):
timer.start()
loop.exec_()
yield path
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.list_widget = QtWidgets.QListWidget()
self.list_widget.setViewMode(QtWidgets.QListView.IconMode)
self.list_widget.setIconSize(QtCore.QSize(128, 128))
self.list_widget.setResizeMode(QtWidgets.QListView.Adjust)
self.list_widget.setFlow(QtWidgets.QListView.TopToBottom)
self.setCentralWidget(self.list_widget)
self.resize(640, 480)
QtCore.QTimer.singleShot(0, self.load_icons)
#QtCore.Slot()
def load_icons(self):
paths = ["icon1.png", "icon2.png", "icon3.png", "icon4.png"]
for path in for_loop_files(paths, parent=self, objectName="icon_timer", interval=30):
it = QtWidgets.QListWidgetItem()
it.setIcon(QtGui.QIcon(path))
self.list_widget.addItem(it)
def closeEvent(self, event):
timer = self.findChild(QtCore.QTimer, "icon_timer")
timer.deleteLater()
super(MainWindow, self).closeEvent(event)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
Related
what I am trying to do with a python script: Use a pytest test method to print a text line to a label in the pyqt GUI.
When running the main python file, the GUI starts and a click on the "test" button runs the test without blocking the GUI (see full code example below). But I have no clue how to proceed now.
Code:
import sys
import pytest
from PyQt5 import QtWidgets, QtCore
from PyQt5.QtWidgets import QApplication, QVBoxLayout, QWidget
class Window(QtWidgets.QMainWindow):
signal_start_background_job = QtCore.pyqtSignal()
def __init__(self):
super(Window, self).__init__()
layout = QVBoxLayout()
self.button = QtWidgets.QPushButton("test", self)
self.label = QtWidgets.QLabel("console output")
layout.addWidget(self.button)
layout.addWidget(self.label)
widget = QWidget()
widget.setLayout(layout)
self.setCentralWidget(widget)
self.worker = WorkerObject()
self.thread = QtCore.QThread()
self.worker.moveToThread(self.thread)
self.signal_start_background_job.connect(self.worker.background_job)
self.button.clicked.connect(self.start_background_job)
def start_background_job(self):
self.thread.start()
self.signal_start_background_job.emit()
class WorkerObject(QtCore.QObject):
#QtCore.pyqtSlot()
def background_job(self):
pytest.main(["-s", "-k test_something"])
if __name__ == "__main__":
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
def test_something():
print("unit test some stuff")
assert 0 == 0
Instead of using pytest directly you could use QProcess to launch it and then capture the output:
import os
import sys
from PyQt5 import QtWidgets, QtCore
from PyQt5.QtWidgets import QApplication, QVBoxLayout, QWidget
CURRENT_DIR = os.path.dirname(os.path.realpath(__file__))
class Window(QtWidgets.QMainWindow):
def __init__(self):
super(Window, self).__init__()
self.button = QtWidgets.QPushButton("test", self)
self.label = QtWidgets.QLabel("console output")
self.textedit = QtWidgets.QTextEdit(readOnly=True)
widget = QWidget()
layout = QVBoxLayout(widget)
layout.addWidget(self.button)
layout.addWidget(self.label)
layout.addWidget(self.textedit)
self.setCentralWidget(widget)
self.process = QtCore.QProcess()
self.process.setProgram(sys.executable)
self.process.readyReadStandardError.connect(self.on_readyReadStandardError)
self.process.readyReadStandardOutput.connect(self.on_readyReadStandardOutput)
self.button.clicked.connect(self.on_clicked)
#QtCore.pyqtSlot()
def on_clicked(self):
self.process.setWorkingDirectory(CURRENT_DIR)
self.process.setArguments(["-m", "pytest", "-s", "-k", "test_something"])
self.process.start()
#QtCore.pyqtSlot()
def on_readyReadStandardError(self):
err = self.process.readAllStandardError().data().decode()
self.textedit.append(err)
#QtCore.pyqtSlot()
def on_readyReadStandardOutput(self):
out = self.process.readAllStandardOutput().data().decode()
self.textedit.append(out)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
I'm thinking you need to check out sys.stdout, and route that to an io object that you can route to the label in your widget. Then I would set a timer and every 0.1 seconds or so set the text of your label to that object. Alternatively you can implement a widget that grabs the stdout text in qt, example here: https://stackoverflow.com/a/1220002/6615517 I haven't tried it but the other answers to that question should help. You'll probably want to clear the label on each test to prevent it from getting too long.
I would like to continuously rotate a QPainter pixmap every tick based from a QTimer - in this example a clock arm. I can rotate the clock arm, however I dont have the skills to make the rotation dynamic. Here is the clock I would like to make and below is my sample code. Let me know if you can help me on the way, thanks!
import sys
import random
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import Qt
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.label = QtWidgets.QLabel()
self.setCentralWidget(self.label)
self.Clock_pixmap = QtGui.QPixmap("clock.png")
self.Arm_pixmap = QtGui.QPixmap("clockarm.png")
self.painter = QtGui.QPainter(self.Clock_pixmap)
self.painter.setRenderHints(QtGui.QPainter.Antialiasing | QtGui.QPainter.SmoothPixmapTransform)
self.painter.drawPixmap(QtCore.QPoint(), self.Arm_pixmap)
self.painter.end()
self.label.setPixmap(self.Clock_pixmap.scaled(self.label.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation))
self.label.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
self.label.setMinimumSize(150, 150)
self.w1 = self.Arm_pixmap.width()/2
self.h1 = self.Arm_pixmap.height()/2
self.rotationData = random.sample(range(100), 100)
timer = QtCore.QTimer(self, timeout=self.rotateArm, interval=100)
timer.start()
self.n=0
def rotateArm(self):
self.n+=1
self.painter.translate(self.w1,self.h1)
self.painter.rotate(self.rotationData[self.n])
self.painter.translate(-self.w1,-self.h1)
self.update()
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
You are painting on a QPainter where you indicated that it was finished painting since you used the end() method. So it is not necessary to make a class attribute to QPainter but only a local variable. Considering the above, the solution is:
import sys
import random
from PyQt5 import QtCore, QtGui, QtWidgets
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self._angle = 0
self.label = QtWidgets.QLabel()
self.setCentralWidget(self.label)
self.clock_pixmap = QtGui.QPixmap("clock.png")
self.arm_pixmap = QtGui.QPixmap("clockarm.png")
rotation_data = random.sample(range(100), 100)
self.data_iter = iter(rotation_data)
timer = QtCore.QTimer(self, timeout=self.rotate_arm, interval=100)
timer.start()
def rotate_arm(self):
try:
angle = next(self.data_iter)
except StopIteration:
pass
else:
self.draw(angle)
def draw(self, angle):
pixmap = self.clock_pixmap.copy()
painter = QtGui.QPainter(pixmap)
painter.setRenderHints(
QtGui.QPainter.Antialiasing | QtGui.QPainter.SmoothPixmapTransform
)
painter.translate(pixmap.rect().center())
painter.rotate(angle)
painter.translate(-pixmap.rect().center())
painter.drawPixmap(QtCore.QPoint(), self.arm_pixmap)
painter.end()
self.label.setPixmap(pixmap)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
In order to track progress, this is the third question about practicing with different classes in PyQt5 .Here are the links to my previous questions:opening a new window, Open a file from main window to a new window in PyQt5 (in different files).
I'm trying to work with two classes, one with one button and when it's pressed it will load a file and show the text in a QTextEdit in other class.
In the first questions I was suggested that as an alternative to work with more classes, they can inherit from QMainWindow so I looked for more info for doing this: PyQt class inheritance
The second question code did worked but it would show both windows at the same time, so this question: PyQt: How to hide QMainWindow guided me to write this code (I attatch this here because it's a little bit different from the one in the link, plus I apply what it says in the answer):
import sys, os
from PyQt5 import QtGui, QtCore, QtWidgets
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QPushButton
class Dialog_02(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Dialog_02, self).__init__(parent, QtCore.Qt.Window)
self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
myBoxLayout = QVBoxLayout()
Button_02 = QPushButton ("Show Dialog 01")
myBoxLayout.addWidget(Button_02)
self.setLayout(myBoxLayout)
self.setWindowTitle('Dialog 02')
Button_02.clicked.connect(self.closeAndReturn)
def closeAndReturn(self):
self.close()
self.parent().show()
class Dialog_01(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Dialog_01, self).__init__()
self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
myBoxLayout = QVBoxLayout()
Button_01 = QPushButton ("Show Dialog 02")
myBoxLayout.addWidget(Button_01)
self.setLayout(myBoxLayout)
self.setWindowTitle('Dialog 01')
Button_01.clicked.connect(self.callAnotherQMainWindow)
def callAnotherQMainWindow(self):
self.hide()
self.dialog_02 = Dialog_02(self)
self.dialog_02.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
dialog_1 = Dialog_01()
dialog_1.show()
sys.exit(app.exec_())
In this code I'm not inheriting, but it works fine.
The issue is that when I try to follow the same syntax in the original question code, it won't run, I'm not sure I'm getting inheritances fine.
import sys
import os
from PyQt5 import QtWidgets, QtGui, QtCore
from PyQt5.QtWidgets import QApplication, QWidget
from PyQt5.QtWidgets import QPushButton, QVBoxLayout, QTextEdit, QHBoxLayout, QLabel, QMainWindow, QAction, QFileDialog
class SecondWindow(QWidget):
def __init__(self, Window):
super(SecondWindow, self).__init__(parent, QtCore.Qt.Window)
self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
self.text = QTextEdit(self)
self.btn_return= QPushButton("Return")
self.init_ui()
def init_ui(self):
v_layout = QVBoxLayout(self)
v_layout.addWidget(self.text)
v_layout.addWidget(self.btn_return)
self.setLayout(v_layout)
self.setWindowTitle('Opened Text')
self.btn_return.clicked.connect(self.closeAndReturn)
def closeAndReturn(self):
self.close()
self.parent().show()
class Window(QMainWindow):
textChanged = QtCore.pyqtSignal(str)
def __init__(self, *args):
super(Window, self).__init__()
self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
self.img = QLabel()
self.load_file= QPushButton('Load')
self.width = 400
self.height = 150
self.init_ui()
def init_ui(self):
self.img.setPixmap(QtGui.QPixmap("someimage.png"))
h_layout = QHBoxLayout()
v_layout = QVBoxLayout()
h_final = QHBoxLayout()
h_layout.addWidget(self.img)
v_layout.addWidget(self.load_file)
h_final.addLayout(h_layout)
h_final.addLayout(v_layout)
self.load_file.clicked.connect(self.loadafile)
self.setLayout(h_final)
self.setWindowTitle('Main Window')
self.setGeometry(600,150,self.width,self.height)
#QtCore.pyqtSlot()
def loadafile(self):
filename = QFileDialog.getOpenFileName(self, 'Open File', os.getenv('HOME'))
with open(filename[0], 'r') as f:
file_text = f.read()
self.textChanged.emit(file_text)
self.hide()
self.dialog_02 = SecondWindow(self)
self.dialog_02.show()
def main():
app = QApplication(sys.argv)
main = Window()
s = SecondWindow(main)
main.textChanged.connect(s.text.append)
main.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
You are coupling many classes: What happens if at a moment SecondWindow does not have a parent? Well, your code will have problems and you will have to modify it a lot so that it works correctly. So first it is to design the behavior of each class, for example SecondWindow has to warn the other windows that it was clicked, it has to have a method that updates the text. Similarly, Window must notify that there is new text available.
On the other hand QMainWindow already has a predefined layout so you must create a centralwidget where you place the other widgets.
import os
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class SecondWindow(QtWidgets.QWidget):
closed = QtCore.pyqtSignal()
def __init__(self, parent=None):
super(SecondWindow, self).__init__(parent, QtCore.Qt.Window)
self.text = QtWidgets.QTextEdit()
self.btn_return= QtWidgets.QPushButton("Return")
self.init_ui()
def init_ui(self):
v_layout = QtWidgets.QVBoxLayout(self)
v_layout.addWidget(self.text)
v_layout.addWidget(self.btn_return)
self.setWindowTitle('Opened Text')
self.btn_return.clicked.connect(self.close)
self.btn_return.clicked.connect(self.closed)
#QtCore.pyqtSlot(str)
def update_text(self, text):
self.text.setText(text)
self.show()
class Window(QtWidgets.QMainWindow):
textChanged = QtCore.pyqtSignal(str)
def __init__(self, *args):
super(Window, self).__init__()
self.img = QtWidgets.QLabel()
self.load_file= QtWidgets.QPushButton('Load')
self.width = 400
self.height = 150
self.init_ui()
def init_ui(self):
self.img.setPixmap(QtGui.QPixmap("someimage.png"))
self.load_file.clicked.connect(self.loadafile)
central_widget = QtWidgets.QWidget()
self.setCentralWidget(central_widget)
h_layout = QtWidgets.QHBoxLayout(central_widget)
h_layout.addWidget(self.img)
h_layout.addWidget(self.load_file)
self.setWindowTitle('Main Window')
self.setGeometry(600,150,self.width,self.height)
#QtCore.pyqtSlot()
def loadafile(self):
filename, _ = QtWidgets.QFileDialog.getOpenFileName(self, 'Open File', os.getenv('HOME'))
if filename:
with open(filename, 'r') as f:
file_text = f.read()
self.textChanged.emit(file_text)
self.close()
def main():
app = QtWidgets.QApplication(sys.argv)
main = Window()
s = SecondWindow()
main.textChanged.connect(s.update_text)
s.closed.connect(main.show)
main.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Maybe I'm doing this completely wrong here, I'm trying to make the "timeline" for an mp3 player. Everything works and I've tried to set the slider to one value and it was fine. My problem is when I try updating the timeline as it keeps on freezing the program and it doesn't unfreeze. Here's my code, I commented out where to start looking at in terms of where I'm having trouble in:
import sys
from PyQt5.QtCore import QCoreApplication, Qt
from PyQt5.QtGui import *
from PyQt5.QtWidgets import QApplication, QWidget, QMainWindow, QPushButton,
QSlider
import vlc
player = vlc.MediaPlayer("/songs/Immigrant Song.mp3")
class window(QMainWindow):
def __init__(self):
super(window, self).__init__()
self.setGeometry(50, 50, 400, 200)
self.setWindowTitle('SlugPlayer')
#self.setWindowIcon(QIcon('pic.png'))
self.home()
def home(self):
playing = 0
btn = QPushButton('quit', self)
btn.clicked.connect(self.close_application)
btn.resize(btn.sizeHint()) #set to acceptable size automatic
btn.move(0, 0)
playbtn = QPushButton('Play', self)
# playbtn.clicked.connect(self.update_bar, playing)
playbtn.clicked.connect(self.play_music, playing)
playbtn.resize(playbtn.sizeHint())
playbtn.move(100, 0)
psebtn = QPushButton('Pause', self)
psebtn.clicked.connect(self.pause_music, playing)
psebtn.resize(psebtn.sizeHint())
psebtn.move(200, 0)
stopbtn = QPushButton('Stop', self)
stopbtn.clicked.connect(self.stop_music, playing)
stopbtn.resize(stopbtn.sizeHint())
stopbtn.move(300, 0)
self.sl = QSlider(Qt.Horizontal, self)
self.sl.setFocusPolicy(Qt.NoFocus)
self.sl.setGeometry(30, 50, 300, 20)
self.sl.setMinimum(0)
self.sl.setMaximum(100)
self.sl.setValue(0)
self.sl.move(50, 100)
# self.sl.valueChanged[int].connect(self.print_value)
self.show()
def close_application(self):
sys.exit()
#here is where I'm trying to update the bar
def play_music(self, playing):
player.play()
playing = 1
while playing == 1:
self.update_bar(playing)
def pause_music(self, playing):
player.pause()
playing = 0
def stop_music(self, playing):
player.stop()
playing = 0
def print_value(self, value):
print(value)
#function to update the bar (converts to an int)
def update_bar(self, playing):
self.sl.setValue(int(player.get_position() * 100))
def run():
app = QApplication(sys.argv)
Gui = window()
sys.exit(app.exec_())
run()
An infinite loop like while playing == 1 freezes the GUI since it does not allow you to do other tasks, in these cases it is better to use a QTimer. Going a little further to the bottom of the problem I have implemented a class that is in charge of managing the player by sending signals when necessary.
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
import vlc
class VLCManager(QtCore.QObject):
durationChanged = QtCore.pyqtSignal(int)
positionChanged = QtCore.pyqtSignal(int)
def __init__(self, parent=None):
super(VLCManager, self).__init__(parent)
self._player = vlc.MediaPlayer()
self._timer = QtCore.QTimer(self, interval=100, timeout=self.update_values)
def setFileName(self, filename):
self._player = vlc.MediaPlayer(filename)
def position(self):
self.durationChanged.emit(self.duration())
return self._player.get_time()
def setPosition(self, time):
if self.position() != time:
self._player.set_time(time)
#QtCore.pyqtSlot()
def update_values(self):
self.positionChanged.emit(self.position())
def duration(self):
return self._player.get_length()
#QtCore.pyqtSlot()
def play(self):
self._player.play()
self.update_values()
self._timer.start()
#QtCore.pyqtSlot()
def pause(self):
self._player.pause()
self.update_values()
self._timer.stop()
#QtCore.pyqtSlot()
def stop(self):
self._player.stop()
self.update_values()
self._timer.stop()
class window(QtWidgets.QMainWindow):
def __init__(self):
super(window, self).__init__()
self._manager = VLCManager(self)
self._manager.setFileName("/songs/Immigrant Song.mp3")
self.setWindowTitle('SlugPlayer')
self.home()
def home(self):
quitbtn = QtWidgets.QPushButton('quit')
quitbtn.clicked.connect(self.close)
playbtn = QtWidgets.QPushButton('Play')
playbtn.clicked.connect(self._manager.play)
psebtn = QtWidgets.QPushButton('Pause', self)
psebtn.clicked.connect(self._manager.pause)
stopbtn = QtWidgets.QPushButton('Stop', self)
stopbtn.clicked.connect(self._manager.stop)
self.sl = QtWidgets.QSlider(orientation=QtCore.Qt.Horizontal)
self.sl.setFocusPolicy(QtCore.Qt.NoFocus)
self._manager.durationChanged.connect(self.sl.setMaximum)
self._manager.positionChanged.connect(self.sl.setValue)
central_widget = QtWidgets.QWidget()
self.setCentralWidget(central_widget)
lay = QtWidgets.QVBoxLayout(central_widget)
hlay = QtWidgets.QHBoxLayout()
for b in (quitbtn, playbtn, psebtn, stopbtn, ):
hlay.addWidget(b)
lay.addLayout(hlay)
lay.addWidget(self.sl)
self.sl.valueChanged[int].connect(self._manager.setPosition)
self.show()
def run():
app = QtWidgets.QApplication(sys.argv)
Gui = window()
sys.exit(app.exec_())
run()
I have to activate some function, when the cursor is moving. So, I used self.setMouseTracking(True) in MainWidget. But in this way mouseMoveEvent() works only when there is an empty form under cursor. I tried to create another widget over main, but it doesnt work at all.
class ClickButton(QPushButton):
def __init__(self, text, window):
...
def run(self):
...
class Window(QWidget):
def __init__(self):
super().__init__()
self.setGeometry(0, 0, 1000, 1000)
self.setMouseTracking(True)
self.clickers = [ClickButton('OK', self) for i in range(8)]
def mouseMoveEvent(self, ev):
for e in self.clickers:
e.run()
Whats to do?
If you want to detect the position of the mouse even when the mouse is on top of a child, a possible option is to use an event filter.
from PyQt5 import QtCore, QtGui, QtWidgets
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Widget, self).__init__(parent)
w_ = QtWidgets.QWidget()
lay_w = QtWidgets.QHBoxLayout(w_)
for c in (QtWidgets.QPushButton(), QtWidgets.QLineEdit()):
lay_w.addWidget(c)
lay = QtWidgets.QVBoxLayout(self)
for w in (QtWidgets.QPushButton(), QtWidgets.QLineEdit(), QtWidgets.QTextEdit(), w_):
lay.addWidget(w)
for ws in self.findChildren(QtWidgets.QWidget) + [self]:
ws.setMouseTracking(True)
ws.installEventFilter(self)
def eventFilter(self, obj, event):
if event.type() == QtCore.QEvent.MouseMove:
p_respect_to_window = self.mapFromGlobal(obj.mapToGlobal(event.pos()))
print(p_respect_to_window)
return super(Widget, self).eventFilter(obj, event)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())
On the other hand if you only want to do it in only one type of custom widget it is better to overwrite the mouseMoveEvent method of the custom widget:
from PyQt5 import QtCore, QtGui, QtWidgets
class ClickButton(QtWidgets.QPushButton):
def __init__(self, text, parent=None):
super(ClickButton, self).__init__(text=text, parent=parent)
self.setMouseTracking(True)
def mouseMoveEvent(self, event):
self.run()
super(ClickButton, self).mouseMoveEvent(event)
def run(self):
print("call to run function in button{} and time: {}".format(self.text(),
QtCore.QDateTime.currentDateTime().toString()))
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Widget, self).__init__(parent)
lay = QtWidgets.QVBoxLayout(self)
for i in range(10):
w = ClickButton(str(i), self)
lay.addWidget(w)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())