Python Qt - Connect function to each button - python

I am trying to build an hdri browser in Maya with python/Qt but I have limited knowledge on this subject. Basically I am trying to print the path of each "jpg" on a folder.
I built an UI with buttons as images and when I click on them I need them to print the respective path.
Ex: Button_01 > prints D:/Hdris/HDRI_01.jpg, Button_02 > prints D:/Hdris/HDRI_02.jpg, etc...
I understand that my code is a bit of a mess right now, that's why I can't connect the button press to the loop I have where I create the buttons. How can I organize this better?
Thank you.
from PySide2 import QtWidgets
from PySide2 import QtGui
from PySide2 import QtCore
from shiboken2 import wrapInstance
import maya.OpenMayaUI as omui
import glob, os
def maya_main_window():
main_window_ptr = omui.MQtUtil.mainWindow()
return wrapInstance( int(main_window_ptr), QtWidgets.QWidget )
class PicButton(QtWidgets.QAbstractButton):
def __init__(self, pixmap, parent=None ):
super(PicButton, self).__init__(parent)
self.pixmap = pixmap
self.pressed.connect( self.press )
def paintEvent(self, event):
painter = QtGui.QPainter(self)
painter.drawPixmap(event.rect(), self.pixmap)
pix = painter.fillRect( event.rect(), QtGui.QBrush( QtGui.QColor (150,150,150,128) ) ) if self.underMouse() else self.pixmap
def enterEvent(self, event):
self.update()
def leaveEvent(self, event):
self.update()
def press(self):
print('Button image Pressed! To do...')
def sizeHint(self):
return QtCore.QSize(200, 100)
class TestDialog(QtWidgets.QDialog):
def __init__(self, parent=maya_main_window()):
super(TestDialog, self).__init__(parent)
self.setWindowTitle('Test Dialog')
self.setMinimumWidth(200)
self.create_widgets()
def create_widgets(self):
main_layout = QtWidgets.QVBoxLayout(self)
pathToHdris = "D:/CG_CONTENT/HDRIS/HDRI_BROWSER/"
os.chdir(pathToHdris)
for item, hdri in enumerate( glob.glob("*.jpg") ):
newPath = pathToHdris+str(hdri)
pixmap = QtGui.QPixmap(newPath)
self.my_button = PicButton(pixmap)
self.my_button.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
self.my_button.setMaximumSize(200,100)
main_layout.addWidget(self.my_button)
if __name__ == "__main__":
try:
test_dialog.close()
test_dialog.deleteLater()
except:
pass
test_dialog = TestDialog()
test_dialog.show()

Related

Trigger a paint event inside the enter event in Qt

Q: I am trying to trigger a paint event inside the enter event in Qt but I am getting an error, basically can't call the painter inside the enter mouse event. What I need is to darken the image(as button) as I hover the mouse. Is this even possible? Thank you.
from PySide2 import QtWidgets
from PySide2 import QtGui
from PySide2 import QtCore
class PicButton(QtWidgets.QAbstractButton):
def __init__(self, pixmap, parent=None):
super(PicButton, self).__init__(parent)
self.pixmap = pixmap
def paintEvent(self, event):
painter = QtGui.QPainter(self)
painter.drawPixmap(event.rect(), self.pixmap)
painter.fillRect(event.rect(), QtGui.QBrush(QtGui.QColor (0,0,0,0)))
def enterEvent(self, event):
#error with the line below: 'PySide2.QtGui.QEnterEvent' object has no attribute 'rect'
#painter.fillRect(event.rect(), QtGui.QBrush(QtGui.QColor (0,0,0,128)))
print('hovering')
def sizeHint(self):
return self.pixmap.size()
window_wid = QtWidgets.QWidget()
vlayout_wid = QtWidgets.QVBoxLayout()
myPixmap = QtGui.QPixmap("image.jpg")
my_button = PicButton(myPixmap)
my_button.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
my_button.setMaximumSize(200,100)
vlayout_wid.addWidget(my_button)
window_wid.setLayout(vlayout_wid)
window_wid.show()
Ok, I solved the problem with underMouse() within the paintEvent. Not sure if it's the best solution but it's working now. Just wish I was able to brighten the image instead of darkening.
from PySide2 import QtWidgets
from PySide2 import QtGui
from PySide2 import QtCore
class PicButton(QtWidgets.QAbstractButton):
def __init__(self, pixmap, parent=None):
super(PicButton, self).__init__(parent)
self.pixmap = pixmap
self.pressed.connect(self.update)
self.released.connect(self.update)
def paintEvent(self, event):
#pix = self.pixmap_hover if self.underMouse() else self.pixmap
painter = QtGui.QPainter(self)
painter.drawPixmap(event.rect(), self.pixmap)
pix = painter.fillRect(event.rect(), QtGui.QBrush(QtGui.QColor (0,0,0,128))) if self.underMouse() else self.pixmap
def enterEvent(self, event):
self.update()
def leaveEvent(self, event):
self.update()
def sizeHint(self):
return QtCore.QSize(200, 100)
window_wid = QtWidgets.QWidget()
vlayout_wid = QtWidgets.QVBoxLayout()
pixmap = QtGui.QPixmap("image.jpg")
my_button = PicButton(pixmap)
my_button.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
#my_button.setMaximumSize(200,100)
vlayout_wid.addWidget(my_button)
window_wid.setLayout(vlayout_wid)
window_wid.show()

