PySide/PyQT5: How to emit signals from a QGraphicsItem? - python

I want to emit a signal from a QGraphicsItem when it is doubled-clicked, in order to change a widget in the main window. The graphics-scene/-item does not provide an emit() method, but I was just wondering if there is an alternate way to do this. The code below has a function within a QGraphicsView class that will print to the terminal when an item is double-clicked. How can I make that into a slot/signal instead (if QGraphicsItem does not support signal/slots)?
import sys
from PySide.QtCore import *
from PySide.QtGui import *
class MyFrame(QGraphicsView):
def __init__( self, parent = None ):
super(MyFrame, self).__init__(parent)
scene = QGraphicsScene()
self.setScene(scene)
self.setFixedSize(500, 500)
pen = QPen(QColor(Qt.green))
brush = QBrush(pen.color().darker(150))
item = scene.addEllipse(0, 0, 45, 45, pen, brush)
item.setPos(0,0)
def mouseDoubleClickEvent(self, event):
print("Circle Clicked!")
# this double click event prints to terminal but how to setup
# signal/slot to update the QWidget QLabel text instead?
class Example(QWidget):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
hbox = QHBoxLayout(self)
top = QLabel("Double Click Green Circle (Howto change this QWidget Label with signals?)")
bottom = MyFrame()
splitter = QSplitter(Qt.Vertical)
splitter.addWidget(top)
splitter.addWidget(bottom)
hbox.addWidget(splitter)
self.setLayout(hbox)
self.setGeometry(0, 0, 500, 600)
self.show()
def main():
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
if __name__ == '__main__':
main()

Below is a simple example showing one way to emit signals from a graphics-item. This defines a custom signal on a subclass of QGraphicsScene and then uses the scene() method of graphics-items to emit it:
import sys
from PySide import QtCore, QtGui
class GraphicsScene(QtGui.QGraphicsScene):
itemDoubleClicked = QtCore.Signal(object)
class GraphicsRectangle(QtGui.QGraphicsRectItem):
def mouseDoubleClickEvent(self, event):
self.scene().itemDoubleClicked.emit(self)
class Window(QtGui.QWidget):
def __init__(self):
super(Window, self).__init__()
self.view = QtGui.QGraphicsView()
self.scene = GraphicsScene(self)
self.view.setScene(self.scene)
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.view)
for i in range(1, 4):
self.scene.addItem(GraphicsRectangle(50 * i, 50 * i, 20, 20))
self.scene.itemDoubleClicked.connect(self.handleItemDoubleClicked)
def handleItemDoubleClicked(self, item):
print(item.boundingRect())
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
window = Window()
window.setGeometry(600, 100, 300, 200)
window.show()
sys.exit(app.exec_())
UPDATE:
Below is a an example based on the code in your question. The basic idea is the same: define a custom signal on an available QObject (the graphics-view in this case), and use that to emit the double-click notification.
import sys
from PySide.QtCore import *
from PySide.QtGui import *
class MyFrame(QGraphicsView):
itemDoubleClicked = Signal(object)
def __init__(self, parent=None):
super(MyFrame, self).__init__(parent)
scene = QGraphicsScene()
self.setScene(scene)
self.setFixedSize(500, 500)
for i, color in enumerate('red blue green'.split()):
pen = QPen(QColor(color))
brush = QBrush(pen.color().darker(150))
item = scene.addEllipse(i * 50, i * 50, 45, 45, pen, brush)
item.setData(0, color.upper())
def mouseDoubleClickEvent(self, event):
item = self.itemAt(event.pos())
if item is not None:
self.itemDoubleClicked.emit(item)
class Example(QWidget):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
hbox = QHBoxLayout(self)
top = QLabel('Double Click a Circle')
bottom = MyFrame()
bottom.itemDoubleClicked.connect(
lambda item, top=top:
top.setText('Double Clicked: %s' % item.data(0)))
splitter = QSplitter(Qt.Vertical)
splitter.addWidget(top)
splitter.addWidget(bottom)
hbox.addWidget(splitter)
self.setLayout(hbox)
self.setGeometry(0, 0, 500, 600)
self.show()
def main():
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
if __name__ == '__main__':
main()

Related

Have QScrollArea react on wheelEvent, also in space taken up by children?

