PyQt4-PySide - Formatting a table widget - python

In the following code, I would like to use thicker rule separator between the column 2 and 3 for example. How can I achieve this ?
#! /usr/bin/env python2.7
# -*- coding: utf-8 -*-
import sys
from PySide import QtCore, QtGui
class MainWindow(QtGui.QWidget):
def __init__(
self,
parent = None
):
super(MainWindow, self).__init__(parent)
# General grid
self.table = QtGui.QTableWidget(self)
self.nbrow, self.nbcol = 9, 9
self.table.setRowCount(self.nbrow)
self.table.setColumnCount(self.nbcol)
# Each cell has dimension 50 pixels x 50 pixels
for row in range(0, self.nbrow):
self.table.setRowHeight(row, 50)
for col in range(0, self.nbcol):
self.table.setColumnWidth(col, 50)
# Each cell contains one single QTableWidgetItem
for row in range(0, self.nbrow):
for col in range(0, self.nbcol):
item = QtGui.QTableWidgetItem()
item.setTextAlignment(
QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter
)
self.table.setItem(row, col, item)
# Header formatting
font = QtGui.QFont()
font.setFamily(u"DejaVu Sans")
font.setPointSize(12)
self.table.horizontalHeader().setFont(font)
self.table.verticalHeader().setFont(font)
# Font used
font = QtGui.QFont()
font.setFamily(u"DejaVu Sans")
font.setPointSize(20)
self.table.setFont(font)
# Global Size
self.resize(60*9, 60*9 + 20)
# Layout of the table
layout = QtGui.QGridLayout()
layout.addWidget(self.table, 0, 0)
self.setLayout(layout)
# Set the focus in the first cell
self.table.setFocus()
self.table.setCurrentCell(0, 0)
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
fen = MainWindow()
fen.show()
sys.exit(app.exec_())

You can use stylesheets to change the style of the cell borders. See examples:
http://www.qtcentre.org/threads/27195-Border-around-selected-cell-in-qtablewidget
Set QTableWidget cell's borders to 0px

This is not a complete or ideal solution, but it's somewhat relevant and might be helpful to making progress. I found an example of changing a whole cell's border in Qt and translated it to PyQt.
First, create a delegate and override its paint method to do the customization you want.
class TableBorderDelegate(QtGui.QItemDelegate):
"""Delegate for customizing cell borders"""
def paint(self, painter, option, index):
"""Overrides paint to put a red border around cells in column 3"""
if index.column() == 3: # index.row() is also a thing
painter.setPen(QtGui.QColor(255, 0, 0))
painter.drawRect(option.rect)
QtGui.QItemDelegate.paint(self, painter, option, index)
# End of class TableBorderDelegate
And then in your table
delegate = TableBorderDelegate()
self.table.setItemDelegate(delegate)
Where self.table is an instance of QtGui.QTableWidget()
The Qt solution came from https://stackoverflow.com/a/7264248/6605826 .
I found a pyqt example of delegate usage in https://github.com/baoboa/pyqt5/blob/master/examples/itemviews/spinboxdelegate.py An unrelated pyqt delegate

Related

QComboBox show whole row of QTreeView

I want to use QtreeView to organize the data shown by a QComboBox. As you can see in my example, creating the box and setting up data works so far.
But my problem is, that the combobox itself only shows the first argument and not the whole line. what I want to have is, that there is shown the whole row, not only the first item of the row.
Is this maybe related to the fact, that each cell is selectable? Do I have to prohibit to select items at the end of the tree branch?
How can I achieve this while adding the elements to the QtreeView-data?
minimal example:
import sys
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
data = [['a','b','c'],['d','e','f'],['g','h','i']]
class MainWindow(QMainWindow):
dispatcher = 0
def __init__(self):
super().__init__()
# buil UI
self.init_ui()
def init_ui(self):
# layout
self.box_window = QVBoxLayout()
# content
model = QStandardItemModel(len(data),len(data[0]))
row = 0
for r in data:
col = 0
for item in r:
model.setData(model.index(row, col), item)
col += 1
row += 1
tree_view = QTreeView()
tree_view.setHeaderHidden(True)
tree_view.setRootIsDecorated(False)
tree_view.setAlternatingRowColors(True)
combobox = QComboBox()
combobox.setMinimumSize(250,50)
combobox.setView(tree_view)
combobox.setModel(model)
self.box_window.addWidget(combobox)
self.box_window.addStretch()
# build central widget and select it
self.central_widget = QWidget()
self.setCentralWidget(self.central_widget)
self.centralWidget().setLayout(self.box_window)
# show window
self.setGeometry(50,50,1024,768)
self.setWindowTitle("Test")
self.show()
def main():
app = QApplication(sys.argv)
main_window = MainWindow()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
A possible solution is to concatenate the texts in the row and set as the text to be painted:
class ComboBox(QComboBox):
def paintEvent(self, event):
painter = QStylePainter(self)
painter.setPen(self.palette().color(QPalette.Text))
# draw the combobox frame, focusrect and selected etc.
opt = QStyleOptionComboBox()
self.initStyleOption(opt)
values = []
for c in range(self.model().columnCount()):
index = self.model().index(self.currentIndex(), c, self.rootModelIndex())
values.append(index.data())
opt.currentText = " ".join(values)
painter.drawComplexControl(QStyle.CC_ComboBox, opt)
# draw the icon and text
painter.drawControl(QStyle.CE_ComboBoxLabel, opt)

