How to make double arrow button in PyQt - python

I'm trying to make a button like this
Currently I can create the middle two buttons (single right or single left) using Qt::LeftArrow or Qt::RightArrow from setArrowType(). From the docs, there seems to only be 5 possible types. If this feature is not built in, how can I make a custom double arrow button?
Right now I have this
from PyQt4 import QtCore, QtGui
import sys
class PanningButton(QtGui.QToolButton):
"""Custom button with click, short/long press, and auto-repeat functionality"""
def __init__(self):
QtGui.QToolButton.__init__(self)
self.setArrowType(QtCore.Qt.LeftArrow)
# Enable auto repeat on button hold
self.setAutoRepeat(True)
# Initial delay in ms before auto-repetition kicks in
self.setAutoRepeatDelay(700)
# Length of auto-repetition
self.setAutoRepeatInterval(500)
self.clicked.connect(self.buttonClicked)
self._state = 0
def buttonClicked(self):
# Panning
if self.isDown():
if self._state == 0:
self._state = 1
self.setAutoRepeatInterval(50)
# Mouse release
elif self._state == 1:
self._state = 0
self.setAutoRepeatInterval(125)
def pressed():
global counter
counter += 1
print(counter)
if __name__ == '__main__':
app = QtGui.QApplication([])
counter = 0
panning_button = PanningButton()
panning_button.clicked.connect(pressed)
panning_button.show()
sys.exit(app.exec_())

There are several options:
Use the Qt icons, in this case you can use the standardIcon() of QStyle:
from PyQt4 import QtCore, QtGui
class Widget(QtGui.QWidget):
def __init__(self, parent=None):
super(Widget, self).__init__(parent)
button1 = QtGui.QToolButton()
button1.setIcon(
button1.style().standardIcon(QtGui.QStyle.SP_MediaSeekBackward)
)
button2 = QtGui.QToolButton()
button2.setArrowType(QtCore.Qt.LeftArrow)
button3 = QtGui.QToolButton()
button3.setArrowType(QtCore.Qt.RightArrow)
button4 = QtGui.QToolButton()
button4.setIcon(
button1.style().standardIcon(QtGui.QStyle.SP_MediaSeekForward)
)
lay = QtGui.QHBoxLayout(self)
for btn in (button1, button2, button3, button4):
lay.addWidget(btn)
if __name__ == "__main__":
import sys
app = QtGui.QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())
Create the icons with QPainter
def create_icon():
pixmap = QtGui.QPixmap(QtCore.QSize(128, 128))
pixmap.fill(QtCore.Qt.transparent)
painter = QtGui.QPainter(pixmap)
# draw icon
painter.end()
return QtGui.QIcon(pixmap)
Use icons created by an image editor like photoshop, corel draw, gimp, etc.
IMHO the simplest solution is the last case since in the first case you are limited to what Qt provides, in the second case it can be unnecessarily complicated, however the third case is the optimal option.

Related

Return clicked button id instead of (accept, reject flags) on QDialog- PyQt5

