Open blabut opened 5 years ago
Hi Paul, I would be interested in your code. Would you consider providing it?
Regards, Werner
Hi Werner, of course! There you go:
# from PyQt5 import QtGui, QtCore
from PyQt5.QtWidgets import QFrame, QWidget, QHBoxLayout, QVBoxLayout, QLabel, QPushButton
from PyQt5.QtCore import QPoint, QPointF
from PyQt5.QtGui import QColor
from PyQt5.QtGui import QPainter
class FrameLayout(QWidget):
def __init__(self, parent=None, title=None):
QFrame.__init__(self, parent=parent)
self._is_collasped = True
self._title_frame = None
self._content, self._content_layout = (None, None)
self._main_v_layout = QVBoxLayout(self)
self._main_v_layout.addWidget(self.initTitleFrame(title, self._is_collasped))
self._main_v_layout.addWidget(self.initContent(self._is_collasped))
self.initCollapsable()
def initTitleFrame(self, title, collapsed):
self._title_frame = self.TitleFrame(title=title, collapsed=collapsed)
return self._title_frame
def initContent(self, collapsed):
self._content = QWidget()
self._content_layout = QVBoxLayout()
self._content.setLayout(self._content_layout)
self._content.setVisible(not collapsed)
return self._content
def addWidget(self, widget):
self._content_layout.addWidget(widget)
def initCollapsable(self):
self._title_frame.clicked.connect(self.toggleCollapsed)
def toggleCollapsed(self):
self._content.setVisible(self._is_collasped)
self._title_frame.setMinimumHeight([50,25][int(self._is_collasped)])
self._is_collasped = not self._is_collasped
self._title_frame._arrow.setArrow(int(self._is_collasped))
############################
# TITLE #
############################
class TitleFrame(QPushButton):
def __init__(self, parent=None, title="", collapsed=False):
QFrame.__init__(self, parent=parent)
self.setMinimumHeight(50) # height of the button
self.move(QPoint(24, 0))
self.setStyleSheet("border:2px solid rgb(41, 41, 41); ")
self._hlayout = QHBoxLayout(self)
self._hlayout.setContentsMargins(0, 0, 0, 0)
self._hlayout.setSpacing(0)
self._arrow = None
self._title = None
self._hlayout.addWidget(self.initArrow(collapsed))
self._hlayout.addWidget(self.initTitle(title))
def initArrow(self, collapsed):
self._arrow = FrameLayout.Arrow(collapsed=collapsed)
self._arrow.setStyleSheet("border:0px")
return self._arrow
def initTitle(self, title=None):
self._title = QLabel(title)
self._title.setMinimumHeight(17)
self._title.move(QPoint(24, 0))
self._title.setStyleSheet("border:0px")
return self._title
# def mousePressEvent(self, event):
# self.emit(QtCore.SIGNAL('clicked()'))
# return super(FrameLayout.TitleFrame, self).mousePressEvent(event)
#############################
# ARROW #
#############################
class Arrow(QFrame):
def __init__(self, parent=None, collapsed=False):
QFrame.__init__(self, parent=parent)
self.setMaximumSize(24, 24)
# horizontal == 0
self._arrow_horizontal = (QPointF(7.0, 8.0), QPointF(17.0, 8.0), QPointF(12.0, 13.0))
# vertical == 1
self._arrow_vertical = (QPointF(8.0, 7.0), QPointF(13.0, 12.0), QPointF(8.0, 17.0))
# arrow
self._arrow = None
self.setArrow(int(collapsed))
def setArrow(self, arrow_dir):
if arrow_dir:
self._arrow = self._arrow_vertical
else:
self._arrow = self._arrow_horizontal
def paintEvent(self, event):
painter = QPainter()
painter.begin(self)
painter.setBrush(QColor(192, 192, 192))
painter.setPen(QColor(64, 64, 64))
painter.drawPolygon(*self._arrow)
painter.end()
Cheers,
Paul
Thanks for the fast reply Paul,
Greetings, Werner
Hi Paul, Sorry to trouble you again. Do you have any sample code? Something like Caroline Beyne provides in her 'main.py' file? There seems to have changed a lot and i'm not an expert in QT5 stuff :)
Regards, Werner
Hi Werner,
Unfortunately the only project I have involving it is a professional one I cannot share in its integrity.. But basically, if you get familiar with the main concepts of PyQt5 (see here for instance), then you should be fine.
The main idea is that the FrameLayout behaves as an QWidget (a graphical element in PyQt5), so once instanciated, they can be for instance added to any layout object:
layout_object.addWidget(your_frame_layout_instance)
To instanciate it, just provide the title
argument to the constructor:
your_frame_layout_instance = FrameLayout(title = "your_title_here")
Then use the addWidget
method to add the inner element(s) of your collapsible frame:
your_frame_layout_instance.addWidget(any_widget_like_object)
I hope that helps! I am quite busy at the moment but I'll try to create a clean repo with the code and some examples soon.
Best, Paul
@wernerlievens Here's a rough mashup of @paupaulaz and @By0ute's code. Basically, some components in QtGui were moved to QtWidgets. Replace the appropriate modules (by running and correcting errors) in __main__.py
and update to use Python3 (print functions instead of expressions):
import sys
from PyQt5 import QtWidgets, QtCore
from PyQt5.QtWidgets import QFrame, QWidget, QHBoxLayout, QVBoxLayout, QLabel, QPushButton
from PyQt5.QtCore import QPoint, QPointF
from PyQt5.QtGui import QColor
from PyQt5.QtGui import QPainter
class FrameLayout(QWidget):
def __init__(self, parent=None, title=None):
QFrame.__init__(self, parent=parent)
self._is_collasped = True
self._title_frame = None
self._content, self._content_layout = (None, None)
self._main_v_layout = QVBoxLayout(self)
self._main_v_layout.addWidget(self.initTitleFrame(title, self._is_collasped))
self._main_v_layout.addWidget(self.initContent(self._is_collasped))
self.initCollapsable()
def initTitleFrame(self, title, collapsed):
self._title_frame = self.TitleFrame(title=title, collapsed=collapsed)
return self._title_frame
def initContent(self, collapsed):
self._content = QWidget()
self._content_layout = QVBoxLayout()
self._content.setLayout(self._content_layout)
self._content.setVisible(not collapsed)
return self._content
def addWidget(self, widget):
self._content_layout.addWidget(widget)
def initCollapsable(self):
self._title_frame.clicked.connect(self.toggleCollapsed)
def toggleCollapsed(self):
self._content.setVisible(self._is_collasped)
self._title_frame.setMinimumHeight([50,25][int(self._is_collasped)])
self._is_collasped = not self._is_collasped
self._title_frame._arrow.setArrow(int(self._is_collasped))
############################
# TITLE #
############################
class TitleFrame(QPushButton):
def __init__(self, parent=None, title="", collapsed=False):
QFrame.__init__(self, parent=parent)
self.setMinimumHeight(50) # height of the button
self.move(QPoint(24, 0))
self.setStyleSheet("border:2px solid rgb(41, 41, 41); ")
self._hlayout = QHBoxLayout(self)
self._hlayout.setContentsMargins(0, 0, 0, 0)
self._hlayout.setSpacing(0)
self._arrow = None
self._title = None
self._hlayout.addWidget(self.initArrow(collapsed))
self._hlayout.addWidget(self.initTitle(title))
def initArrow(self, collapsed):
self._arrow = FrameLayout.Arrow(collapsed=collapsed)
self._arrow.setStyleSheet("border:0px")
return self._arrow
def initTitle(self, title=None):
self._title = QLabel(title)
self._title.setMinimumHeight(17)
self._title.move(QPoint(24, 0))
self._title.setStyleSheet("border:0px")
return self._title
# def mousePressEvent(self, event):
# self.emit(QtCore.SIGNAL('clicked()'))
# return super(FrameLayout.TitleFrame, self).mousePressEvent(event)
#############################
# ARROW #
#############################
class Arrow(QFrame):
def __init__(self, parent=None, collapsed=False):
QFrame.__init__(self, parent=parent)
self.setMaximumSize(24, 24)
# horizontal == 0
self._arrow_horizontal = (QPointF(7.0, 8.0), QPointF(17.0, 8.0), QPointF(12.0, 13.0))
# vertical == 1
self._arrow_vertical = (QPointF(8.0, 7.0), QPointF(13.0, 12.0), QPointF(8.0, 17.0))
# arrow
self._arrow = None
self.setArrow(int(collapsed))
def setArrow(self, arrow_dir):
if arrow_dir:
self._arrow = self._arrow_vertical
else:
self._arrow = self._arrow_horizontal
def paintEvent(self, event):
painter = QPainter()
painter.begin(self)
painter.setBrush(QColor(192, 192, 192))
painter.setPen(QColor(64, 64, 64))
painter.drawPolygon(*self._arrow)
painter.end()
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
win = QtWidgets.QMainWindow()
w = QtWidgets.QWidget()
w.setMinimumWidth(350)
win.setCentralWidget(w)
l = QtWidgets.QVBoxLayout()
l.setSpacing(0)
l.setAlignment(QtCore.Qt.AlignTop)
w.setLayout(l)
t = FrameLayout(title="Buttons")
t.addWidget(QtWidgets.QPushButton('a'))
t.addWidget(QtWidgets.QPushButton('b'))
t.addWidget(QtWidgets.QPushButton('c'))
f = FrameLayout(title="TableWidget")
rows, cols = (6, 3)
data = {'col1': ['1', '2', '3', '4', '5', '6'],
'col2': ['7', '8', '9', '10', '11', '12'],
'col3': ['13', '14', '15', '16', '17', '18']}
table = QtWidgets.QTableWidget(rows, cols)
headers = []
for n, key in enumerate(sorted(data.keys())):
headers.append(key)
for m, item in enumerate(data[key]):
newitem = QtWidgets.QTableWidgetItem(item)
table.setItem(m, n, newitem)
table.setHorizontalHeaderLabels(headers)
f.addWidget(table)
l.addWidget(t)
l.addWidget(f)
win.show()
win.raise_()
print("\2")
sys.exit(app.exec_())
Does the code should't be updated to PyQt5 and Python 3? I mean, update the repository not having the updated code on an issue
I've created my own collapsible widget based on this https://gist.github.com/Patitotective/0a9cd57dfeb448fd77c148bd9b7b2ed8
Hi Caroline,
Thank you very much for this code, helped me a lot ! I actually cloned it and modify it to make it PyQt5 compatible, since this is the version I am using. If you are interested, feel free to add me as a contributor and I'd be glad to share !
Best, Paul