How to align widgets contained on several HBoxLayouts? - python

Let's consider this little snippet:
import sys
from PyQt5 import QtWidgets
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QStandardItem
from PyQt5.QtGui import QStandardItemModel
from PyQt5.QtWidgets import QHBoxLayout
from PyQt5.QtWidgets import QLabel
from PyQt5.QtWidgets import QLineEdit
from PyQt5.QtWidgets import QTreeView
from PyQt5.QtWidgets import QWidget
class PropertiesWidget(QTreeView):
def __init__(self, columns, *args, **kwargs):
super(PropertiesWidget, self).__init__(*args, **kwargs)
self.model = QStandardItemModel(self)
self.setModel(self.model)
self.model.setColumnCount(columns)
self.model.setHeaderData(0, Qt.Horizontal, "Property")
self.model.setHeaderData(1, Qt.Horizontal, "Value")
self.setFocusPolicy(Qt.NoFocus)
self.last_item = 0
self.last_item = QStandardItem()
self.parameters = {}
def begin_group(self, name, key):
root = QStandardItem(name)
root.setEditable(False)
if not key:
root.setData(key)
self.model.appendRow([root])
self.last_item = root
def end_group(self):
if (self.last_item and self.last_item.parent()):
self.last_item = self.last_item.parent()
def append_row(self, text, widget):
if not self.last_item:
return
if text in self.parameters:
raise Exception("Not allowed duplicate keys {0}".format(text))
item = self.last_item
child = QStandardItem(text)
child2 = QStandardItem()
child.setEditable(False)
item.appendRow([child, child2])
if widget:
self.setIndexWidget(child2.index(), widget)
self.expand(child.index().parent())
def add_vec2(self, key, value):
x = QLineEdit(value, self)
y = QLineEdit(value, self)
lbl = QLabel('')
lbl.setMinimumWidth(0)
lbl2 = QLabel('')
lbl2.setMinimumWidth(0)
layout = QHBoxLayout(self)
layout.addWidget(x, stretch=1)
layout.addWidget(y, stretch=1)
layout.addWidget(lbl, stretch=1)
layout.addWidget(lbl2, stretch=1)
layout.setContentsMargins(0, 0, 0, 0)
widget = QWidget(self)
widget.setLayout(layout)
setattr(widget, "operator_key", key)
self.append_row(key, widget)
def add_vec3(self, key, value):
x = QLineEdit(value, self)
y = QLineEdit(value, self)
z = QLineEdit(value, self)
lbl = QLabel('')
lbl.setMinimumWidth(0)
layout = QHBoxLayout(self)
layout.addWidget(x, stretch=1)
layout.addWidget(y, stretch=1)
layout.addWidget(z, stretch=1)
layout.addWidget(lbl, stretch=1)
layout.setContentsMargins(0, 0, 0, 0)
widget = QWidget(self)
widget.setLayout(layout)
setattr(widget, "operator_key", key)
self.append_row(key, widget)
def add_vec4(self, key, value):
x = QLineEdit(value, self)
y = QLineEdit(value, self)
z = QLineEdit(value, self)
w = QLineEdit(value, self)
layout = QHBoxLayout(self)
layout.addWidget(x, stretch=1)
layout.addWidget(y, stretch=1)
layout.addWidget(z, stretch=1)
layout.addWidget(w, stretch=1)
layout.setContentsMargins(0, 0, 0, 0)
widget = QWidget(self)
widget.setLayout(layout)
setattr(widget, "operator_key", key)
self.append_row(key, widget)
def main():
app = QtWidgets.QApplication(sys.argv)
ex = PropertiesWidget(2)
ex.begin_group("foo", "foo")
ex.add_vec2("vec2", "vec2_value")
ex.add_vec3("vec3", "vec3_value")
ex.add_vec4("vec4", "vec4_value")
ex.end_group()
ex.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
If i run it and i extend the widget we'll see all the lineedits are aligned properly on each row:
But if i shrink it the widgets will be misaligned with different sizes, like this:
How can i guarantee no matter how you've resized the widget all the lineedits will have the same size and they will be aligned each other? I've already tried using setMinimumWidth(100) but that won't do it.