How to print the file path when the mouse hovers over an item in this QTreeView with QFileSystemModel

I'm making a custom QTreeView with QFileSystem model, and I have a MouseMoveEvent set up to print the path of the item that is hovered over.
I'm way down the rabbit hole and doing all kinds of weird things to make this work.
Here is the latest minimal reproducible code:
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import sys
class MainWindow(QWidget):
def __init__(self):
QWidget.__init__(self)
self.resize(500, 300)
self.layout = QVBoxLayout()
self.setLayout(self.layout)
self.myList = CustomTreeWidget()
self.myList.model.setRootPath("/Volumes/Home/User/Desktop/testsrc")
self.myList.setObjectName("/Volumes/Home/User/Desktop/testsrc")
self.layout.addWidget(self.myList)
class CustomTreeWidget(QTreeView):
def __init__(self):
super().__init__()
self.model = QFileSystemModel()
self.model.setFilter(QDir.NoDotAndDotDot | QDir.Files)
self.setModel(self.model)
self.setAlternatingRowColors(True)
self.setDragDropMode(QAbstractItemView.DragDrop)
self.setIndentation(0)
self.setSelectionMode(QAbstractItemView.ExtendedSelection)
self.setDragEnabled(True)
self.setAcceptDrops(True)
self.setContextMenuPolicy(Qt.CustomContextMenu)
self.setMouseTracking(True)
self.model.directoryLoaded.connect(self._runwhenloaded)
def _runwhenloaded(self):
self.setRootIndex(self.model.index(self.objectName()))
self.model.setRootPath(self.objectName())
def mouseMoveEvent(self, event):
prev = ""
if self.selectedIndexes():
prev = self.selectedIndexes()[0]
x = event.x()
y = event.y()
self.setSelection(QRect(x, y, 1, 1), QItemSelectionModel.ClearAndSelect)
self.setCurrentIndex(self.selectedIndexes()[0])
print(self.model.filePath(self.currentIndex()))
if prev:
self.setCurrentIndex(prev)
# pos = QCursor.pos()
# indexat = self.indexAt(pos).row() # why -1?
# print(indexat) # why -1?
# print(self.indexAt(pos).row())
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
Obviously this example is not proper at all, as it destroys multiple selections and scrolls to the previously selected item whenever the mouse moves, and just a hack in general.
I've gone through many iterations and read everything I could put my hands on but I can't figure it out.
The closest answer seems to be HERE, but it's in C and I don't understand it.
So the question is: How to print the file path when the mouse hovers over an item in this QTreeView?
A possible solution is to create an event filter that tracks the hover events and, according to that information, emits a signal that has the QModelIndex:
import sys
from PyQt5.QtCore import (
pyqtSignal,
pyqtSlot,
Qt,
QDir,
QEvent,
QModelIndex,
QObject,
QPersistentModelIndex,
QStandardPaths,
)
from PyQt5.QtWidgets import (
QAbstractItemView,
QApplication,
QFileSystemModel,
QMainWindow,
QTreeView,
)
from PyQt5 import sip
class HoverViewHelper(QObject):
hovered = pyqtSignal(QModelIndex)
def __init__(self, view):
super().__init__(view)
self._current_index = QPersistentModelIndex()
if not isinstance(view, QAbstractItemView):
raise TypeError(f"The {view} must be of type QAbstractItemView")
self._view = view
self.view.viewport().setAttribute(Qt.WA_Hover)
self.view.viewport().installEventFilter(self)
#property
def view(self):
return self._view
def eventFilter(self, obj, event):
if sip.isdeleted(self.view):
return True
if self.view.viewport() is obj:
if event.type() in (QEvent.HoverMove, QEvent.HoverEnter):
p = event.pos()
index = self.view.indexAt(p)
self._update_index(index)
elif event.type() == QEvent.HoverLeave:
if self._current_index.isValid():
self._update_index(QModelIndex())
return super().eventFilter(obj, event)
def _update_index(self, index):
pindex = QPersistentModelIndex(index)
if pindex != self._current_index:
self._current_index = pindex
self.hovered.emit(QModelIndex(self._current_index))
class MainWindow(QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.model = QFileSystemModel()
self.model.setRootPath(QDir.rootPath())
self.view = QTreeView()
self.view.setModel(self.model)
path = QStandardPaths.writableLocation(QStandardPaths.DocumentsLocation)
self.view.setRootIndex(self.model.index(path))
self.setCentralWidget(self.view)
helper = HoverViewHelper(self.view)
helper.hovered.connect(self.handle_hovered)
#pyqtSlot(QModelIndex)
def handle_hovered(self, index):
if not index.isValid():
return
path = self.model.filePath(index)
print(f"path: {path}")
def main():
app = QApplication(sys.argv)
w = MainWindow()
w.resize(640, 480)
w.show()
app.exec_()
if __name__ == "__main__":
main()

Is there a way to create a custom animated / gif QCursor?

I am trying to create a custum animated cursor that replaces the regular cursor for when lengthy processes are taking place similar to here and here, however, I woud like mine to be animated, for example using a gif, just as it is the case with the standard Qt.WaitCursor. How can I do this? I found this solution regarding animated system tray icons, however, I didn't manage to adapt it for it to work with the cursor icon.
As a side note: When I try to execute pm.setAlphaChannel(bm) as stated in the first link it does not work for me and I get the following error:
'AttributeError: QPixmap' object has no attribute 'setAlphaChannel'
Which is odd because according to the documentation, QPixmap does have a setAlphaChannel method.
One possible solution is to create a class that handles the update of the cursor of a given widget. In the following example the cursor is set when the start button is pressed and the cursor is restored when the stop button is pressed:
from PyQt5 import QtCore, QtGui, QtWidgets
class ManagerCursor(QtCore.QObject):
def __init__(self, parent=None):
super(ManagerCursor, self).__init__(parent)
self._movie = None
self._widget = None
self._last_cursor = None
def setMovie(self, movie):
if isinstance(self._movie, QtGui.QMovie):
if not self._movie != QtGui.QMovie.NotRunning:
self._movie.stop()
del self._movie
self._movie = movie
self._movie.frameChanged.connect(self.on_frameChanged)
self._movie.started.connect(self.on_started)
self._movie.finished.connect(self.restore_cursor)
def setWidget(self, widget):
self._widget = widget
#QtCore.pyqtSlot()
def on_started(self):
if self._widget is not None:
self._last_cursor = self._widget.cursor()
#QtCore.pyqtSlot()
def restore_cursor(self):
if self._widget is not None:
if self._last_cursor is not None:
self._widget.setCursor(self._last_cursor)
self._last_cursor = None
#QtCore.pyqtSlot()
def start(self):
if self._movie is not None:
self._movie.start()
#QtCore.pyqtSlot()
def stop(self):
if self._movie is not None:
self._movie.stop()
self.restore_cursor()
#QtCore.pyqtSlot()
def on_frameChanged(self):
pixmap = self._movie.currentPixmap()
cursor = QtGui.QCursor(pixmap)
if self._widget is not None:
if self._last_cursor is None:
self._last_cursor = self._widget.cursor()
self._widget.setCursor(cursor)
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Widget, self).__init__(parent)
start_btn = QtWidgets.QPushButton("start", clicked=self.on_start)
stop_btn = QtWidgets.QPushButton("stop", clicked=self.on_stop)
self._manager = ManagerCursor(self)
movie = QtGui.QMovie("loading-icon.gif")
self._manager.setMovie(movie)
self._manager.setWidget(self)
lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(start_btn)
lay.addWidget(stop_btn)
lay.addStretch()
#QtCore.pyqtSlot()
def on_start(self):
self._manager.start()
#QtCore.pyqtSlot()
def on_stop(self):
self._manager.stop()
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.resize(640, 480)
w.show()
sys.exit(app.exec_())