Consider this example:
#!/usr/bin/env python
import sys,os
from PyQt5 import QtCore, QtWidgets, QtGui
from PyQt5.QtCore import Qt
class MainWindow(QtWidgets.QMainWindow):
class ScrollAreaWheel(QtWidgets.QScrollArea): # SO:9475772
def __init__(self, parent=None):
super(MainWindow.ScrollAreaWheel, self).__init__(parent)
self.parent = parent
def wheelEvent(self, event):
print("wheelEvent", event.angleDelta().y())
def __init__(self):
#~ self.do_init = QtCore.QEvent.registerEventType()
QtWidgets.QMainWindow.__init__(self)
self.setMinimumWidth(1000)
self.setMinimumHeight(400)
self.frame1 = QtWidgets.QFrame(self)
self.frame1.setFrameShape(QtWidgets.QFrame.StyledPanel)
self.frame1layout = QtWidgets.QGridLayout(self.frame1)
self.frame1layout.setSpacing(0);
self.frame1layout.setContentsMargins(0,0,0,0);
self.frame1widget = QtWidgets.QWidget()
self.frame1widget.setLayout(QtWidgets.QGridLayout())
self.frame1layout.addWidget(self.frame1widget)
self.frame1scroll = MainWindow.ScrollAreaWheel(self) #QtWidgets.QScrollArea()
self.frame1scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
self.frame1scroll.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
self.frame1widget.layout().addWidget(self.frame1scroll, 0, 0) #, Qt.AlignCenter)
#self.frame1scrolllayout = QtWidgets.QHBoxLayout(self.frame1scroll)
self.frame1scrolllayout = QtWidgets.QGridLayout(self.frame1scroll)
self.frame1scroll.setWidget(self.frame1scrolllayout.widget())
self.frame1scroll.setWidgetResizable(True)
self.frame1scroll.setAlignment(Qt.AlignCenter)
self.frame1label = QtWidgets.QLabel()
self.frame1scrolllayout.addWidget(self.frame1label, 0, 0, Qt.AlignCenter) ##
pixmap = QtGui.QPixmap(200, 100)
pixmap.fill(Qt.red)
self.frame1label.setPixmap(pixmap)
self.frame2 = QtWidgets.QFrame(self)
self.frame2.setFrameShape(QtWidgets.QFrame.StyledPanel)
self.frame2layout = QtWidgets.QHBoxLayout(self.frame2)
self.frame2layout.setSpacing(0);
self.frame2layout.setContentsMargins(0,0,0,0);
self.frame2scroll = QtWidgets.QScrollArea(self)
self.frame2scroll.setWidgetResizable(True)
self.frame2widget = QtWidgets.QWidget()
self.frame2widget.setLayout(QtWidgets.QGridLayout())
self.frame2scroll.setWidget(self.frame2widget)
self.frame2layout.addWidget(self.frame2scroll)
self.mainwid = QtWidgets.QWidget()
self.mainwid.setLayout(QtWidgets.QGridLayout())
self.setCentralWidget(self.mainwid)
self.splitter1 = QtWidgets.QSplitter(Qt.Horizontal)
self.splitter1.addWidget(self.frame1)
self.splitter1.addWidget(self.frame2)
self.splitter1.setSizes([600, 600]); # equal splitter at start
self.mainwid.layout().addWidget(self.splitter1)
self.mainwid.layout().update()
if __name__ == "__main__":
app = QtWidgets.QApplication([])
main = MainWindow()
main.show()
sys.exit(app.exec_())
It generates this (Ubuntu 18.04):
I want to use mousewheel only on the left QScrollArea, for which I've made a separate class. However, its wheelEvent fires only when I'm outside the red box, not when I hover over it. How can I make ScrollAreaWheel.wheelEvent fire even when mouse is over the child label (the red box)?
You are the QLabel placing on top of the QScrollArea instead of placing it inside, visually it is the same but at the level of events it is not.
from PyQt5 import QtCore, QtGui, QtWidgets
class ScrollAreaWheel(QtWidgets.QScrollArea):
def wheelEvent(self, event):
print("wheelEvent", event.angleDelta().y())
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.setMinimumSize(1000, 400)
frame1 = QtWidgets.QFrame(frameShape=QtWidgets.QFrame.StyledPanel)
scrollarea1 = ScrollAreaWheel(widgetResizable=True)
scrollarea1.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
scrollarea1.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
widget1 = QtWidgets.QWidget()
scrollarea1.setWidget(widget1)
label_lay = QtWidgets.QGridLayout(widget1)
lay1 = QtWidgets.QVBoxLayout(frame1)
lay1.addWidget(scrollarea1)
pixmap = QtGui.QPixmap(200, 100)
pixmap.fill(QtCore.Qt.red)
label = QtWidgets.QLabel(pixmap=pixmap)
label_lay.addWidget(label, 0, 0, QtCore.Qt.AlignCenter)
#==============================
frame2 = QtWidgets.QFrame(frameShape=QtWidgets.QFrame.StyledPanel)
scrollarea2 = QtWidgets.QScrollArea(widgetResizable=True)
scrollarea2.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
scrollarea2.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
widget2 = QtWidgets.QWidget()
scrollarea2.setWidget(widget2)
splitter = QtWidgets.QSplitter(QtCore.Qt.Horizontal)
splitter.addWidget(frame1)
splitter.addWidget(frame2)
splitter.setSizes([600, 600])
self.setCentralWidget(splitter)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.resize(640, 480)
w.show()
sys.exit(app.exec_())