#Avaris from #pyqt (freenode) channel gave me the solution I was looking for, the main trick was using properly the QSizePolicy on the QLineEdits and empty QLabels, something like this setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Expanding) will do the trick. For a full working example, here's a little snippet:
import sys
from PyQt5 import QtWidgets
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QStandardItem
from PyQt5.QtGui import QStandardItemModel
from PyQt5.QtWidgets import QHBoxLayout
from PyQt5.QtWidgets import QLabel
from PyQt5.QtWidgets import QLineEdit
from PyQt5.QtWidgets import QTreeView
from PyQt5.QtWidgets import QWidget
from PyQt5.QtCore import pyqtSignal
from PyQt5.QtWidgets import QSizePolicy
class VecParameter(QWidget):
value_changed = pyqtSignal(object)
def __init__(self, value, num_components, max_columns=4, parent=None, *args, **kwargs):
super().__init__(parent, *args, **kwargs)
self.components = []
if num_components > max_columns:
num_components = max_columns
layout = QHBoxLayout(self)
for i in range(num_components):
c = QLineEdit(str(value[i]), self)
c.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Expanding)
self.components.append(c)
layout.addWidget(c, stretch=1)
for i in range(num_components, max_columns):
lbl = QLabel('')
lbl.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Expanding)
layout.addWidget(lbl, stretch=1)
layout.setContentsMargins(0, 0, 0, 0)
self.setLayout(layout)
class PropertiesWidget(QTreeView):
def __init__(self, columns, *args, **kwargs):
super(PropertiesWidget, self).__init__(*args, **kwargs)
self.model = QStandardItemModel(self)
self.setModel(self.model)
self.model.setColumnCount(columns)
self.model.setHeaderData(0, Qt.Horizontal, "Property")
self.model.setHeaderData(1, Qt.Horizontal, "Value")
self.setFocusPolicy(Qt.NoFocus)
self.last_item = 0
self.last_item = QStandardItem()
self.parameters = {}
def begin_group(self, name, key):
root = QStandardItem(name)
root.setEditable(False)
if not key:
root.setData(key)
self.model.appendRow([root])
self.last_item = root
def end_group(self):
if (self.last_item and self.last_item.parent()):
self.last_item = self.last_item.parent()
def append_row(self, text, widget):
if not self.last_item:
return
if text in self.parameters:
raise Exception("Not allowed duplicate keys {0}".format(text))
item = self.last_item
child = QStandardItem(text)
child2 = QStandardItem()
child.setEditable(False)
item.appendRow([child, child2])
if widget:
self.setIndexWidget(child2.index(), widget)
self.expand(child.index().parent())
def add_vec1(self, key, value=[0]):
widget = VecParameter(value, 1, parent=self)
self.append_row(key, widget)
def add_vec2(self, key, value=[0, 0]):
widget = VecParameter(value, 2, parent=self)
self.append_row(key, widget)
def add_vec3(self, key, value=[0, 0, 0]):
widget = VecParameter(value, 3, parent=self)
self.append_row(key, widget)
def add_vec4(self, key, value=[0, 0, 0, 0]):
widget = VecParameter(value, 4, parent=self)
self.append_row(key, widget)
def main():
app = QtWidgets.QApplication(sys.argv)
ex = PropertiesWidget(2)
ex.begin_group("foo", "foo")
ex.add_vec1("vec1", [1])
ex.add_vec2("vec2", [1, 2])
ex.add_vec3("vec3", [1, 2, 3])
ex.add_vec4("vec4", [1, 2, 3, 4])
ex.end_group()
ex.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()

Actualy your layouts does not have the same amount of items. Thats why. Every "line" you have must have the same lenght but with different amount of items. so when you are making it smaller, the space is divided with the items inside.
For a quick workaround your can try to add "invisible" items like spacers to your layouts, so the sum of the items in each line can be the same

You could use Tkinter to do this. It is much simpler and will keep everything aligned with its grid feature! (Just be sure to set the x offset for each box!)
Import tkinter
root = tk.Tk() # Some versions of python throw tantrums when you don't do this
root.geometry(...) #Like so: root.geometry("400x400")
#to make this easy I'll only do one box.
"""
The grid feature of tkinter allows you to grid-align boxes to a page.
By just simply asking if root.geometry is less than ... then realign to a new grid.
"""
Entry = tkinter.Entry(root, background = "...") #Use Entry.get() feature to gather what's been written as a value called ans if u want to do this.
#More entries down here.....
Entry.grid(row = 2, column = 2) #Edit row and column for where to grid it.
root.mainloop() # Required by tkinter in order to run.