PyQt5 Automatic drawing from input value

I have recently created a widget with Qpaint, which I want to pass value to it, at the same time force the Qpaint Widget to draw from input values. The idea is to define a data value from a Qdialog and pass it to main widget, and pass the value to Qpaint Widget class. I would like to have, when user clicks on the button 'Getting values' a dialog widget would appear and insert some int values, then pass it to main Widget. from there pass value to correct class Paint. Which would draw and display the result. I tried with Qlabel, to assign value first to Qlabel or QlineEdit,
class Button(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Button, self).__init__(parent)
---------
self.value = QtWidgets.QLabel()
--------
Then inside the paint class call the value or text of those. then assign it to Qpaint event. But seems does not work.'
class Paint(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Paint, self).__init__(parent)
self.button = Button()
self.Value = self.button.value
---------
painter.drawRect(100,100,250,250) <----- instead of value 250 having self.Value
The code Main.py
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from datainput import *
class Foo(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Foo, self).__init__(parent)
self.setGeometry(QtCore.QRect(200, 100, 800, 800))
self.button = Button()
self.paint = Paint()
self.lay = QtWidgets.QVBoxLayout()
self.lay.addWidget(self.paint)
self.lay.addWidget(self.button)
self.setLayout(self.lay)
class Paint(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Paint, self).__init__(parent)
self.button = Button()
self.Value = self.button.value
self.setBackgroundRole(QtGui.QPalette.Base)
self.setAutoFillBackground(True)
def paintEvent(self, event):
self.pen = QtGui.QPen()
self.brush = QtGui.QBrush( QtCore.Qt.gray, QtCore.Qt.Dense7Pattern)
painter = QtGui.QPainter(self)
painter.setRenderHint(QtGui.QPainter.Antialiasing)
painter.setPen(self.pen)
painter.setBrush(self.brush)
painter.drawRect(100,100,250,250)
painter.setBrush(QtGui.QBrush())
class Button(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Button, self).__init__(parent)
getbutton = QtWidgets.QPushButton('Getting values')
Alay = QtWidgets.QVBoxLayout(self)
Alay.addWidget(getbutton)
self.value = QtWidgets.QLabel()
getbutton.clicked.connect(self.getbuttonfunc)
def getbuttonfunc(self):
subwindow=Dinput()
subwindow.setWindowModality(QtCore.Qt.ApplicationModal)
if subwindow.exec_() == QtWidgets.QDialog.Accepted:
self._output = subwindow.valueEdit.text()
return self.value.setText(self._output)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
w = Foo()
w.show()
sys.exit(app.exec_())
Input Qdialog code, datainput.py
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class Dinput(QtWidgets.QDialog):
def __init__(self, parent=None):
super(Dinput, self).__init__(parent)
valuelabel = QtWidgets.QLabel('Input: ')
self.valueEdit = QtWidgets.QLineEdit()
buttonBox = QtWidgets.QDialogButtonBox()
buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
buttonBox.accepted.connect(self.accept)
buttonBox.rejected.connect(self.close)
self.Alay = QtWidgets.QHBoxLayout()
self.Alay.addWidget(valuelabel)
self.Alay.addWidget(self.valueEdit)
self.Blay = QtWidgets.QVBoxLayout()
self.Blay.addLayout(self.Alay)
self.Blay.addWidget(buttonBox)
self.setLayout(self.Blay)
def closeEvent(self, event):
super(Dinput, self).closeEvent(event)
def accept(self):
super(Dinput, self).accept()
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
w = Dinput()
w.show()
sys.exit(app.exec_())
Visualization
I appreciate any help. Thankssss
datainput is irrelevant, your task is only to obtain a number so for space question I will not use it and instead I will use QInputDialog::getInt(). Going to the problem, the strategy in these cases where the value can be obtained at any time is to notify the change to the other view through a signal, in the slot that receives the value is to update a variable that stores the value and call update so that it calls when necessary to paintEvent, and in the paintEvent use the variable that stores the value.
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class Foo(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Foo, self).__init__(parent)
self.setGeometry(QtCore.QRect(200, 100, 800, 800))
self.button = Button()
self.paint = Paint()
self.button.valueChanged.connect(self.paint.set_size_square)
self.lay = QtWidgets.QVBoxLayout(self)
self.lay.addWidget(self.paint)
self.lay.addWidget(self.button)
class Paint(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Paint, self).__init__(parent)
self.setBackgroundRole(QtGui.QPalette.Base)
self.setAutoFillBackground(True)
self._size_square = 250
#QtCore.pyqtSlot(int)
def set_size_square(self, v):
self._size_square = v
self.update()
def paintEvent(self, event):
pen = QtGui.QPen()
brush = QtGui.QBrush( QtCore.Qt.gray, QtCore.Qt.Dense7Pattern)
painter = QtGui.QPainter(self)
painter.setRenderHint(QtGui.QPainter.Antialiasing)
painter.setPen(pen)
painter.setBrush(brush)
r = QtCore.QRect(QtCore.QPoint(100, 100), self._size_square*QtCore.QSize(1, 1))
painter.drawRect(r)
class Button(QtWidgets.QWidget):
valueChanged = QtCore.pyqtSignal(int)
def __init__(self, parent=None):
super(Button, self).__init__(parent)
getbutton = QtWidgets.QPushButton('Getting values')
Alay = QtWidgets.QVBoxLayout(self)
Alay.addWidget(getbutton)
self.value = QtWidgets.QLabel()
getbutton.clicked.connect(self.getbuttonfunc)
#QtCore.pyqtSlot()
def getbuttonfunc(self):
number, ok = QtWidgets.QInputDialog.getInt(self, self.tr("Set Number"),
self.tr("Input:"), 1, 1)
if ok:
self.valueChanged.emit(number)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
w = Foo()
w.show()
sys.exit(app.exec_())