PyQt - Custom scrolling with QListWidget

I am trying to figure out a way to customize the scrollbars for QListWidget to have the scrollbars above and below the QListWidget instead of the normal vertical and horizontal scrollbars.
Please check out my example below if you don't understand what I mean.
In the example below I use QPushButtons with QTimers controlling the scrolling in place of the scrollbars but what I am looking for are scrollbars like the ones in QMenu when menu scrolling is enabled.
If that is not an option, I am wondering if there is a scrollbar signal or something that I could try to use to know when the scrollbars are normally activated? That way I can show/hide the buttons as needed. Thanks.
import sys
from PyQt5.QtCore import pyqtSignal, QTimer, Qt
from PyQt5.QtGui import QPainter
from PyQt5.QtWidgets import QWidget, QPushButton, QVBoxLayout, \
QApplication, QStyle, QListWidget, QStyleOptionButton, QListWidgetItem
class UpBtn(QPushButton):
mouseHover = pyqtSignal()
def __init__(self):
QPushButton.__init__(self)
self.setMouseTracking(True)
self.timer = QTimer()
def paintEvent(self, event):
painter = QPainter()
painter.begin(self)
opt = QStyleOptionButton()
self.initStyleOption(opt)
self.style().drawControl(QStyle.CE_ScrollBarSubLine, opt, painter, self)
painter.end()
def startScroll(self):
self.mouseHover.emit()
def enterEvent(self, event):
self.timer.timeout.connect(self.startScroll)
self.timer.start(120)
def leaveEvent(self, event):
self.timer.stop()
class DwnBtn(QPushButton):
mouseHover = pyqtSignal()
def __init__(self):
QPushButton.__init__(self)
self.setMouseTracking(True)
self.timer = QTimer()
def paintEvent(self, event):
painter = QPainter()
painter.begin(self)
opt = QStyleOptionButton()
self.initStyleOption(opt)
self.style().drawControl(QStyle.CE_ScrollBarAddLine, opt, painter, self)
painter.end()
def startScroll(self):
self.mouseHover.emit()
def enterEvent(self, event):
self.timer.timeout.connect(self.startScroll)
self.timer.start(120)
def leaveEvent(self, event):
self.timer.stop()
class Window(QWidget):
def __init__(self):
super(Window, self).__init__()
self.layout = QVBoxLayout()
self.layout.setContentsMargins(0, 0, 0, 0)
self.layout.setSpacing(0)
self.upBtn = UpBtn()
self.upBtn.setFixedWidth(230)
self.layout.addWidget(self.upBtn)
self.listWidget = QListWidget()
self.listWidget.setFixedWidth(230)
self.listWidget.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.listWidget.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.layout.addWidget(self.listWidget)
self.downBtn = DwnBtn()
self.downBtn.setFixedWidth(230)
self.layout.addWidget(self.downBtn)
self.setLayout(self.layout)
self.upBtn.clicked.connect(self.upBtnClicked)
self.upBtn.mouseHover.connect(self.upBtnClicked)
self.downBtn.clicked.connect(self.downBtnClicked)
self.downBtn.mouseHover.connect(self.downBtnClicked)
for i in range(100):
item = QListWidgetItem()
item.setText("list item " + str(i))
self.listWidget.addItem(item)
def upBtnClicked(self):
cur = self.listWidget.currentRow()
self.listWidget.setCurrentRow(cur - 1)
def downBtnClicked(self):
cur = self.listWidget.currentRow()
self.listWidget.setCurrentRow(cur + 1)
if __name__ == '__main__':
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
EDIT:
Here is an example image for what I am talking about. This is a scrollable QMenu.
EDIT:
Scrollable QMenu code.
Uncomment the commented parts to get a fixed size like in the image. Normally Qmenu scrolling only works when the menu items exceed the screen height. I am just looking for the top and bottom hover style scrolling but to be used in QListWidget.
import sys
from PyQt5.QtCore import QPoint, QEvent
from PyQt5.QtWidgets import QWidget, QPushButton, QVBoxLayout, \
QApplication, QAction, QMenu, QProxyStyle, QStyle
class MyMenu(QMenu):
def event(self, event):
if event.type() == QEvent.Show:
self.move(self.parent().mapToGlobal(QPoint(-108, 0)))
return super(MyMenu, self).event(event)
# class CustomStyle(QProxyStyle):
# def pixelMetric(self, QStyle_PixelMetric, option=None, widget=None):
# if QStyle_PixelMetric == QStyle.PM_MenuScrollerHeight:
# return 15
# if QStyle_PixelMetric == QStyle.PM_MenuDesktopFrameWidth:
# return 290
# else:
# return QProxyStyle.pixelMetric(self, QStyle_PixelMetric, option, widget)
class MainWindow(QWidget):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.layout = QVBoxLayout()
self.btn = QPushButton("Button")
self.btn.setFixedHeight(30)
self.btn.setFixedWidth(100)
self.myMenu = MyMenu("Menu", self.btn)
self.btn.setMenu(self.myMenu)
self.layout.addWidget(self.btn)
self.setLayout(self.layout)
menus = []
for _ in range(5):
myMenus = QMenu("Menu"+str(_+1), self.btn)
# myMenus.setFixedHeight(120)
myMenus.setStyleSheet("QMenu{menu-scrollable: 1; }")
menus.append(myMenus)
for i in menus:
self.btn.menu().addMenu(i)
for item in range(100):
action = QAction("item" + str(item), self)
i.addAction(action)
if __name__ == '__main__':
app = QApplication(sys.argv)
# app.setStyle(CustomStyle())
w = MainWindow()
w.show()
app.exec_()
The idea is to obtain the row of the upper and lower element that will decide whether the buttons are hidden or not, for that we use the method itemAt () that returns the item given the geometrical coordinates. On the other hand I have improved this calculation has to do every time they change the number of items in the QListView for that we use the signals of the internal model.
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class Button(QtWidgets.QPushButton):
moveSignal = QtCore.pyqtSignal()
def __init__(self, *args, **kwargs):
super(Button, self).__init__(*args, **kwargs)
self.m_timer = QtCore.QTimer(self, interval=120)
self.m_timer.timeout.connect(self.moveSignal)
self.setMouseTracking(True)
self.setFixedHeight(20)
def mouseReleaseEvent(self, e):
super(Button, self).mousePressEvent(e)
self.setDown(True)
def enterEvent(self, e):
self.setDown(True)
self.m_timer.start()
super(Button, self).enterEvent(e)
def leaveEvent(self, e):
self.setDown(False)
self.m_timer.stop()
super(Button, self).leaveEvent(e)
class Window(QtWidgets.QWidget):
def __init__(self):
super(Window, self).__init__()
self.setFixedWidth(230)
icon = self.style().standardIcon(QtWidgets.QStyle.SP_ArrowUp)
self.upBtn = Button(icon=icon)
self.upBtn.moveSignal.connect(self.moveUp)
icon = self.style().standardIcon(QtWidgets.QStyle.SP_ArrowDown)
self.downBtn = Button(icon=icon)
self.downBtn.moveSignal.connect(self.moveDown)
self.listWidget = QtWidgets.QListWidget()
self.listWidget.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.listWidget.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
layout = QtWidgets.QVBoxLayout(self)
layout.setContentsMargins(0, 0, 0, 0)
layout.setSpacing(0)
layout.addWidget(self.upBtn)
layout.addWidget(self.listWidget)
layout.addWidget(self.downBtn)
self.adjust_buttons()
self.create_connections()
def create_connections(self):
self.listWidget.currentItemChanged.connect(self.adjust_buttons)
model = self.listWidget.model()
model.rowsInserted.connect(self.adjust_buttons)
model.rowsRemoved.connect(self.adjust_buttons)
model.rowsMoved.connect(self.adjust_buttons)
model.modelReset.connect(self.adjust_buttons)
model.layoutChanged.connect(self.adjust_buttons)
#QtCore.pyqtSlot()
def adjust_buttons(self):
first = self.listWidget.itemAt(QtCore.QPoint())
r = self.listWidget.row(first)
self.upBtn.setVisible(r != 0 and r!= -1)
last = self.listWidget.itemAt(self.listWidget.viewport().rect().bottomRight())
r = self.listWidget.row(last)
self.downBtn.setVisible( r != (self.listWidget.count() -1) and r != -1)
#QtCore.pyqtSlot()
def moveUp(self):
ix = self.listWidget.moveCursor(QtWidgets.QAbstractItemView.MoveUp, QtCore.Qt.NoModifier)
self.listWidget.setCurrentIndex(ix)
#QtCore.pyqtSlot()
def moveDown(self):
ix = self.listWidget.moveCursor(QtWidgets.QAbstractItemView.MoveDown, QtCore.Qt.NoModifier)
self.listWidget.setCurrentIndex(ix)
#QtCore.pyqtSlot(str)
def add_item(self, text):
item = QtWidgets.QListWidgetItem(text)
self.listWidget.addItem(item)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = Window()
for i in range(100):
window.add_item("item {}".format(i))
window.show()
sys.exit(app.exec_())