Related

PyQt6/PySide6: QStackedLayout aligns the size of nested widgets

I created a QStacedLayout and nested 2 widgets in it.
However, as I understand it, if one widget is larger than the other, then QStackedLayout seems to be trying to equalize the size of the larger one to the smaller one.
Because of this, part of the content of the 2nd widget is "eaten" and appears only after I move the window.
How can this be fixed?
Large Widget Code:
from PySide6.QtWidgets import QFrame, QWidget, QLabel
from PySide6.QtWidgets import QVBoxLayout, QPushButton
from PySide6.QtCore import Qt
import tech
class AboutMe(QFrame):
def __init__(self):
super(AboutMe, self).__init__()
with open(tech.resource_path("ui\\assets\\styles\\AboutMe.qss"), "r", encoding="utf-8") as f:
self.style = f.read()
self.setStyleSheet(self.style)
self.layout = QVBoxLayout()
self.layout.setAlignment(Qt.AlignTop)
self.layout.setContentsMargins(5, 5, 5, 5)
self.layout.setSpacing(3)
self.text = QLabel("text "*800)
self.text.setWordWrap(True)
self.layout.addWidget(self.text)
self.setLayout(self.layout)
class AboutPage(QWidget):
def __init__(self):
super(AboutPage, self).__init__()
self.layout = QVBoxLayout()
self.layout.setContentsMargins(0, 0, 0, 0)
self.layout.setSpacing(3)
self.aboutme = AboutMe()
self.layout.addWidget(self.aboutme)
self.setLayout(self.layout)
Main Widget Code:
class MainWidget(QWidget):
def __init__(self, bot, parent):
super(MainWidget, self).__init__()
self.page_layout = QStackedLayout()
self.page_layout.setSpacing(0)
self.page_layout.setContentsMargins(0, 0, 0, 0)
self.main_layout = QVBoxLayout()
self.main_layout.setSpacing(3)
self.main_layout.setContentsMargins(0, 0, 0, 0)
self.main_page = MainPage(bot)
self.about_page = AboutPage()
self.page_layout.addWidget(self.main_page)
self.page_layout.addWidget(self.about_page)
self.page_layout.setCurrentIndex(0)
self.bar = CustomBar(parent)
self.main_layout.addWidget(self.bar)
self.main_layout.addLayout(self.page_layout)
self.setLayout(self.main_layout)
Result:
First Widget:
Second (larger):
When I switched to at least a pixel:

How can I generate the buttons and connect them to different functions? [duplicate]