Summary:
I've been using QMessageBox in my application (for this purpose asking for saving project before closing, Error messages), And now I want to have a custom style (Custom title bar, custom buttons and so on), I found it is hard to do that with QMessageBox, And since I've been using a QDialog in my application as well (For this purpose: Taking input from user), I decided to use a custom QDialog (Make my own style on it) instead of QMessageBox for these all previous purpose, since it is easier to customize and creating my style.
The problem:
The problem that I have now is: QDialog return only a flag value (0,1) depending on the button role And that's fine if I have only 2 buttons BUT here I have 3 (Save, Cancel, Close), Save will return 1 and both Close and Cancel return 0. And QMessageBox as I've seen it return the id of the Button that was clicked.
An example (commented well hopefully :D):
from PyQt5.QtWidgets import *
class CustomDialog(QDialog):
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle("Data will be lost")
label = QLabel("Are you sure you want close the app before saving?")
buttonBox = QDialogButtonBox()
buttonBox.addButton(QDialogButtonBox.Save)
buttonBox.addButton(QDialogButtonBox.Cancel)
buttonBox.addButton(QDialogButtonBox.Close)
layout = QVBoxLayout()
layout.addWidget(label)
layout.addWidget(buttonBox)
self.resize(300, 100)
self.setLayout(layout)
# These two lines, return 0 or 1.
buttonBox.rejected.connect(self.reject)
buttonBox.accepted.connect(self.accept)
class Window(QWidget):
def __init__(self):
super().__init__()
label = QLabel('Hello Dialog', self)
open_dialog_button = QPushButton('Open Dialog', self)
open_dialog_button.clicked.connect(self.showDialog)
open_message_box_button = QPushButton('Open Message Box', self)
open_message_box_button.clicked.connect(self.show_message_box)
layout = QVBoxLayout()
layout.addWidget(label)
layout.addWidget(open_dialog_button)
layout.addWidget(open_message_box_button)
self.setLayout(layout)
def showDialog(self):
self.dialog = CustomDialog(self)
btn_clicked = self.dialog.exec_() # dialog exec returns 0 or 1 (Save = 1, Close and Cancel returns 0)
# I want something like this.
if btn_clicked == QDialogButtonBox.Save:
print("Close.. After Saving...")
elif btn_clicked == QDialogButtonBox.Close:
print("Close.. Without saving")
else:
print("Cancel.. Don't exit the program")
def show_message_box(self):
msg = QMessageBox()
msg.setIcon(QMessageBox.Warning)
msg.setText("Are you sure you want close the app before saving?")
msg.setStandardButtons(QMessageBox.Close | QMessageBox.Save | QMessageBox.Cancel)
msg.setWindowTitle("Data will be lost")
btn_clicked = msg.exec_()
# Here i can do this.
if btn_clicked == QMessageBox.Save:
print("Close.. After Saving...")
elif btn_clicked == QMessageBox.Close:
print("Close.. Without saving")
else:
print("Cancel.. Don't exit the program")
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
win = Window()
win.resize(200, 200)
win.show()
sys.exit(app.exec_())
I've found the solution and it worked with me by using a custom signal and slot, but still not sure if it is good or not. waiting for approval and then I can mark this solution as an accepted one.
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
class CustomDialog(QDialog):
# Create a signal
signal = pyqtSignal(int)
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle("Data will be lost")
label = QLabel("Are you sure you want close the app before saving?")
self.buttonBox = QDialogButtonBox()
self.buttonBox.addButton(QDialogButtonBox.Save)
self.buttonBox.addButton(QDialogButtonBox.Cancel)
self.buttonBox.addButton(QDialogButtonBox.Close)
layout = QVBoxLayout()
layout.addWidget(label)
layout.addWidget(self.buttonBox)
self.resize(300, 100)
self.setLayout(layout)
# connect each button with custom slot
self.buttonBox.button(QDialogButtonBox.Save).clicked.connect(lambda: self.customSlot(QDialogButtonBox.Save))
self.buttonBox.button(QDialogButtonBox.Close).clicked.connect(lambda: self.customSlot(QDialogButtonBox.Close))
self.buttonBox.button(QDialogButtonBox.Cancel).clicked.connect(lambda: self.customSlot(QDialogButtonBox.Cancel))
# connect signal with buil-in function from QDialog (done())
# This done function will return <int> value and close the dialog.
self.signal.connect(self.done)
def customSlot(self, button_id):
# emit button's id
self.signal.emit(button_id)
class Window(QWidget):
def __init__(self):
super().__init__()
label = QLabel('Hello Dialog', self)
open_dialog_button = QPushButton('Open Dialog', self)
open_dialog_button.clicked.connect(self.showDialog)
layout = QVBoxLayout()
layout.addWidget(label)
layout.addWidget(open_dialog_button)
self.setLayout(layout)
def showDialog(self):
dialog = CustomDialog()
btn_clicked = dialog.exec_() # dialog exec returns button's id
# Now you can use the following lines of code
if btn_clicked == QDialogButtonBox.Save:
print("Close.. After Saving...")
elif btn_clicked == QDialogButtonBox.Close:
print("Close.. Without saving")
else:
print("Cancel.. Don't exit the program")
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
win = Window()
win.resize(200, 200)
win.show()
sys.exit(app.exec_())

How to change the opacity of a PyQt5 window