Using QStyledItemDelegates as custom items in QListView

I want to design a custom ListView widget which has custom items similar to this:
https://i.stack.imgur.com/iTNbN.png
However, the qt documentation and some stackoverflow posts state that one should ideally use a QStyleItemDelegate. I never worked with 'delegates' before but as far as I understood from my research they are called by the ListView for drawing / rendering each item.
I found a delegate example in another project (https://github.com/pyblish/pyblish-lite/blob/master/pyblish_lite/delegate.py) and they draw everything by hand / are essentially rebuilding entire widgets by painting rectangles.
This seems a bit impractical for me as most of the time custom item widgets can be compounds of existing widgets. Take a look at the screenshot above. It essentially contains a Qlabel, QPixmap, and four DoubleSpinBoxes.
Question: How would you use the painting / rendering methods that already exist in them instead of manually painting everything on your own?
That way you can profit from existing member methods and can use layouts for structuring your widget.
For example the first ListViewItem should pass the model data to the delegate so that the text of the self.lightGroupName QLabel can be set to "Light1".
Any help is greatly appreciated, since I have no idea how to go on from here:
from PySide2 import QtCore, QtGui, QtWidgets
class LightDelagate(QtWidgets.QStyledItemDelegate): #custom item view
def __init__(self, parent=None):
super(LightDelagate, self).__init__(parent)
self.setupUI()
def setupUI(self):
self.masterWidget = QtWidgets.QWidget()
#Light Group Header
self.hlayLightHeader = QtWidgets.QHBoxLayout()
self.lightGroupName = QtWidgets.QLabel("Checker")
self.hlayLightHeader.addWidget(self.lightGroupName)
#Light AOV Preview
self.lightPreview = QtWidgets.QLabel()
#set size
self.aovThumbnail = QtGui.QPixmap(180, 101)
#self.lightPreview.setPixmap(self.aovThumbnail.scaled(self.lightPreview.width(), self.lightPreview.height(), QtCore.Qt.KeepAspectRatio))
# #Color Dials
# self.hlayColorDials = QtWidgets.QHBoxLayout()
# self.rgbDials = QtWidgets.QHBoxLayout()
# self.rDial = QtWidgets.QDoubleSpinBox()
# self.rDial.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons)
# self.gDial = QtWidgets.QDoubleSpinBox()
# self.gDial.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons)
# self.bDial = QtWidgets.QDoubleSpinBox()
# self.bDial.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons)
# self.rgbDials.addWidget(self.rDial)
# self.rgbDials.addWidget(self.gDial)
# self.rgbDials.addWidget(self.bDial)
# #Exposure
# self.hlayExposureDials = QtWidgets.QHBoxLayout()
# self.exposureDial = QtWidgets.QDoubleSpinBox()
# self.exposureDial.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons)
# self.hlayExposureDials.addWidget(self.exposureDial)
# self.hlayColorDials.addLayout(self.rgbDials)
# self.hlayColorDials.addLayout(self.hlayExposureDials)
#entire layout
self.vlayWidget = QtWidgets.QVBoxLayout()
self.vlayWidget.addLayout(self.hlayLightHeader)
self.vlayWidget.addWidget(self.lightPreview)
# self.vlayWidget.addLayout(self.hlayColorDials)
self.vlayWidget.setContentsMargins(2,2,2,2)
self.vlayWidget.setSpacing(2)
self.masterWidget.setLayout(self.vlayWidget)
def paint(self, painter, option, index):
rowData = index.model().data(index, QtCore.Qt.DisplayRole)
self.lightGroupName.setText(rowData[0])
print (option.rect)
painter.drawRect(option.rect)
painter.drawText()
def sizeHint(self, option, index):
return QtCore.QSize(200, 150)
class LightListModel(QtCore.QAbstractListModel): #data container for list view
def __init__(self, lightList= None):
super(LightListModel, self).__init__()
self.lightList = lightList or []
#reimplement
def rowCount(self, index):
return len(self.lightList)
def data(self, index, role):
if role == QtCore.Qt.DisplayRole:
lightGroupData = self.lightList[index.row()]
return lightGroupData
class LightListView(QtWidgets.QListView): #
def __init__(self):
super(LightListView, self).__init__()
self.setFlow(QtWidgets.QListView.LeftToRight)
self.setItemDelegate(LightDelagate(self))
self.setMinimumWidth(1880)
lightListTest = [
('Light1' , {'lightList' : [], 'lightColor': (0,0,0), 'mod_exposure': 1, 'mod_color' : (0,0,0)}),
('Light2' , {'lightList' : [], 'lightColor': (0,0,0), 'mod_exposure': 1, 'mod_color' : (0,0,0)}),
('Light3' , {'lightList' : [], 'lightColor': (0,0,0), 'mod_exposure': 1, 'mod_color' : (0,0,0)}),
('Light4' , {'lightList' : [], 'lightColor': (0,0,0), 'mod_exposure': 1, 'mod_color' : (0,0,0)})
]
app = QtWidgets.QApplication([])
LLV = LightListView()
model = LightListModel(lightList=lightListTest)
LLV.setModel(model)
LLV.show()
LLV.setSe
app.exec_()
Instead of QListView, could you use QListWidget and override itemWidget? The idea would be that this lets you return a QWidget (with children as per your screenshot) instead of having to implement a QStyledItemDelegate that calls each child widget's paint method.

How to convert QLabel to QTableWidgetItem to put into QTableWidget

I want to add an icon to my QTableWidget. However, the icon being added is pretty small, so I try to find a way to resize the icon
I have tried using setSizeHint(), but it didn't work. So I thought of creating a pixmap and set the pixmap in QLabel, but I couldn't figure out to convert the QLabel into QTabelWidgetItem.
this is the code in two different approaches
##this is when I try to use setSizeHint()
class test_UI(Ui_MainWindow,QtWidgets.QMainWindow)
def set_icon(self):
icon_item=QtWidgets.QTableWidgetItem()
icon_item.setSizeHint(QtCore.QSize(100,100))
icon_item.setIcon(QtGui.QIcon("Kevin_test.png"))
self.tableWidget.setItem(0,1,icon_item)
##this is when I try to use pixmap to put it inside the table
class test_UI(Ui.MainWindow,QtWidgets.QMainWindow)
def set_icon(self):
icon_item=QtWidgets.QTableWidgetItem(self.label)
icon_item.setFlags(QtCore.Qt.ItemIsEditable)
self.tableWidget.setItem(0,1,icon_item)
def build_icon(self):
self.icon = QtGui.QIcon("Kevin_test.png")
self.label=QtWidgets.QLabel('pic',self)
self.label.setFixedSize(300,300)
pixmap1=self.icon.pixmap(100,100,QtGui.QIcon.Active,QtGui.QIcon.On)
self.label.setPixmap(pixmap1)
For the first approach, I expect the size of the icon to change but it did not.
For the second approach, my program crash because there is no overload call to make QTableWidgetItem with a QLabel.
There are at least the following methods:
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class Delegate(QtWidgets.QStyledItemDelegate):
def initStyleOption(self, option, index):
super().initStyleOption(option, index)
option.decorationSize = index.data(QtCore.Qt.SizeHintRole)
def main(args):
app = QtWidgets.QApplication(args)
# sol1
widget_1 = QtWidgets.QTableWidget(1, 1)
it1 = QtWidgets.QTableWidgetItem()
widget_1.setItem(0, 0, it1)
it1.setIcon(QtGui.QIcon("so-logo.png"))
it1.setSizeHint(QtCore.QSize(100, 100))
widget_1.setIconSize(QtCore.QSize(100, 100))
# sol2
widget_2 = QtWidgets.QTableWidget(1, 1)
it2 = QtWidgets.QTableWidgetItem()
widget_2.setItem(0, 0, it2)
label = QtWidgets.QLabel()
pixmap = QtGui.QPixmap("so-logo.png")
""" scaled
pixmap = pixmap.scaled(
QtCore.QSize(400, 400),
QtCore.Qt.KeepAspectRatio,
QtCore.Qt.SmoothTransformation,
)"""
size = pixmap.size()
label.setPixmap(pixmap)
it2.setSizeHint(size)
label.setFixedSize(size)
widget_2.setCellWidget(0, 0, label)
# sol3
widget_3 = QtWidgets.QTableWidget(1, 1)
it3 = QtWidgets.QTableWidgetItem()
widget_3.setItem(0, 0, it3)
it3.setIcon(QtGui.QIcon("so-logo.png"))
it3.setSizeHint(QtCore.QSize(100, 100))
delegate = Delegate(widget_3)
widget_3.setItemDelegate(delegate)
w = QtWidgets.QWidget()
lay = QtWidgets.QVBoxLayout(w)
lay.addWidget(widget_1)
lay.addWidget(widget_2)
lay.addWidget(widget_3)
w.show()
ret = app.exec_()
return ret
if __name__ == "__main__":
sys.exit(main(sys.argv))
Explanation:
By default the icon size is taken based on the iconSize property.
The QLabel can be added using the setCellWidget() method.
You can use a delegate to set the icon size.

resize QTableWidget / QHBoxLayout at runtime

With Python and PyQt5 I programmed follow GUI.
Now I want customize the program so that I'm able to change the left table width by click on the right table border (light blue line) and moving the mouse (see arrows in picture). while changing the table width, the right side should also change.
I hope my description is clear.
GUI output:
Part of the code for GUI layout:
# table for writeOutput
self.tableWidget = QTableWidget(0, 2)
self.tableWidget.verticalHeader().setVisible(False);
self.tableWidget.setMinimumHeight(170)
self.tableWidget.setMinimumWidth(600)
# generate chart
self.main_widget = QWidget(self)
self.chart = QtBarChart(self.main_widget)
# Layout for input elements
inputLayout = QHBoxLayout()
inputLayout.addWidget(self.typeCBox)
inputLayout.addWidget(self.yearSpinBox)
inputLayout.addWidget(self.button)
inputLayout.addStretch(1)
# right inner layout for output elements e.g chart
innerOutLayout = QVBoxLayout()
innerOutLayout.addWidget(self.chart)
innerOutLayout.addWidget(self.outEdit)
# outer layout for output elements e.g. table
outLayout = QHBoxLayout()
outLayout.addWidget(self.tableWidget)
outLayout.addLayout(innerOutLayout)
# top layout who groups all elements together
topLevelLayout = QVBoxLayout()
topLevelLayout.addLayout(inputLayout)
topLevelLayout.addLayout(outLayout)
What kind of function or code changes do I need to achieve my goal?
--> QSplitter is a good hint
When I try to implement a QSplitter and change my program as below I get an error.
# Layout for input elements
inputLayout = QHBoxLayout()
inputLayout.addWidget(self.typeCBox)
inputLayout.addWidget(self.yearSpinBox)
inputLayout.addWidget(self.button)
inputLayout.addStretch(1)
# right inner layout for output elements e.g chart
innerOutLayout = QVBoxLayout()
innerOutLayout.addWidget(self.chart)
innerOutLayout.addWidget(self.outEdit)
# create Splitter between Table and innerOutLayout() --> (Chart and outEdit)
tableSplitter = QSplitter(Qt.Horizontal)
tableSplitter.addWidget(self.tableWidget)
tableSplitter.addLayout(innerOutWidget)
# outer layout for output elements e.g. table
outLayout = QHBoxLayout()
outLayout.addWidget(self.tableWidget)
outLayout.addWidget(tableSplitter)
# top layout who groups all elements together
topLevelLayout = QVBoxLayout()
topLevelLayout.addLayout(inputLayout)
topLevelLayout.addLayout(outLayout)
When I ran the program I get following error:
tableSplitter.addLayout(innerOutWidget)
AttributeError: 'QSplitter' object has no attribute 'addLayout'
How can I solve this error and how to change to code to work.
The solution is to use QSplitter, these are similar to QLayout, the goal is to control the size of the widgets.
In the following example example shows its use.
from PyQt5 import QtCore, QtGui, QtWidgets
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Widget, self).__init__(parent=parent)
self.verticalLayout = QtWidgets.QVBoxLayout(self)
self.splitter = QtWidgets.QSplitter(self)
self.splitter.setOrientation(QtCore.Qt.Horizontal)
self.createTable(self.splitter, 2, 2)
self.splitter1 = QtWidgets.QSplitter(self.splitter)
self.splitter1.setOrientation(QtCore.Qt.Vertical)
self.createTable(self.splitter1, 3, 3)
self.createTable(self.splitter1, 4, 4)
self.verticalLayout.addWidget(self.splitter)
def createTable(self, parent, nrows, ncols):
tableWidget = QtWidgets.QTableWidget(parent)
tableWidget.setRowCount(nrows)
tableWidget.setColumnCount(ncols)
for i in range(tableWidget.rowCount()):
for j in range(tableWidget.columnCount()):
item = QtWidgets.QTableWidgetItem(f"{i}{j}")
tableWidget.setItem(i, j, item)
return tableWidget
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())