This question already has answers here:
How do I assert the identity of a PyQt5 signal?
(2 answers)
Closed 2 years ago.
I've created a search engine in PyQt5, using the code below:
import sys
from PyQt5.QtWidgets import (
QWidget, QLineEdit, QLabel, QScrollArea, QMainWindow,
QApplication, QHBoxLayout, QVBoxLayout, QSpacerItem, QSizePolicy, QCompleter, QPushButton
)
from PyQt5 import QtCore
from PyQt5.QtCore import Qt
tlist = ['thing1', 'thing2', 'thing3', 'thing4']
class Label(QWidget):
def __init__(self, name):
super(Label, self).__init__()
self.name = name
self.lbl = QLabel(self.name)
self.lbl.setTextInteractionFlags(QtCore.Qt.TextSelectableByMouse)
self.btn = QPushButton("Preview")
self.btn.setMaximumSize(QtCore.QSize(100,100))
self.btn.clicked.connect(self.printsignal)
self.hbox = QHBoxLayout()
self.hbox.addWidget(self.lbl)
self.hbox.addWidget(self.btn)
self.setLayout(self.hbox)
def show(self):
for labels in [self, self.lbl]:
labels.setVisible(True)
def hide(self):
for labels in [self, self.lbl]:
labels.setVisible(False)
def printsignal(self):
print("clicked")
class MainWindow(QMainWindow):
def __init__(self, *args, **kwargs):
super().__init__()
self.controls = QWidget()
self.controlsLayout = QVBoxLayout()
self.widgets = []
for name in tlist:
item = Label(name)
self.controlsLayout.addWidget(item)
self.widgets.append(item)
spacer = QSpacerItem(1, 1, QSizePolicy.Minimum, QSizePolicy.Expanding)
self.controlsLayout.addItem(spacer)
self.controls.setLayout(self.controlsLayout)
self.scroll = QScrollArea()
self.scroll.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
self.scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.scroll.setWidgetResizable(True)
self.scroll.setWidget(self.controls)
self.searchbar = QLineEdit()
self.searchbar.textChanged.connect(self.update_display)
self.completer = QCompleter(tlist)
self.completer.setCaseSensitivity(Qt.CaseInsensitive)
self.searchbar.setCompleter(self.completer)
container = QWidget()
containerLayout = QVBoxLayout()
containerLayout.addWidget(self.searchbar)
containerLayout.addWidget(self.scroll)
container.setLayout(containerLayout)
self.setCentralWidget(container)
self.setGeometry(600, 100, 800, 600)
self.setWindowTitle('Search Engine')
def update_display(self, text):
for widget in self.widgets:
if text.lower() in widget.name.lower():
widget.show()
else:
widget.hide()
app = QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
The problem I have is, all the buttons share the same function and I don't know how to make them have different signals, as they are generated automatically. Basically, if I run the code it will show up like
this:
and when I press any of the buttons, it will print "clicked" (as in printsignal function). What I want is a different function for each button. Is there a way to do that?
Normally you can use self.sender().text() to get text from QButton which generated signal.
But because you create own widget Label with QButton and QLabel and you want text from label so you can get directly self.name
def printsignal(self):
print("clicked", self.name)
eventually self.lbl.text()
def printsignal(self):
print("clicked", self.lbl.text())
Working code.
I removed show(), hide() because you don't need it
import sys
from PyQt5.QtWidgets import (
QWidget, QLineEdit, QLabel, QScrollArea, QMainWindow,
QApplication, QHBoxLayout, QVBoxLayout, QSpacerItem, QSizePolicy, QCompleter, QPushButton
)
from PyQt5 import QtCore
from PyQt5.QtCore import Qt
tlist = ['thing1', 'thing2', 'thing3', 'thing4']
class Label(QWidget):
def __init__(self, name):
super().__init__()
self.name = name
self.lbl = QLabel(self.name)
self.lbl.setTextInteractionFlags(QtCore.Qt.TextSelectableByMouse)
self.btn = QPushButton("Preview")
self.btn.setMaximumSize(QtCore.QSize(100,100))
self.btn.clicked.connect(self.printsignal)
self.hbox = QHBoxLayout()
self.hbox.addWidget(self.lbl)
self.hbox.addWidget(self.btn)
self.setLayout(self.hbox)
def printsignal(self):
print("clicked", self.name)
class MainWindow(QMainWindow):
def __init__(self, *args, **kwargs):
super().__init__()
self.controls = QWidget()
self.controlsLayout = QVBoxLayout()
self.widgets = []
for name in tlist:
item = Label(name)
self.controlsLayout.addWidget(item)
self.widgets.append(item)
spacer = QSpacerItem(1, 1, QSizePolicy.Minimum, QSizePolicy.Expanding)
self.controlsLayout.addItem(spacer)
self.controls.setLayout(self.controlsLayout)
self.scroll = QScrollArea()
self.scroll.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
self.scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.scroll.setWidgetResizable(True)
self.scroll.setWidget(self.controls)
self.searchbar = QLineEdit()
self.searchbar.textChanged.connect(self.update_display)
self.completer = QCompleter(tlist)
self.completer.setCaseSensitivity(Qt.CaseInsensitive)
self.searchbar.setCompleter(self.completer)
container = QWidget()
containerLayout = QVBoxLayout()
containerLayout.addWidget(self.searchbar)
containerLayout.addWidget(self.scroll)
container.setLayout(containerLayout)
self.setCentralWidget(container)
self.setGeometry(600, 100, 800, 600)
self.setWindowTitle('Search Engine')
def update_display(self, text):
for widget in self.widgets:
if text.lower() in widget.name.lower():
widget.show()
else:
widget.hide()
app = QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())

Get state from QCheckBox inside QTableWidget cell