I have just been experimenting with some animation in PyQt5 and am looking to animate the opacity of a window. I have had success with animating the opacity of buttons and QWidgets withink the window, however when I try to apply the same concept to the main QWidget class it doesn't seem to work. Below is my code for trying to get the animation to work on the window (I am aware this isn't the best looking code but I'm just trying to experiment, but also feel free to tell me about any big errors unrelated to the problem as well as I am also quite new to PyQt5):
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
import sys
class Test(QWidget):
def __init__(self):
super().__init__()
self.setStyleSheet("background: green")
self.setGeometry(100, 100, 400, 400)
self.b = QPushButton("Reduce", self, clicked=self.reduce)
self.b.show()
self.b.setStyleSheet("color: yellow")
self.show()
def reduce(self):
self.eff = QGraphicsOpacityEffect(self)
self.eff.setOpacity(1)
self.setGraphicsEffect(self.eff)
self.anim = QPropertyAnimation(self.eff, b"opacity")
self.anim.setDuration(500)
self.anim.setStartValue(1)
self.anim.setEndValue(0)
self.anim.start()
self.t = QTimer()
self.t.timeout.connect(self.loop)
self.t.start(10)
print(self.eff.opacity())
def loop(self):
self.update()
print(self.eff.opacity())
if self.anim.currentValue() == 0:
self.t.stop()
self.update()
if __name__ == "__main__":
window = QApplication(sys.argv)
app = Test()
window.exec_()
I thought maybe I am getting odd results as it isn't possible to do this when the widget is made to be the main widget in the window, however I also don't see why that would be a thing. What seems to happen for me is that the window does nothing and nothing changes, other than the button seems to half freeze, as in I am able to click it and get a result (run the function it is connected too) however no pressing down animation occurs as normally would. However, when I resize the window it will show a change, however this has to be manual as I added a basic loop to constantly resize and it did nothing. And finally when I resize the window, or change the opacity just by changing the opacity of self.eff it changes, but it just makes it all black even at values of 0.8, etc which is what leads me to believe it just isn't possible and some script is defaulting it to black, as the button will work fine while the rest goes black. Any help is appreciated.
Try like this:
import sys
from PyQt5.Qt import *
class Test(QWidget):
def __init__(self):
super().__init__()
self.setStyleSheet("background: green;")
self.resize(400, 400)
self.label = QLabel()
self.pixmap = QPixmap('Ok.png')
self.label.setPixmap(self.pixmap)
self.label.setAlignment(Qt.AlignCenter)
self.label.setStyleSheet("background: #CD113B;")
self.b = QPushButton("Reduce", self, clicked=self.reduce)
self.b.setStyleSheet("background: blue; color: yellow;")
layout = QVBoxLayout(self)
layout.addWidget(self.label)
layout.addWidget(self.b)
def reduce(self):
self.anim = QPropertyAnimation(self, b"opacity")
self.anim.setDuration(3000)
self.anim.setLoopCount(3)
self.anim.setStartValue(0.0)
self.anim.setEndValue(1.0)
self.anim.start()
def windowOpacity(self):
return super().windowOpacity()
def setWindowOpacity(self, opacity):
super().setWindowOpacity(opacity)
opacity = pyqtProperty(float, windowOpacity, setWindowOpacity)
if __name__ == "__main__":
app = QApplication(sys.argv)
w = Test()
w.show()
sys.exit(app.exec_())
or so:
import sys
from PyQt5.Qt import *
class Test(QWidget):
def __init__(self):
super().__init__()
self.setStyleSheet("background: green;")
self.resize(400, 400)
self.label = QLabel()
self.pixmap = QPixmap('Ok.png')
self.label.setPixmap(self.pixmap)
self.label.setAlignment(Qt.AlignCenter)
self.label.setStyleSheet("background: #CD113B;")
self.b = QPushButton("Reduce", self, clicked=self.reduce)
self.b.setStyleSheet("background: blue; color: yellow;")
layout = QVBoxLayout(self)
layout.addWidget(self.label)
layout.addWidget(self.b)
def reduce(self):
self.eff = QGraphicsOpacityEffect()
self.eff.setOpacity(1.0)
self.label.setGraphicsEffect(self.eff)
self.anim = QPropertyAnimation(self.eff, b"opacity")
self.anim.setDuration(3000)
self.anim.setLoopCount(3)
self.anim.setStartValue(0.0)
self.anim.setEndValue(1.0)
self.anim.start()
if __name__ == "__main__":
app = QApplication(sys.argv)
w = Test()
w.show()
sys.exit(app.exec_())
Ok.png

QFileDialog.getOpenFileName change button text from 'Open' to 'Remove'