pyside key event invalid

I'm trying to write an image display function that instantiates the following class and renders the image when I double-click the image, I wanted to close the window by pressing ESC, but it didn't seem to work. The keyPressEvent function was written before copying. It worked before, but it didn't work here. I'm sad.
class ImageLabel(QtGui.QWidget):
def __init__(self, imagePath, parent=None):
super(ImageLabel, self).__init__(parent)
self.imagePath = imagePath
self.initUI()
def initUI(self):
from PIL import Image
pic_size = (Image.open(self.imagePath).width, Image.open(self.imagePath).height)
self.image_Label = QtGui.QLabel()
self.image_Label.setWindowIcon(QtGui.QIcon(fileconfig.MAIN_ICON))
self.image_Label.resize(pic_size[0] + 50, pic_size[1] + 50)
self.image_Label.setWindowTitle(os.path.basename(self.imagePath))
self.image_Label.setWindowModality(QtCore.Qt.ApplicationModal)
self.image_Label.setPixmap(QtGui.QPixmap(self.imagePath))
self.image_Label.setAlignment(QtCore.Qt.AlignCenter)
self.image_Label.show()
def keyPressEvent(self, event):
key = event.key()
if key == QtCore.Qt.Key_Escape:
self.close()
Here is working code:
from PySide import QtGui,QtCore
import sys
import os
class ImageLabel(QtGui.QWidget):
def __init__(self, imagePath, parent=None):
super(ImageLabel, self).__init__(parent)
self.imagePath = imagePath
self.initUI()
def initUI(self):
from PIL import Image
pic_size = (Image.open(self.imagePath).width,
Image.open(self.imagePath).height)
self.image_Label = QtGui.QLabel()
self.image_Label.setWindowIcon(QtGui.QIcon(fileconfig.MAIN_ICON))
self.image_Label.resize(pic_size[0] + 50, pic_size[1] + 50)
self.image_Label.setWindowTitle(os.path.basename(self.imagePath))
self.image_Label.setWindowModality(QtCore.Qt.ApplicationModal)
self.image_Label.setPixmap(QtGui.QPixmap(self.imagePath))
self.image_Label.setAlignment(QtCore.Qt.AlignCenter)
# self.image_Label.show()
def keyPressEvent(self, event):
key = event.key()
if key == QtCore.Qt.Key_Escape:
self.close()
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
il = ImageLabel('usr/image/img.png')
il.show()
sys.exit(app.exec_())
I just commented one line self.image_Label.show() and everything works fine for me

Categories