I want to get a state from QCheckBox inside QTableWidget cell.
I've made example code for this issue.
import sys
from PyQt5.QtCore import Qt, QSettings
from PyQt5.QtWidgets import QApplication, QWidget, QHBoxLayout, QVBoxLayout
from PyQt5.QtWidgets import QPushButton, QTableWidget, QCheckBox, QTextEdit
class TestUI(QWidget):
def __init__(self):
super().__init__()
self.btn = QPushButton("Get Data")
self.tbl = QTableWidget()
self.log = QTextEdit()
self.init_ui()
def init_ui(self):
self.btn.clicked.connect(self.get_data)
self.tbl.setFocusPolicy(Qt.NoFocus)
self.tbl.setMinimumHeight(255)
self.tbl.setMaximumHeight(255)
self.tbl.setRowCount(20)
self.tbl.setColumnCount(3)
self.tbl.horizontalHeader().setStretchLastSection(True)
self.tbl.setHorizontalHeaderLabels(["", "Option", "Value"])
self.tbl.resizeRowsToContents()
self.tbl.resizeColumnsToContents()
for row in range(20):
chk_box = QCheckBox()
chk_box.setCheckState(Qt.Unchecked)
cell = QWidget()
hlayout = QHBoxLayout()
hlayout.addWidget(chk_box)
hlayout.setAlignment(Qt.AlignCenter | Qt.AlignVCenter)
hlayout.setContentsMargins(0, 0, 0, 0)
cell.setLayout(hlayout)
self.tbl.setCellWidget(row, 0, cell)
vlayout = QVBoxLayout()
vlayout.addWidget(self.btn)
vlayout.addWidget(self.tbl)
vlayout.addWidget(self.log)
self.setLayout(vlayout)
self.show()
def get_data(self):
self.log.clear()
self.log.append(self.tbl.cellWidget(0, 0).isChecked())
self.log.append(self.tbl.cellWidget(0, 1).text())
self.log.append(self.tbl.cellWidget(0, 2).text())
if __name__ == "__main__":
APP = QApplication(sys.argv)
ex = TestUI()
sys.exit(APP.exec_())
How can I do this? I can't get the state through this code.
I think the code should be self.log.append(self.tbl.cellWidget(0, 0).????.isChecked()).
But I do not know exactly how to do it.
Please help me.
If the code that places the widget in column 0 is analyzed:
chk_box = QCheckBox()
chk_box.setCheckState(Qt.Unchecked)
cell = QWidget()
hlayout = QHBoxLayout()
hlayout.addWidget(chk_box)
hlayout.setAlignment(Qt.AlignCenter | Qt.AlignVCenter)
hlayout.setContentsMargins(0, 0, 0, 0)
cell.setLayout(hlayout)
self.tbl.setCellWidget(row, 0, cell)
It is noted that the widget set is not the QCheckBox but a QWidget, and the QCheckBox is the son of the QWidget so that information can be used to obtain it using the findChild() method. On the other hand, the item method may return None, so you should check if it is not, since it could throw an exception:
def get_data(self):
self.log.clear()
widget = self.tbl.cellWidget(0, 0)
if widget is not None:
chk_box = widget.findChild(QCheckBox)
if chk_box is not None:
self.log.append(str(chk_box.isChecked()))
it1 = self.tbl.item(0, 1)
self.log.append(it1.text() if it1 is not None else "")
it2 = self.tbl.item(0, 2)
self.log.append(it2.text() if it2 is not None else "")
A more elegant version of the above is to make the "cell" a custom widget that exposes the isChecked() method of the QCheckBox:
class Widget(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.chk_box = QCheckBox()
self.chk_box.setCheckState(Qt.Unchecked)
hlayout = QHBoxLayout(self)
hlayout.addWidget(self.chk_box)
hlayout.setAlignment(Qt.AlignCenter)
hlayout.setContentsMargins(0, 0, 0, 0)
def isChecked(self):
return self.chk_box.isChecked()
class TestUI(QWidget):
def __init__(self):
super().__init__()
self.btn = QPushButton("Get Data")
self.tbl = QTableWidget()
self.log = QTextEdit()
self.init_ui()
def init_ui(self):
self.btn.clicked.connect(self.get_data)
self.tbl.setFocusPolicy(Qt.NoFocus)
self.tbl.setMinimumHeight(255)
self.tbl.setMaximumHeight(255)
self.tbl.setRowCount(20)
self.tbl.setColumnCount(3)
self.tbl.horizontalHeader().setStretchLastSection(True)
self.tbl.setHorizontalHeaderLabels(["", "Option", "Value"])
self.tbl.resizeRowsToContents()
self.tbl.resizeColumnsToContents()
for row in range(20):
cell = Widget()
self.tbl.setCellWidget(row, 0, cell)
vlayout = QVBoxLayout(self)
vlayout.addWidget(self.btn)
vlayout.addWidget(self.tbl)
vlayout.addWidget(self.log)
self.show()
def get_data(self):
self.log.clear()
widget = self.tbl.cellWidget(0, 0)
if widget is not None:
self.log.append(str(widget.isChecked()))
it1 = self.tbl.item(0, 1)
self.log.append(it1.text() if it1 is not None else "")
it2 = self.tbl.item(0, 2)
self.log.append(it2.text() if it2 is not None else "")
It can be deduced that you use the QWidget to center the QCheckBox inside the cell but I think it can be optimized to avoid creating widgets using a QProxyStyle, and then access the information through the checkState() method of the QTableWidgetItem:
import sys
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import (
QApplication,
QWidget,
QHBoxLayout,
QVBoxLayout,
QPushButton,
QTableWidget,
QTextEdit,
QTableWidgetItem,
QProxyStyle,
QStyle,
)
class ProxyStyle(QProxyStyle):
def subElementRect(self, element, option, widget):
r = super().subElementRect(element, option, widget)
if element == QStyle.SE_ItemViewItemCheckIndicator:
r.moveCenter(option.rect.center())
return r
class TestUI(QWidget):
def __init__(self):
super().__init__()
self.btn = QPushButton("Get Data")
self.tbl = QTableWidget()
self.log = QTextEdit()
self.init_ui()
def init_ui(self):
self.btn.clicked.connect(self.get_data)
proxy = ProxyStyle()
self.tbl.setStyle(proxy)
self.tbl.setFocusPolicy(Qt.NoFocus)
self.tbl.setFixedHeight(255)
self.tbl.setRowCount(20)
self.tbl.setColumnCount(3)
self.tbl.horizontalHeader().setStretchLastSection(True)
self.tbl.setHorizontalHeaderLabels(["", "Option", "Value"])
self.tbl.resizeRowsToContents()
self.tbl.resizeColumnsToContents()
for row in range(20):
it = QTableWidgetItem()
it.setCheckState(Qt.Checked)
self.tbl.setItem(row, 0, it)
vlayout = QVBoxLayout(self)
vlayout.addWidget(self.btn)
vlayout.addWidget(self.tbl)
vlayout.addWidget(self.log)
self.show()
def get_data(self):
self.log.clear()
it0 = self.tbl.item(0, 0)
self.log.append(str(it0.checkState() == Qt.Checked))
it1 = self.tbl.item(0, 1)
self.log.append(it1.text() if it1 is not None else "")
it2 = self.tbl.item(0, 2)
self.log.append(it2.text() if it2 is not None else "")
if __name__ == "__main__":
APP = QApplication(sys.argv)
ex = TestUI()
sys.exit(APP.exec_())

Can a checkable Qstandard item be centered within a QStandardItemModel column?

I have a table class with a column of check boxes. I'd like to center these within the column but using item.setTextAlignment(Qt.AlignCenter) doesn't work.
from PyQt5.QtWidgets import *
from PyQt5.QtCore import (QDate, QDateTime, QRegExp, QSortFilterProxyModel, Qt,
QTime, QModelIndex, QSize, pyqtSignal, QObject)
from PyQt5.QtGui import QStandardItemModel, QIcon, QStandardItem
class Table(QWidget):
def __init__(self, parent=None):
QWidget.__init__(self, parent)
self.initUI()
self.show()
def initUI(self):
mainLayout = QVBoxLayout()
self.proxyModel = QSortFilterProxyModel()
self.proxyModel.setDynamicSortFilter(True)
self.sourceModel = QStandardItemModel(0, 2, self)
self.sourceModel.setHeaderData(0, Qt.Horizontal, '')
self.sourceModel.setHeaderData(1, Qt.Horizontal, 'Value')
self.proxyModel.setSourceModel(self.sourceModel)
self.proxyGroupBox = QGroupBox('data')
self.proxyView = QTreeView()
self.proxyView.setRootIsDecorated(False)
self.proxyView.setAlternatingRowColors(True)
self.proxyView.setModel(self.proxyModel)
self.proxyView.setEditTriggers(QAbstractItemView.NoEditTriggers)
proxyLayout = QGridLayout()
proxyLayout.addWidget(self.proxyView, 0, 0, 1, 3)
self.proxyGroupBox.setLayout(proxyLayout)
mainLayout.addWidget(self.proxyGroupBox)
self.setLayout(mainLayout)
for i in range(5):
self.proxyView.resizeColumnToContents(0)
item = QStandardItem(True)
item.setCheckable(True)
item.setCheckState(False)
#item.setTextAlignment(Qt.AlignCenter)
self.sourceModel.setItem(i, 0, item)
self.sourceModel.setData(self.sourceModel.index(i, 1), i+1)
def setSourceModel(self, model):
self.proxyModel.setSourceModel(model)
if __name__=='__main__':
import sys
app = QApplication(sys.argv)
window = Table()
sys.exit(app.exec_())
Is there anyway to center each item of the first column?
If you want to change the alignment of the checkbox then you must use a QProxyStyle:
class CheckBoxProxyStyle(QProxyStyle):
def subElementRect(self, element, option, widget):
rect = super().subElementRect(element, option, widget)
if element == QStyle.SE_ItemViewItemCheckIndicator:
rect.moveCenter(option.rect.center())
return rect
self.proxyView = QTreeView()
self.proxyView.setStyle(CheckBoxProxyStyle(self.proxyView.style()))

How to make a tab pane with no title bar?

I want to make GUI like below with PyQt5, but I can't find an example to help me.
I searched for "change layout on qwidget" and "tab pane with no title bar" and "card layout" without luck. How can I make this with PyQt5?
You have to use a QStackedLayout (or a QStackedWidget) that changes pages when the buttons are pressed. And the first page should have the buttons. I have also implemented the back() method that returns to the initial page, that slot must be invoked when the Change button is pressed:
from functools import partial
from PyQt5 import QtCore, QtWidgets
class CardWidget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(CardWidget, self).__init__(parent)
self._layout = QtWidgets.QStackedLayout(self)
button_widget = QtWidgets.QWidget()
self.btn_lay = QtWidgets.QFormLayout(button_widget)
self._layout.addWidget(button_widget)
def add_widget(self, text, widget):
self._layout.addWidget(widget)
btn = QtWidgets.QPushButton(text)
self.btn_lay.addRow(btn)
btn.clicked.connect(partial(self._layout.setCurrentWidget, widget))
#QtCore.pyqtSlot()
def back(self):
self._layout.setCurrentIndex(0)
class Widget(QtWidgets.QWidget):
backSignal = QtCore.pyqtSignal()
def __init__(self, parent=None):
super(Widget, self).__init__(parent)
self.le1 = QtWidgets.QLineEdit()
self.le2 = QtWidgets.QLineEdit()
button = QtWidgets.QPushButton("Change")
button.clicked.connect(self.backSignal)
flay = QtWidgets.QFormLayout()
flay.addRow("Value 1:", self.le1)
flay.addRow("Value 2:", self.le2)
lay = QtWidgets.QVBoxLayout(self)
lay.addLayout(flay)
lay.addWidget(button)
def create_label():
label = QtWidgets.QLabel(
"Some Other Components",
alignment=QtCore.Qt.AlignCenter
)
label.setStyleSheet("background-color:blue;")
return label
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
c = CardWidget()
for i in range(3):
w = Widget()
w.backSignal.connect(c.back)
c.add_widget("Want to Change value {}".format(i+1), w)
p = QtWidgets.QWidget()
lay = QtWidgets.QGridLayout(p)
lay.addWidget(create_label(), 0, 0, 1, 2)
lay.addWidget(c, 1, 0)
lay.addWidget(create_label(), 1, 1)
lay.setColumnStretch(0, 1)
lay.setColumnStretch(1, 1)
lay.setRowStretch(0, 1)
lay.setRowStretch(1, 1)
p.resize(640, 480)
p.show()
sys.exit(app.exec_())

Categories