I am using QFileDialog.getOpenFileName(self,'Remove File', "path", '*.pdf') to select a file and get the path in order to remove it from my application. The issue is the QFileDialog.getOpenFileName window button says 'Open' when selecting a file which will be confusing to the user.
Is there any way to change the button text from 'Open' to 'Remove'/'Delete'
When using the static method QFileDialog::getOpenFileName() the first thing is to obtain the QFileDialog object and for that we use a QTimer and the findChild() method:
# ...
QtCore.QTimer.singleShot(0, self.on_timeout)
filename, _ = QtWidgets.QFileDialog.getOpenFileName(...,
options=QtWidgets.QFileDialog.DontUseNativeDialog)
def on_timeout(self):
dialog = self.findChild(QtWidgets.QFileDialog)
# ...
Then you can get the text iterating over the buttons until you get the button with the searched text and change it:
for btn in dialog.findChildren(QtWidgets.QPushButton):
if btn.text() == "&Open":
btn.setText("Remove")
That will work at the beginning but every time you interact with the QTreeView they show, update the text to the default value, so the same logic will have to be applied using the currentChanged signal from the selectionModel() of the QTreeView but for synchronization reasons it is necessary Update the text later using another QTimer.singleShot(), the following code is a workable example:
import sys
from PyQt5 import QtCore, QtWidgets
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
button = QtWidgets.QPushButton("Press me")
button.clicked.connect(self.on_clicked)
lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(button)
#QtCore.pyqtSlot()
def on_clicked(self):
QtCore.QTimer.singleShot(0, self.on_timeout)
filename, _ = QtWidgets.QFileDialog.getOpenFileName(
self,
"Remove File",
"path",
"*.pdf",
options=QtWidgets.QFileDialog.DontUseNativeDialog,
)
def on_timeout(self):
dialog = self.findChild(QtWidgets.QFileDialog)
dialog.findChild(QtWidgets.QTreeView).selectionModel().currentChanged.connect(
lambda: self.change_button_name(dialog)
)
self.change_button_name(dialog)
def change_button_name(self, dialog):
for btn in dialog.findChildren(QtWidgets.QPushButton):
if btn.text() == self.tr("&Open"):
QtCore.QTimer.singleShot(0, lambda btn=btn: btn.setText("Remove"))
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())
The first step can be avoided if the static method is not used and create the dialog using an instance of QFileDialog:
import sys
from PyQt5 import QtCore, QtWidgets
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
button = QtWidgets.QPushButton("Press me")
button.clicked.connect(self.on_clicked)
lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(button)
#QtCore.pyqtSlot()
def on_clicked(self):
dialog = QtWidgets.QFileDialog(
self,
"Remove File",
"path",
"*.pdf",
supportedSchemes=["file"],
options=QtWidgets.QFileDialog.DontUseNativeDialog,
)
self.change_button_name(dialog)
dialog.findChild(QtWidgets.QTreeView).selectionModel().currentChanged.connect(
lambda: self.change_button_name(dialog)
)
if dialog.exec_() == QtWidgets.QDialog.Accepted:
filename = dialog.selectedUrls()[0]
print(filename)
def change_button_name(self, dialog):
for btn in dialog.findChildren(QtWidgets.QPushButton):
if btn.text() == self.tr("&Open"):
QtCore.QTimer.singleShot(0, lambda btn=btn: btn.setText("Remove"))
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())
While I appreciate the solution provided by #eyllanesc, I'd like to propose a variation.
Under certain circumstances, the code for that answer might fail, specifically:
the delay that X11 suffers from mapping windows;
the checking of localized button strings;
the selection using the file name edit box;
Considering the above, I propose an alternate solution, based on the points above.
For obvious reasons, the main point remains: the dialog must be non-native.
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class FileDialogTest(QWidget):
def __init__(self):
super().__init__()
layout = QHBoxLayout(self)
self.fileEdit = QLineEdit()
layout.addWidget(self.fileEdit)
self.selectBtn = QToolButton(icon=QIcon.fromTheme('folder'), text='…')
layout.addWidget(self.selectBtn)
self.selectBtn.clicked.connect(self.showSelectDialog)
def checkSelectDialog(self):
dialog = self.findChild(QFileDialog)
if not dialog.isVisible():
# wait for dialog finalization, as testOption might fail
return
# dialog found, stop the timer and delete it
self.sender().stop()
self.sender().deleteLater()
if not dialog.testOption(dialog.DontUseNativeDialog):
# native dialog, we cannot do anything!
return
def updateOpenButton():
selection = tree.selectionModel().selectedIndexes()
if selection and not tree.model().isDir(selection[0]):
# it's a file, change the button text
button.setText('Select my precious file')
tree = dialog.findChild(QTreeView)
button = dialog.findChild(QDialogButtonBox).button(
QDialogButtonBox.Open)
# check for selected files on open
updateOpenButton()
# connect the selection update signal
tree.selectionModel().selectionChanged.connect(
lambda: QTimer.singleShot(0, updateOpenButton))
def showSelectDialog(self):
QTimer(self, interval=10, timeout=self.checkSelectDialog).start()
path, filter = QFileDialog.getOpenFileName(self,
'Select file', '<path_to_file>',
"All Files (*);;Python Files (*.py);; PNG Files (*.png)",
options=QFileDialog.DontUseNativeDialog)
if path:
self.fileEdit.setText(path)
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
ex = FileDialogTest()
ex.show()
sys.exit(app.exec())
Obviously, for PyQt6 you'll need to use the proper Enum namespaces (i.e. QFileDialog.Option.DontUseNativeDialog, etc.).