PyQt4 QTableWidget: Set row width and column height to fill parent widget

I am working on a fairly simply PyQt program which basically is just a QTableWidget full of items. My goal is for the width and height of the items to automatically resize based on the size of the parent QTableWidget. For example, if I resize the window smaller, the items width and height should decrease in size but there should remain the same number of items and the items should still completely fill the parent QTableWidget. As you see, I am currently using setColumnWidth and setRowHeight to manually set the width and height.
I have tried the suggestions in the following Stack Overflow questions, as well as many other questions and websites, none of which do what I am attemping to do.
1. How to make qtablewidgets columns assume the maximum space
2. pyqt how to maximize the column width in a tableview
Here is the code I am using currently:
from PyQt4 import QtGui, QtCore
import PyQt4.Qt
class Window(QtGui.QWidget):
def __init__(self, rows, columns):
super(Window, self).__init__()
self.setWindowState(QtCore.Qt.WindowMaximized)
self.table = QtGui.QTableWidget(rows, columns, self)
self.table.verticalHeader().setVisible(False)
self.table.horizontalHeader().setVisible(False)
# this is what I am currently using to set the row and column size
for x in range(columns):
self.table.setColumnWidth(x, 13)
for x in range(rows):
self.table.setRowHeight(x, 13)
for row in range(rows):
for column in range(columns):
item = QtGui.QTableWidgetItem()
self.table.setItem(row, column, item)
layout = QtGui.QGridLayout(self)
layout.addWidget(self.table, 0, 0, 1, 6)
self.show()
def main():
import sys
app = QtGui.QApplication(sys.argv)
window = Window(54, 96)
sys.exit(app.exec_())
if __name__ == "__main__":
main()
I appoligize for the length of this question, but I wanted to make my question very clear. Thank you all in advance for your help!
You can do this using a QItemDelegate and overriding the sizeHint method. Then override the resizeEvent and showEvent methods of you main widget to update the sizes of each cell whenever the widget is resized.
import sys
from PyQt4 import QtGui, QtCore
class MyDelegate(QtGui.QItemDelegate):
def __init__(self, parent, table):
super(MyDelegate, self).__init__(parent)
self.table = table
def sizeHint(self, option, index):
# Get full viewport size
table_size = self.table.viewport().size()
gw = 1 # Grid line width
rows = self.table.rowCount() or 1
cols = self.table.columnCount() or 1
width = (table_size.width() - (gw * (cols - 1))) / cols
height = (table_size.height() - (gw * (rows - 1))) / rows
return QtCore.QSize(width, height)
class Window(QtGui.QWidget):
def __init__(self, rows, columns):
super(Window, self).__init__()
self.lay = QtGui.QVBoxLayout()
self.setLayout(self.lay)
self.table = QtGui.QTableWidget(rows, columns, self)
self.table.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.table.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.lay.addWidget(self.table)
self.delegate = MyDelegate(self, self.table)
self.table.setItemDelegate(self.delegate)
def showEvent(self, event):
super(Window, self).showEvent(event)
self.resizeTable()
def resizeTable(self):
self.table.resizeRowsToContents()
self.table.resizeColumnsToContents()
def resizeEvent(self, event):
super(Window, self).resizeEvent(event)
self.resizeTable()

Categories