Add an always visible spinbox colum in QtableView
-
Re: How to add a spinbox column and combo box column in QTableWidget or QTableView?
Hello everyone !
I'm bumping this topic because I need to perform the same thing as my fellow. It's kind of hard to find such example on the web, so I need your help.
So, I need to integrate a spinbox in a TableView, and to have this spinbox always visible and "clickable".
I have few questions:
- I understood that I have to use in some way a QStyleOptionsSpinBox, but I don't have any idea of how to use its specific attributes like buttonSymbols. In fact, I don't really have the idea of how to use this option item in my situation.
- Related to the first point, how can I draw the different elements of the spinbox like the buttons ?
- I want to retrieve the index of the spin box. However, it's located in my QAbstractTableModel. I don't have any idea of to properly get this information from this delegate.
Note that I'm using python Pyside2 binding, which doesn't change anything related to the function signatures and usage..
class SpinBoxDelegate(QtWidgets.QStyledItemDelegate): def __init__(self, parent): super().__init__(parent) def createEditor(self, parent, option, index): return QtWidgets.QSpinBox(parent) def setEditorData(self, editor, index): editor.setValue(int(index.data())) def updateEditorGeometry(self, editor, option, index): editor.setGeometry(option.rect) def paint(self, painter, option, index): painter.save() # How to use all the specific attributes of this option ? option_spinbox = QtWidgets.QStyleOptionSpinBox() option_spinbox.rect = option.rect option_spinbox.state = option.state import pdb pdb.set_trace() # Here, I want to retrieve the spin box index, which is stored in my model... painter.drawText(option_spinbox.rect, "Test") if option_spinbox.state & QtWidgets.QStyle.State_Selected: painter.setPen(QtGui.QPen(QtGui.QColor(255, 0, 0))) painter.drawRect(option_spinbox.rect) painter.restore() return painter.setPen(QtGui.QPen(QtGui.QColor(0, 255, 0))) painter.drawRect(option_spinbox.rect) painter.restore() return
For the moment, here is the result of
-
I finally achieved to a good little result:
I have some questions remaining:
For the moment, the editor shows when double clicking anywhere on the column 2.
I would like to show the editor only if the user clicked on the number on the center, and not on the arrows on the side.For that, I would need to retrieve the mouse position when entering the function createEditor, but I don't know how to do.
I guess that I can re-implement in some way the function that makes the editor popping...Also, is my code ugly in term of conception ? What can I improve to make it prettier then ?
from PySide2 import QtCore, QtWidgets, QtGui from model_ui.segment_model import Columns class SpinBoxDelegate(QtWidgets.QStyledItemDelegate): """This delegates allow a custom display of a spinbox into a tableview""" def __init__(self, parent): super().__init__(parent) def is_segment_analyzed(self, index): """Checks from a given SegmentModel index, if the corresponding segment have been analyzed """ analyzed_index = index.siblingAtColumn(Columns.analyzing.value) return not index.model().get_attribute_from_index(analyzed_index) def createEditor(self, parent, option, index): """Returns a new SpinBox widget if the segment have been analyzed""" if self.is_segment_analyzed(index): return QtWidgets.QSpinBox(parent) return None def setEditorData(self, editor, index): """Handles the way editor store new data""" editor.setValue(int(index.data())) def editorEvent(self, event, model, option, index): """This function is used to handle the interactions with handmade buttons. """ # If the event is a click event if event.type() == QtCore.QEvent.MouseButtonRelease: # Retrieves the button positions left_arrow_button, right_arrow_button = self.get_left_right_arrow_buttons(option) # Retrieves the combo index value from the model index current_combo_value = int(index.data()) value_changed = False # If left button was clicked, we decrease combo index if left_arrow_button.contains(event.pos()): if current_combo_value > 0: value_changed = True current_combo_value = current_combo_value - 1 # If left button was clicked, we increase combo index if right_arrow_button.contains(event.pos()): # TODO: Globally handle right combo limit (for all the project) value_changed = True current_combo_value = current_combo_value + 1 # Report the changes to the model if value_changed: model.setData(index, current_combo_value) def updateEditorGeometry(self, editor, option, index): editor.setGeometry(option.rect) def get_left_arrow_button(self, option): """Creates the left arrow button, depending on the option rect area""" arrow_buttons_size = option.rect.height()//1.5 left_arrow_box = QtCore.QRect( option.rect.left()+1, option.rect.top()+(option.rect.height()//4.5), arrow_buttons_size, arrow_buttons_size) return left_arrow_box def paint_left_arrow_button(self, painter, option): """Creates the left arrow button et paints it""" left_arrow_box = self.get_left_arrow_button(option) painter.drawRect(left_arrow_box) painter.fillRect(left_arrow_box, QtGui.QColor(255, 255, 255)) painter.drawText(left_arrow_box, QtCore.Qt.AlignCenter, "<") def get_right_arrow_button(self, option): """Creates the right arrow button, depending on the option rect area""" arrow_buttons_size = option.rect.height()//1.5 right_arrow_box = QtCore.QRect( option.rect.right()-arrow_buttons_size, option.rect.top()+(option.rect.height()//4.5), arrow_buttons_size, arrow_buttons_size) return right_arrow_box def paint_right_arrow_button(self, painter, option): """Creates the right arrow button et paints it""" right_arrow_box = self.get_right_arrow_button(option) painter.drawRect(right_arrow_box) painter.fillRect(right_arrow_box, QtGui.QColor(255, 255, 255)) painter.drawText(right_arrow_box, QtCore.Qt.AlignCenter, ">") def get_left_right_arrow_buttons(self, option): return self.get_left_arrow_button(option), self.get_right_arrow_button(option) def paint_left_right_arrow_buttons(self, painter, option): self.paint_left_arrow_button(painter, option) self.paint_right_arrow_button(painter, option) def paint(self, painter, option, index): painter.save() if not self.is_segment_analyzed(index): painter.setPen(QtGui.QPen(QtGui.QColor(255, 255, 255))) painter.drawRect(option.rect) painter.restore() return option_spinbox = QtWidgets.QStyleOptionSpinBox() option_spinbox.rect = option.rect option_spinbox.state = option.state # By default: white pen (eraser) painter.setPen(QtGui.QPen(QtGui.QColor(255, 255, 255))) # If the segment have been analyzed: we draw information # Else, we proint a blank cell if self.is_segment_analyzed(index): painter.setPen(QtGui.QPen(QtGui.QColor(0, 0, 0))) painter.drawText(option_spinbox.rect, QtCore.Qt.AlignCenter, str(index.data())) self.paint_left_right_arrow_buttons(painter, option_spinbox) painter.setPen(QtGui.QPen(QtGui.QColor(255, 255, 255))) # If the segment associated to the spinbox is selected: green line if option_spinbox.state & QtWidgets.QStyle.State_Selected: painter.setPen(QtGui.QPen(QtGui.QColor(0, 255, 0))) # Drawing boundaries painter.drawRect(option_spinbox.rect) painter.restore() def sizeHint(self, option, index): """Gives an hint about the minimal size of the element to the view""" number_of_digits_to_draw = len(str(index.data())) arrow_button_width = 25 width = number_of_digits_to_draw * 10 + 2 * arrow_button_width height = 45 return QtCore.QSize(width, height)