How to make a widget attached to QTabWidget's gap and move accordingly with QSplitter in pyqt5

I want to make the button always next to the second tab's label, even when I use splitter to move the edge between two tabs. How to achieve that? I believe it's because I use the absolute position, but how make an widget attached in the gap in QTabWidget?
from PyQt5.QtWidgets import (QWidget, QHBoxLayout, QFrame,
QSplitter, QStyleFactory, QApplication,QTabWidget,QPushButton)
from PyQt5.QtCore import Qt
import sys
class Example(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
hbox = QHBoxLayout(self)
tab1 = QTabWidget(self)
tab2 = QTabWidget(self)
a=QWidget()
a.setGeometry(0,0, 40, 40)
tab2.addTab(a,"2")
splitter1 = QSplitter(Qt.Vertical)
splitter1.addWidget(tab1)
splitter1.addWidget(tab2)
splitter1.setSizes([100,100])
hbox.addWidget(splitter1)
self.setLayout(hbox)
z=QPushButton(self)
z.setGeometry(100,100,70,20)
z.setText("button")
self.setGeometry(300, 300, 300, 200)
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
This is what I want to achieve all the time.
This is what happened when I adjust the edge between two tabs.
When you add a parent to a widget, the widget has a coordinate system relative to the parent, so you must change the parent. In addition to adjusting the position of the button to one relative to the parent:
[...]
z=QPushButton(tab2)
z.setGeometry(100,0,70,20)
z.setText("button")
[...]
Another series improvement that even if the size of the QtabWidget changes, the button remains centered:
class TabWidgetWithButton(QTabWidget):
def __init__(self, *args, **kwargs):
QTabWidget.__init__(self, *args, **kwargs)
self.button = QPushButton("button", self)
def resizeEvent(self, event):
self.button.move((self.width()-self.button.width())/2, 0)
QTabWidget.resizeEvent(self, event)
class Example(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
hbox = QHBoxLayout(self)
tab1 = QTabWidget(self)
tab2 = TabWidgetWithButton(self)
a=QWidget(self)
tab2.addTab(a,"2")
splitter1 = QSplitter(Qt.Vertical)
splitter1.addWidget(tab1)
splitter1.addWidget(tab2)
splitter1.setSizes([100,100])
hbox.addWidget(splitter1)
self.setLayout(hbox)
self.setGeometry(300, 300, 300, 200)
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())

PySide/PyQt Overlay widget

I am trying to achieve something like this in PySide: https://codepen.io/imprakash/pen/GgNMXO
What I want to do is create a child window frameless with a black overlay below.
I didn't succeed to create a child window frameless and the overlay...
This is a base code to replicate the HTML:
from PySide import QtCore, QtGui
import sys
class MainWindow(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
self.resize(800, 500)
self.button = QtGui.QPushButton("Click Me")
self.setLayout(QtGui.QVBoxLayout())
self.layout().addWidget(self.button)
# Connections
self.button.clicked.connect(self.displayOverlay)
def displayOverlay(self):
popup = QtGui.QDialog(self)
popup.setWindowFlags(QtCore.Qt.FramelessWindowHint)
popup.setLayout(QtGui.QHBoxLayout())
popup.layout().addWidget(QtGui.QLabel("HI"))
popup.show()
print "clicked"
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
If you comment the line with the FramelessWindowHint, the window comes, else nothing happen...
I really hope that someone could help me. Thank you for the time you spent to read my question.
I'll be using PyQt5 for this explanation. It might have some differences to PySide (which I'm not sure if its still maintained) and PyQt4, but it shouldn't be too hard to convert.
The following example has a parent widget which a few buttons. One of them (the obvious one) calls for the popup. I've prepared the example to deal with the parent resize but have not made any code regarding mouse events of dragging the popup (see mouseMoveEvent and mouseReleaseEvent for that).
So here is the code:
import sys
from PyQt5 import QtWidgets, QtCore, QtGui
class TranslucentWidgetSignals(QtCore.QObject):
# SIGNALS
CLOSE = QtCore.pyqtSignal()
class TranslucentWidget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(TranslucentWidget, self).__init__(parent)
# make the window frameless
self.setWindowFlags(QtCore.Qt.FramelessWindowHint)
self.setAttribute(QtCore.Qt.WA_TranslucentBackground)
self.fillColor = QtGui.QColor(30, 30, 30, 120)
self.penColor = QtGui.QColor("#333333")
self.popup_fillColor = QtGui.QColor(240, 240, 240, 255)
self.popup_penColor = QtGui.QColor(200, 200, 200, 255)
self.close_btn = QtWidgets.QPushButton(self)
self.close_btn.setText("x")
font = QtGui.QFont()
font.setPixelSize(18)
font.setBold(True)
self.close_btn.setFont(font)
self.close_btn.setStyleSheet("background-color: rgb(0, 0, 0, 0)")
self.close_btn.setFixedSize(30, 30)
self.close_btn.clicked.connect(self._onclose)
self.SIGNALS = TranslucentWidgetSignals()
def resizeEvent(self, event):
s = self.size()
popup_width = 300
popup_height = 120
ow = int(s.width() / 2 - popup_width / 2)
oh = int(s.height() / 2 - popup_height / 2)
self.close_btn.move(ow + 265, oh + 5)
def paintEvent(self, event):
# This method is, in practice, drawing the contents of
# your window.
# get current window size
s = self.size()
qp = QtGui.QPainter()
qp.begin(self)
qp.setRenderHint(QtGui.QPainter.Antialiasing, True)
qp.setPen(self.penColor)
qp.setBrush(self.fillColor)
qp.drawRect(0, 0, s.width(), s.height())
# drawpopup
qp.setPen(self.popup_penColor)
qp.setBrush(self.popup_fillColor)
popup_width = 300
popup_height = 120
ow = int(s.width()/2-popup_width/2)
oh = int(s.height()/2-popup_height/2)
qp.drawRoundedRect(ow, oh, popup_width, popup_height, 5, 5)
font = QtGui.QFont()
font.setPixelSize(18)
font.setBold(True)
qp.setFont(font)
qp.setPen(QtGui.QColor(70, 70, 70))
tolw, tolh = 80, -5
qp.drawText(ow + int(popup_width/2) - tolw, oh + int(popup_height/2) - tolh, "Yep, I'm a pop up.")
qp.end()
def _onclose(self):
print("Close")
self.SIGNALS.CLOSE.emit()
class ParentWidget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(ParentWidget, self).__init__(parent)
self._popup = QtWidgets.QPushButton("Gimme Popup!!!")
self._popup.setFixedSize(150, 40)
self._popup.clicked.connect(self._onpopup)
self._other1 = QtWidgets.QPushButton("A button")
self._other2 = QtWidgets.QPushButton("A button")
self._other3 = QtWidgets.QPushButton("A button")
self._other4 = QtWidgets.QPushButton("A button")
hbox = QtWidgets.QHBoxLayout()
hbox.addWidget(self._popup)
hbox.addWidget(self._other1)
hbox.addWidget(self._other2)
hbox.addWidget(self._other3)
hbox.addWidget(self._other4)
self.setLayout(hbox)
self._popframe = None
self._popflag = False
def resizeEvent(self, event):
if self._popflag:
self._popframe.move(0, 0)
self._popframe.resize(self.width(), self.height())
def _onpopup(self):
self._popframe = TranslucentWidget(self)
self._popframe.move(0, 0)
self._popframe.resize(self.width(), self.height())
self._popframe.SIGNALS.CLOSE.connect(self._closepopup)
self._popflag = True
self._popframe.show()
def _closepopup(self):
self._popframe.close()
self._popflag = False
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
main = ParentWidget()
main.resize(500, 500)
main.show()
sys.exit(app.exec_())
Which results in the following:
The logic is the following. You create an empty Widget and manually draw the background and popup (paintEvent). You add a button for closing the popup. For this you build a Signal and let the parent widget do the closing. This is important because you need to make the parent widget control some important elements of the popup (such as closing, resizng, etc.). You can add far more complexity but hopefully the example will suffice for starters.
Thanks to armatita, I succeed to get what I wanted. For now, there are some issues but it works and I get the result that I wanted.
I give you the code to the next who will be looking for the same thing.
from PySide import QtCore, QtGui
import sys
class CtmWidget(QtGui.QWidget):
def __init__(self, parent = None):
QtGui.QWidget.__init__(self, parent)
self.button = QtGui.QPushButton("Close Overlay")
self.setLayout(QtGui.QHBoxLayout())
self.layout().addWidget(self.button)
self.button.clicked.connect(self.hideOverlay)
def paintEvent(self, event):
painter = QtGui.QPainter()
painter.begin(self)
painter.setRenderHint(QtGui.QPainter.Antialiasing)
path = QtGui.QPainterPath()
path.addRoundedRect(QtCore.QRectF(self.rect()), 10, 10)
mask = QtGui.QRegion(path.toFillPolygon().toPolygon())
pen = QtGui.QPen(QtCore.Qt.white, 1)
painter.setPen(pen)
painter.fillPath(path, QtCore.Qt.white)
painter.drawPath(path)
painter.end()
def hideOverlay(self):
self.parent().hide()
class Overlay(QtGui.QWidget):
def __init__(self, parent, widget):
QtGui.QWidget.__init__(self, parent)
palette = QtGui.QPalette(self.palette())
palette.setColor(palette.Background, QtCore.Qt.transparent)
self.setPalette(palette)
self.widget = widget
self.widget.setParent(self)
def paintEvent(self, event):
painter = QtGui.QPainter()
painter.begin(self)
painter.setRenderHint(QtGui.QPainter.Antialiasing)
painter.fillRect(event.rect(), QtGui.QBrush(QtGui.QColor(0, 0, 0, 127)))
painter.end()
def resizeEvent(self, event):
position_x = (self.frameGeometry().width()-self.widget.frameGeometry().width())/2
position_y = (self.frameGeometry().height()-self.widget.frameGeometry().height())/2
self.widget.move(position_x, position_y)
event.accept()
class MainWindow(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
self.resize(800, 500)
self.button = QtGui.QPushButton("Click Me")
self.setLayout(QtGui.QVBoxLayout())
self.layout().addWidget(self.button)
self.popup = Overlay(self, CtmWidget())
self.popup.hide()
# Connections
self.button.clicked.connect(self.displayOverlay)
def displayOverlay(self):
self.popup.show()
print "clicked"
def resizeEvent(self, event):
self.popup.resize(event.size())
event.accept()
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
Once again thank you both of you(ymmx and armatita) to spend time on my issue.
did you try replacing popup.show() by popup.exec_()? and remove self as a parameter of the Qdialog? I change QDialog to QmessageBox to be able to quit the subwindow but it still work with the QDialog.
popup = QMessageBox()
popup.setWindowFlags( Qt.FramelessWindowHint)
popup.setLayout( QHBoxLayout())
popup.layout().addWidget( QLabel("HI"))
popup.exec_()
update
class Popup(QDialog ):
def __init__(self):
super().__init__()
self.setWindowFlags( Qt.CustomizeWindowHint)
self.setLayout( QHBoxLayout())
Button_close = QPushButton('close')
self.layout().addWidget( QLabel("HI"))
self.layout().addWidget( Button_close)
Button_close.clicked.connect( self.close )
self.exec_()
print("clicked")
def mousePressEvent(self, event):
self.oldPos = event.globalPos()
def mouseMoveEvent(self, event):
delta = QPoint (event.globalPos() - self.oldPos)
#print(delta)
self.move(self.x() + delta.x(), self.y() + delta.y())
self.oldPos = event.globalPos()
class MainWindow( QWidget):
def __init__(self):
QWidget.__init__(self)
self.resize(800, 500)
self.button = QPushButton("Click Me")
self.setLayout( QVBoxLayout())
self.layout().addWidget(self.button)
# Connections
self.button.clicked.connect(self.displayOverlay)
def displayOverlay(self):
Popup( )
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())

force python pyside splitter to start window center

How can I force the splitter to be positioned in the center of the window at the start? As you can see in the code below it favors the right side because of the button being small. however I would like to have the splitter always appear in the middle of the window as shown in image two.
Current
Goal
import sys
from PySide import QtGui, QtCore
class Example(QtGui.QWidget):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
# formatting
self.resize(550, 400)
self.setWindowTitle("Cameras")
# widgets
self.ListA = QtGui.QTreeWidget()
self.ListB = QtGui.QTreeWidget()
self.Button = QtGui.QPushButton()
# layout Splitter
self.mainLayout = QtGui.QHBoxLayout(self)
self.mainLayout.setContentsMargins(5,5,5,5)
self.leftPanel = QtGui.QFrame(self)
# self.leftPanel.setFrameShape(QtGui.QFrame.StyledPanel)
self.leftPanelLayout = QtGui.QHBoxLayout(self.leftPanel)
self.leftPanelLayout.setContentsMargins(0,0,0,0)
self.leftPanelLayout.addWidget(self.ListA)
self.rightPanel = QtGui.QFrame(self)
# self.rightPanel.setFrameShape(QtGui.QFrame.StyledPanel)
self.rightPanelLayout = QtGui.QHBoxLayout(self.rightPanel)
self.rightPanelLayout.setContentsMargins(0,0,0,0)
self.rightPanelLayout.addWidget(self.Button)
self.splitter = QtGui.QSplitter(QtCore.Qt.Horizontal)
self.splitter.addWidget(self.leftPanel)
self.splitter.addWidget(self.rightPanel)
self.mainLayout.addWidget(self.splitter)
self.setGeometry(300, 300, 300, 200)
self.setWindowTitle('QtGui.QSplitter')
self.show()
def onChanged(self, text):
self.lbl.setText(text)
self.lbl.adjustSize()
def main():
app = QtGui.QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Bam! got it.
import sys
from PySide import QtGui, QtCore
class Example(QtGui.QWidget):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
# formatting
self.resize(1000, 1000)
self.setWindowTitle("Cameras")
# widgets
self.ListA = QtGui.QTreeWidget()
self.ListB = QtGui.QTreeWidget()
self.Button = QtGui.QPushButton()
# layout Splitter
# QHBoxLayout
self.mainLayout = QtGui.QGridLayout(self)
self.mainLayout.setContentsMargins(5,5,5,5)
self.leftPanel = QtGui.QFrame(self)
# self.leftPanel.setFrameShape(QtGui.QFrame.StyledPanel)
self.leftPanelLayout = QtGui.QHBoxLayout(self.leftPanel)
self.leftPanelLayout.setContentsMargins(0,0,0,0)
self.leftPanelLayout.addWidget(self.ListA)
self.rightPanel = QtGui.QFrame(self)
# self.rightPanel.setFrameShape(QtGui.QFrame.StyledPanel)
self.rightPanelLayout = QtGui.QHBoxLayout(self.rightPanel)
self.rightPanelLayout.setContentsMargins(0,0,0,0)
self.rightPanelLayout.addWidget(self.Button)
self.splitter = QtGui.QSplitter(QtCore.Qt.Horizontal)
self.splitter.addWidget(self.leftPanel)
self.splitter.addWidget(self.rightPanel)
self.splitter.setCollapsible(0,False)
self.splitter.setCollapsible(1,False)
self.mainLayout.addWidget(self.splitter,0,0)
self.setWindowTitle('QtGui.QSplitter')
self.show()
self.set_panel_sizes(self.splitter)
def onChanged(self, text):
self.lbl.setText(text)
self.lbl.adjustSize()
def set_panel_sizes(self, ctrl):
width = ctrl.frameSize().width() / 2.0
ctrl.setSizes( [width,width] )
def main():
app = QtGui.QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
if __name__ == '__main__':
main()

Categories

Resources