pyqt5 widget does not show up in desired size

I would like to implement a class to create a simple widget of fixed size with a scrollbar to display one or more (that's crucial to the problem) images at the same time. Here is the (yet complete but working) code:
from PyQt5 import QtCore, QtWidgets, QtGui
class ImageViewWidget(QtWidgets.QScrollArea):
def __init__(self, parent = None):
super(ImageViewWidget, self).__init__(parent)
self.w = QtWidgets.QFrame()
self.l = QtWidgets.QVBoxLayout()
self.w.setLayout(self.l)
self.setWidget(self.w)
def setImages(self, *images):
self.imageLabel = QtWidgets.QLabel()
self.imageLabel.setScaledContents(True)
self.l.addWidget(self.imageLabel)
if not images[0].isNull():
self.imageLabel.setPixmap(QtGui.QPixmap.fromImage(images[0]))
self.normalSize()
## event = QtGui.QKeyEvent(QtCore.QEvent.KeyPress, QtCore.Qt.Key_N, QtCore.Qt.NoModifier)
## QtWidgets.QApplication.sendEvent(self, event)
def normalSize(self):
self.w.adjustSize()
def keyPressEvent(self, event):
if event.key() == QtCore.Qt.Key_N:
self.normalSize()
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
imageViewer = ImageViewWidget()
imageViewer.resize(800, 600)
imageViewer.show()
image1 = QtGui.QImage('test.png')
imageViewer.setImages(image1)
sys.exit(app.exec_())
The problem is, that the image does not show up at startup resp does not have the desired size. One has to press "n" first, then the image is displayed with its natural size. And of course I would like to have its natural size from the beginning on without the need to press "n" first.
It seems strange to me that pressing "n" and calling self.normalSize() do not have the same effect, and even simulation the key event by the two commented outlines in setImages do not have the same effect as pressing "n" physically.
There are two "solutions":
Show the widget after setting image, that is, move the line imageViewer.show() 2 lines down.
Moving the first 3 lines of the method setImages to the __init__ method.
Both are no reasonable option, since I want to add and remove dynamically QLabels(which is not implemented yet) to display different images, and also the number of images (which are displayed at the same time) can change.
Any suggestions?
Hi I have modified your code.
Added this 2 lines.
self.timerSingleShot = QtCore.QTimer()
self.timerSingleShot.singleShot(1, self.normalSize)
Use with PyQt5 syntax. This syntax is for PyQt4
from PyQt5 import QtCore, QtWidgets, QtGui
class ImageViewWidget(QtWidgets.QScrollArea):
def __init__(self, parent = None):
super(ImageViewWidget, self).__init__(parent)
self.w = QtWidgets.QFrame()
self.l = QtWidgets.QVBoxLayout()
self.w.setLayout(self.l)
self.setWidget(self.w)
# Added this lines
self.timerSingleShot = QtCore.QTimer()
self.timerSingleShot.singleShot(1, self.normalSize)
def setImages(self, *images):
self.imageLabel = QtWidgets.QLabel()
self.imageLabel.setScaledContents(True)
self.l.addWidget(self.imageLabel)
if not images[0].isNull():
self.imageLabel.setPixmap(QtGui.QPixmap.fromImage(images[0]))
#self.normalSize()
## event = QtGui.QKeyEvent(QtCore.QEvent.KeyPress, QtCore.Qt.Key_N, QtCore.Qt.NoModifier)
## QtWidgets.QApplication.sendEvent(self, event)
def normalSize(self):
self.w.adjustSize()
def keyPressEvent(self, event):
if event.key() == QtCore.Qt.Key_N:
self.normalSize()
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
imageViewer = ImageViewWidget()
imageViewer.resize(800, 600)
imageViewer.show()
image1 = QtGui.QImage('test.png')
imageViewer.setImages(image1)
sys.exit(app.exec_())
This will also work. Shifted line :
imageViewer.show()
in your code.
from PyQt5 import QtCore, QtWidgets, QtGui
class ImageViewWidget(QtWidgets.QScrollArea):
def __init__(self, parent = None):
super(ImageViewWidget, self).__init__(parent)
self.w = QtWidgets.QFrame()
self.l = QtWidgets.QVBoxLayout()
self.w.setLayout(self.l)
self.setWidget(self.w)
def setImages(self, *images):
self.imageLabel = QtWidgets.QLabel()
self.imageLabel.setScaledContents(True)
self.l.addWidget(self.imageLabel)
if not images[0].isNull():
self.imageLabel.setPixmap(QtGui.QPixmap.fromImage(images[0]))
#self.normalSize()
## event = QtGui.QKeyEvent(QtCore.QEvent.KeyPress, QtCore.Qt.Key_N, QtCore.Qt.NoModifier)
## QtWidgets.QApplication.sendEvent(self, event)
def normalSize(self):
self.w.adjustSize()
def keyPressEvent(self, event):
if event.key() == QtCore.Qt.Key_N:
self.normalSize()
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
imageViewer = ImageViewWidget()
imageViewer.resize(800, 600)
image1 = QtGui.QImage('test.png')
imageViewer.setImages(image1)
imageViewer.show()
sys.exit(app.exec_())

How to make a qframe highlight when cursor is on it in PyQt4?

I have the following window with frames.
I want frame to be highlighted (in my case change its shape) when mouse is in its area.
from PyQt4 import QtGui, QtCore
import sys
app = QtGui.QApplication(sys.argv)
window = QtGui.QWidget()
window_layout = QtGui.QVBoxLayout()
window.setLayout(window_layout)
#fill content
for i in range(10):
label = QtGui.QLabel(str(i))
frame = QtGui.QFrame()
frame_layout = QtGui.QVBoxLayout()
frame.setLayout(frame_layout)
frame_layout.addWidget(label)
window_layout.addWidget(frame)
def layout_widgets(layout):
return (layout.itemAt(i) for i in range(layout.count()))
def mouse_enter(event):
print 'frame enter'
w.widget().setFrameShape(3)
def mouse_leave(event):
print 'frame leave'
w.widget().setFrameShape(0)
for w in layout_widgets(window_layout):
print w.widget()
w.widget().enterEvent = mouse_enter
w.widget().leaveEvent = mouse_leave
window.show()
sys.exit(app.exec_())
It works but only the last frame in layout highlights.
How to make only that frame change its shape where the mouse is?
I've tried the following:
def mouse_enter(event, frame):
print 'frame enter'
frame.setFrameShape(3)
w.widget().enterEvent = functools.partial(mouse_enter, w.widget())
but it gives an error. I have found one more way to do that - signal mapper
but I have no idea how to use it.
The problem in your code the variable w when executing the for is left with the last element, so it will only be executed in the latter. To solve this I have implemented a Frame class that inherits from QFrame where I overwrite the enterEvent and leaveEvent functions.
from PyQt4 import QtGui, QtCore
import sys
class Frame(QtGui.QFrame):
def __init__(self, text, parent=None):
super(Frame, self).__init__(parent=parent)
label = QtGui.QLabel(text)
frame_layout = QtGui.QVBoxLayout()
frame_layout.addWidget(label)
self.setLayout(frame_layout)
def enterEvent(self, event):
self.setFrameShape(3)
def leaveEvent(self, event):
self.setFrameShape(0)
app = QtGui.QApplication(sys.argv)
window = QtGui.QWidget()
window_layout = QtGui.QVBoxLayout()
window.setLayout(window_layout)
for i in range(10):
frame = Frame(str(i))
window_layout.addWidget(frame)
window.show()
sys.exit(app.exec_())

Categories

Resources