I'm developping an application that allows to read and edit json files on pyqt5; the
aplication lets the user make changes to the data, by default it lets the user edit by
hand the fields, however I prefer them to select the information from the a set of options to avoid wrong editions.
To achieve this I am creating an multiple delegates, as an exaple in the code below Delegate_1 and delgate_2 that inherit from QStyledItemDelegate and rewrites the createEditor method. Searching in internet I found three methods from the class QTreeView that can be used to apply the delegates to different situations: setItemDelegateForColumn, setItemDelegateForRow and setItemDelegate, the fist two are works for full columns or rows and the third works for the whole tree, however my intention is to use the delegate_1 for cell with the index (0, 1), and the delegate_2 for the index (1, 1).
is there a method of QTreeView or a way to achieve that ?
import sys
from PySide2.QtWidgets import *
from PySide2.QtGui import *
from PySide2.QtCore import *
class Delegate_1(QStyledItemDelegate):
def createEditor(self, parent, option, index):
combo = QComboBox()
combo.addItem('BINARY')
combo.addItem('ASCII')
return combo
def setModelData(self, editor, model, index):
txt = editor.currentText()
model.setData(index, txt)
class Delegate_2(QStyledItemDelegate):
def createEditor(self, parent, option, index):
combo = QComboBox()
combo.addItem('True')
combo.addItem('False')
return combo
def setModelData(self, editor, model, index):
txt = editor.currentText()
model.setData(index, txt)
if __name__ == '__main__':
app = QApplication(sys.argv)
model = QStandardItemModel(2, 2)
it = QStandardItem('File_mode')
model.setItem(0, 0, it)
it = QStandardItem('ASCII') # apply delegate_1 to the cell
model.setItem(0, 1, it)
it = QStandardItem('Opened')
model.setItem(1, 0, it)
it = QStandardItem('True') # apply delegate_2 to the cell
model.setItem(1, 1, it)
t = QTreeView() # <- it's json data
t.setModel(model)
t.setItemDelegateForColumn(1, Delegate_1()) # <- column 1
#t.setItemDelegate(Delegate()) # <- all cells
t.show()
sys.exit(app.exec_())
Thanks for your help
In the createEditor method, you can call the index.row() and index.column() methods, and depending on the row and column values, create the necessary editor.
def createEditor(self, parent, option, index):
if index.row() == 0 and index.column() == 1:
combo = QComboBox()
combo.addItem('BINARY')
combo.addItem('ASCII')
return combo
elif index.row() == 1 and index.column() == 1:
combo = QComboBox()
combo.addItem('True')
combo.addItem('False')
return combo
Related
I want to make that when i click on particular cell on the QTableWidget it will block the corresponding rows and I want to return the value of each row selected into the QLineEdit.
I couldnt seem to find the solution my code only return when i click it will block the rows but not getting the value.
def click_inventorytable(self):
self.tableInventory.setSelectionBehavior(QtWidgets.QTableView.SelectRows)
index = (self.tableInventory.selectionModel().currentIndex())
value = index.row()
list = [value]
if(len(list)==6):
self.lineproductnameinv.setText((list[1]))
self.linedescinv.setText((list[2]))
self.combocateinv.setText((list[3]))
self.linepriceinv.setText((list[4]))
self.linecurrentstock.setText((list[5]))
self.addstock.setText('')
Since the OP does not provide an MRE then I will create a simple demo of how you can implement the functionality of mapping the elements of the selected row of a table in various widgets.
The appropriate widgets must be chosen so that the user does not enter incorrect values, for example in the case of stock if a QLineEdit is used the user could enter a word which does not make sense since a number is expected so it is better to use a QSpinBox.
Also when the data is saved in the table it is not good to convert it to a string since it loses the way to differentiate them, it is better to save the value through setData() associated with the Qt::DisplayRole role.
Finally, the key to the solution is to use a QDataWidgetMapper that allows mapping parts of a model in widgets, so each time a row is selected the currentIndex of the mapper is updated and it sends the information to the editors.
from functools import cached_property
import random
import sys
from PyQt5 import QtCore, QtWidgets
class MainWindow(QtWidgets.QMainWindow):
LABELS = (
"Product ID",
"Product Name",
"Description",
"Category",
"Price",
"Stock",
)
CATEGORY_OPTIONS = (
"OPTION1",
"OPTION2",
"OPTION3",
"OPTION4",
)
def __init__(self, parent=None):
super().__init__(parent)
self.mapper.setModel(self.tableWidget.model())
self.tableWidget.selectionModel().currentChanged.connect(
self.mapper.setCurrentModelIndex
)
central_widget = QtWidgets.QWidget()
self.setCentralWidget(central_widget)
lay = QtWidgets.QHBoxLayout(central_widget)
flay = QtWidgets.QFormLayout()
lay.addLayout(flay)
lay.addWidget(self.tableWidget, stretch=1)
editors = (
self.name_edit,
self.description_edit,
self.category_combo,
self.price_edit,
self.stock_edit,
)
for i, (label, widget) in enumerate(zip(self.LABELS[1:], editors)):
flay.addRow(label, widget)
self.mapper.addMapping(widget, i)
self.fillTable()
self.resize(960, 480)
#cached_property
def tableWidget(self):
table = QtWidgets.QTableWidget(
0,
len(self.LABELS),
selectionBehavior=QtWidgets.QAbstractItemView.SelectRows,
selectionMode=QtWidgets.QAbstractItemView.SingleSelection,
)
table.setHorizontalHeaderLabels(self.LABELS)
return table
#cached_property
def name_edit(self):
return QtWidgets.QLineEdit()
#cached_property
def description_edit(self):
return QtWidgets.QLineEdit()
#cached_property
def category_combo(self):
combo = QtWidgets.QComboBox()
combo.addItems(["--Null--"] + list(self.CATEGORY_OPTIONS))
combo.setCurrentIndex(0)
return combo
#cached_property
def price_edit(self):
return QtWidgets.QDoubleSpinBox(maximum=2147483647)
#cached_property
def stock_edit(self):
return QtWidgets.QSpinBox(maximum=2147483647)
#cached_property
def mapper(self):
return QtWidgets.QDataWidgetMapper()
def fillTable(self):
self.tableWidget.setRowCount(0)
for i in range(30):
self.tableWidget.insertRow(self.tableWidget.rowCount())
values = (
i,
f"name-{i}",
f"Description-{i}",
random.choice(self.CATEGORY_OPTIONS),
random.uniform(100, 2000),
random.randint(0, 100),
)
for j, value in enumerate(values):
it = QtWidgets.QTableWidgetItem()
it.setData(QtCore.Qt.DisplayRole, value)
it.setFlags(it.flags() & ~QtCore.Qt.ItemIsEditable)
self.tableWidget.setItem(i, j, it)
def main():
app = QtWidgets.QApplication(sys.argv)
win = MainWindow()
win.show()
sys.exit(app.exec())
if __name__ == "__main__":
main()
I want to make some data hidden in QTableWidget until a specific cell is clicked. Right now I only manage to display "*" instead of actual value. I think I could connect somehow with action when the specific cell is clicked and replaced the value of the clicked cell. I also know that there is a function setData() that can be invoked on QTableWidgetItem and give me wanted behavior. But I cannot find any useful example for Python implementation of qt. What is the best solution to this problem?
def setTableValues(self):
self.table.setRowCount(len(self.tableList))
x = 0
for acc in self.tableList:
y = 0
for val in acc.getValuesAsList():
if isinstance(val,Cipher):
val = "*"*len(val.getDecrypted())
item = QTableWidgetItem(val)
self.table.setItem(x,y,item)
y += 1
x += 1
self.table.resizeRowsToContents()
self.table.resizeColumnsToContents()
You can associate a flag with a role that indicates the visibility of the text and then use a delegate to hide the text. That flag will change when the items are pressed:
from PyQt5 import QtCore, QtGui, QtWidgets
VisibilityRole = QtCore.Qt.UserRole + 1000
class VisibilityDelegate(QtWidgets.QStyledItemDelegate):
def initStyleOption(self, option, index):
super().initStyleOption(option, index)
if not index.data(VisibilityRole):
option.text = "*" * len(option.text)
class TableWidget(QtWidgets.QTableWidget):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
delegate = VisibilityDelegate(self)
self.setItemDelegate(delegate)
self.visibility_index = QtCore.QModelIndex()
self.pressed.connect(self.on_pressed)
#QtCore.pyqtSlot(QtCore.QModelIndex)
def on_pressed(self, index):
if self.visibility_index.isValid():
self.model().setData(self.visibility_index, False, VisibilityRole)
self.visibility_index = index
self.model().setData(self.visibility_index, True, VisibilityRole)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = TableWidget(10, 4)
for i in range(w.rowCount()):
for j in range(w.columnCount()):
it = QtWidgets.QTableWidgetItem("{}-{}".format(i, j))
w.setItem(i, j, it)
w.show()
w.resize(640, 480)
sys.exit(app.exec_())
I have the following code:
import datetime
from PyQt5 import QtCore
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
class RadioDelegate(QStyledItemDelegate):
def __init__(self, owner, chs):
super().__init__(owner)
self.items = chs
def paint(self, painter, option, index):
if isinstance(self.parent(), QAbstractItemView):
self.parent().openPersistentEditor(index)
super(RadioDelegate, self).paint(painter, option, index)
def createEditor(self, parent, option, index):
editor = QGroupBox(parent)
# editor.setMinimumHeight(38)
editor.setContentsMargins(0, 0, 0, 0)
layout = QHBoxLayout()
style = """
padding: 0px;
margin-top: 0px;
outline: none;
border: none
"""
for k in self.items:
rb = QRadioButton(k)
rb.setStyleSheet(style)
layout.addWidget(rb)
editor.setStyleSheet(style)
editor.setLayout(layout)
return editor
def setEditorData(self, editor, index):
value = index.data(QtCore.Qt.DisplayRole)
print("setEditorData-" + str(datetime.datetime.now()) + " " + str(value))
def setModelData(self, editor, model, index):
print("setModelData-" + str(datetime.datetime.now()))
def updateEditorGeometry(self, editor, option, index):
editor.setGeometry(option.rect)
class Window(QMainWindow):
def __init__(self):
super().__init__()
self.setGeometry(40, 80, 550, 250)
self.table = QTableView(self)
self.model = QStandardItemModel(self)
self.table.setModel(self.model)
self.table.setItemDelegateForColumn(3, RadioDelegate(self, ["abc", "xyz"]))
self.populate()
self.table.resizeRowsToContents()
self.table.clicked.connect(self.on_click)
self.setCentralWidget(self.table)
def on_click(self, event):
row = event.row()
column = event.column()
cell_dict = self.model.itemData(event)
cell_value = cell_dict.get(0)
print("Row {}, column {} clicked: {}".format(row, column, cell_value))
def populate(self):
values = [["1st", "2nd", "3rd", "radio"], ["111", "222", "333", ""], ["abc", "xyz", "123", ""]]
for value in values:
row = []
for item in value:
cell = QStandardItem(str(item))
row.append(cell)
self.model.appendRow(row)
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
ex = Window()
ex.show()
sys.exit(app.exec_())
And I get the look of the edited cell (4th column), which is not acceptable:
cell with radio buttons
How can I get rid of these margins or this padding? Or make the group box extend to cover the whole cell?
I have tried to manipulate style sheets, as you can see, but with no effect.
Of course, I can use "editor.setMinimumHeight(38)", but the top margin or whatever it is, still remains, and I can select the whole cell (not only the radio buttons).
By the way! Do you know how the paint method should look like to display radio buttons also in display mode?
You have to set the margins of the layout too:
def createEditor(self, parent, option, index):
editor = QGroupBox(parent)
# editor.setMinimumHeight(38)
editor.setContentsMargins(0, 0, 0, 0)
layout = QHBoxLayout()
layout.setContentsMargins(0, 0, 0, 0)
When a layout is created it has no margins, but they will change as soon as it is applied to a widget:
From the getContentsMargins() documentation:
By default, QLayout uses the values provided by the style.
These values are provided by the following QStyle's pixelmetrics: PM_LayoutLeftMargin, PM_LayoutTopMargin, PM_LayoutRightMargin, PM_LayoutBottomMargin. They all depend on the style internal parameters, the level of the widget (if it's a top-level widget - a "window" - or a child) and the dpi of the screen.
While newly created layouts usually return (0, 0, 0, 0) from getContentsMargins(), internally their value is actually -1. When the layout is applied to a widget, Qt will use the style's fallback values only if they've not been set to a value >= 0.
So, the margin has to be explicitly set even for 0 values to ensure that no margin actually exists; conversely, the style's default values can be restored by setting each margin to -1.
A couple of suggestions, even if they're not related to the actual question.
First of all, don't try to open the persistent editor within the paintEvent. Despite the fact that the method you used wouldn't work anyway, paintEvents happen very often, and creating a new widget inside a paintEvent would require another call to the paintEvent itself, potentially creating an infinite recursion (luckily, Qt would ignore the openPersistentEditor if it already exists, but that's not the point).
Whenever you're using a complex widget, it's better to set the background of the editor, otherwise the item's text might be visible under the editor in most styles. Also, there's no need for a QGroupBox for this situation, a simple QWidget will suffice.
With CSS you can just do this (there's no need to apply the style to the children):
editor.setStyleSheet("QWidget { background: palette(base); }")
If you don't need specific customization, you can avoid stylesheets at all and just set the autoFillBackground property:
editor.setAutoFillBackground(True)
Finally, the biggest issue of this kind of implementation is that if the column is not wide enough, some radio buttons might become invisible as using setGeometry will "clip" the contents to the item rectangle.
To avoid that, you can adjust the geometry rectangle to accomodate all its contents, based on its minimum size hint:
def updateEditorGeometry(self, editor, option, index):
rect = QtCore.QRect(option.rect)
minWidth = editor.minimumSizeHint().width()
if rect.width() < minWidth:
rect.setWidth(minWidth)
editor.setGeometry(rect)
Unfortunately, this will not be a very good thing if you want to show the editor even in "display mode", as you requested.
To do that in a good fashion while keeping a good user experience, things will be a bit more complex: the editor should be always be clipped when the item is not the current index, and automatically "resized" when some interaction is needed; also, if a hidden radio is selected, the user might click on an unchecked one, which will make that radio checked, without knowing the previous status.
To do so I'm using an event filter that will "clip" the widget using setMask() if it has no focus, and show all of it whenever it gets it. Since radio buttons can steal focus, I'm setting their focusPolicy to NoFocus. This also allows me to check if the widget has received a click to toggle radio buttons or just to start the editing; in that case, the click event will be ignored.
class RadioDelegate(QStyledItemDelegate):
def __init__(self, owner, chs):
super().__init__(owner)
self.items = chs
def createEditor(self, parent, option, index):
editor = QWidget(parent)
editor.setContentsMargins(0, 0, 0, 0)
editor.setAutoFillBackground(True)
# create a button group to keep track of the checked radio
editor.buttonGroup = QButtonGroup()
# adding the widget as an argument to the layout constructor automatically
# applies it to the widget
layout = QHBoxLayout(editor)
layout.setContentsMargins(0, 0, 0, 0)
for i, k in enumerate(self.items):
rb = QRadioButton(k)
layout.addWidget(rb)
# prevent the radio to get focus from keyboard or mouse
rb.setFocusPolicy(QtCore.Qt.NoFocus)
rb.installEventFilter(self)
editor.buttonGroup.addButton(rb, i)
# add a stretch to always align contents to the left
layout.addStretch(1)
# set a property that will be used for the mask
editor.setProperty('offMask', QRegion(editor.rect()))
editor.installEventFilter(self)
return editor
def eventFilter(self, source, event):
if event.type() == QtCore.QEvent.MouseButtonPress:
if isinstance(source, QRadioButton):
if not source.parent().hasFocus():
# the parent has no focus, set it and ignore the click
source.parent().setFocus()
return True
elif not source.hasFocus():
# the container has been clicked, check
source.setFocus()
elif event.type() == QtCore.QEvent.FocusIn:
# event received as a consequence of setFocus
# clear the mask to show it completely
source.clearMask()
elif event.type() == QtCore.QEvent.FocusOut:
# another widget has requested focus, set the mask
source.setMask(source.property('offMask'))
# update the table viewport to get rid of possible
# grid lines left after masking
source.parent().update()
return super().eventFilter(source, event)
def updateEditorGeometry(self, editor, option, index):
rect = QtCore.QRect(option.rect)
minWidth = editor.minimumSizeHint().width()
if rect.width() < minWidth:
rect.setWidth(minWidth)
editor.setGeometry(rect)
# create a new mask based on the option rectangle, then apply it
mask = QRegion(0, 0, option.rect.width(), option.rect.height())
editor.setProperty('offMask', mask)
editor.setMask(mask)
def setEditorData(self, editor, index):
value = index.data(QtCore.Qt.DisplayRole)
if value in self.items:
editor.buttonGroup.button(self.items.index(value)).setChecked(True)
def setModelData(self, editor, model, index):
button = editor.buttonGroup.checkedId()
if button >= 0:
model.setData(index, self.items[button], QtCore.Qt.DisplayRole)
class Window(QMainWindow):
def __init__(self):
# ...
# I just added an option for the purpose of this example
self.table.setItemDelegateForColumn(3, RadioDelegate(self, ["abc", "xyz", 'www']))
# ...
# open a persistent editor for all rows in column 4
for row in range(self.model.rowCount()):
self.table.openPersistentEditor(self.model.index(row, 3))
The sample code below (heavily influenced from here) has a right-click context menu that will appear as the user clicks the cells in the table. Is it possible to have a different right-click context menu for right-clicks in the header of the table? If so, how can I change the code to incorporate this?
import re
import operator
import os
import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *
def main():
app = QApplication(sys.argv)
w = MyWindow()
w.show()
sys.exit(app.exec_())
class MyWindow(QWidget):
def __init__(self, *args):
QWidget.__init__(self, *args)
self.tabledata = [('apple', 'red', 'small'),
('apple', 'red', 'medium'),
('apple', 'green', 'small'),
('banana', 'yellow', 'large')]
self.header = ['fruit', 'color', 'size']
# create table
self.createTable()
# layout
layout = QVBoxLayout()
layout.addWidget(self.tv)
self.setLayout(layout)
def popup(self, pos):
for i in self.tv.selectionModel().selection().indexes():
print i.row(), i.column()
menu = QMenu()
quitAction = menu.addAction("Quit")
action = menu.exec_(self.mapToGlobal(pos))
if action == quitAction:
qApp.quit()
def createTable(self):
# create the view
self.tv = QTableView()
self.tv.setStyleSheet("gridline-color: rgb(191, 191, 191)")
self.tv.setContextMenuPolicy(Qt.CustomContextMenu)
self.tv.customContextMenuRequested.connect(self.popup)
# set the table model
tm = MyTableModel(self.tabledata, self.header, self)
self.tv.setModel(tm)
# set the minimum size
self.tv.setMinimumSize(400, 300)
# hide grid
self.tv.setShowGrid(True)
# set the font
font = QFont("Calibri (Body)", 12)
self.tv.setFont(font)
# hide vertical header
vh = self.tv.verticalHeader()
vh.setVisible(False)
# set horizontal header properties
hh = self.tv.horizontalHeader()
hh.setStretchLastSection(True)
# set column width to fit contents
self.tv.resizeColumnsToContents()
# set row height
nrows = len(self.tabledata)
for row in xrange(nrows):
self.tv.setRowHeight(row, 18)
# enable sorting
self.tv.setSortingEnabled(True)
return self.tv
class MyTableModel(QAbstractTableModel):
def __init__(self, datain, headerdata, parent=None, *args):
""" datain: a list of lists
headerdata: a list of strings
"""
QAbstractTableModel.__init__(self, parent, *args)
self.arraydata = datain
self.headerdata = headerdata
def rowCount(self, parent):
return len(self.arraydata)
def columnCount(self, parent):
return len(self.arraydata[0])
def data(self, index, role):
if not index.isValid():
return QVariant()
elif role != Qt.DisplayRole:
return QVariant()
return QVariant(self.arraydata[index.row()][index.column()])
def headerData(self, col, orientation, role):
if orientation == Qt.Horizontal and role == Qt.DisplayRole:
return QVariant(self.headerdata[col])
return QVariant()
def sort(self, Ncol, order):
"""Sort table by given column number.
"""
self.emit(SIGNAL("layoutAboutToBeChanged()"))
self.arraydata = sorted(self.arraydata, key=operator.itemgetter(Ncol))
if order == Qt.DescendingOrder:
self.arraydata.reverse()
self.emit(SIGNAL("layoutChanged()"))
if __name__ == "__main__":
main()
Turned out to be simpler than I thought. In the same manner as I add the popup menu for the QTableView widget itself, I can just get the header from table object and then attach a context menu in the same way as I did with the regular context menu.
headers = self.tv.horizontalHeader()
headers.setContextMenuPolicy(Qt.CustomContextMenu)
headers.customContextMenuRequested.connect(self.header_popup)
There's another potentially more powerful way to do this if you take the step and inherit the view instead of simply composing it. Does custom context menu work here? Yes, but why does anything other than the view need to know about it? It also will help better shape your code to deal with other issues properly. Currently the implementation doesn't provide any encapsulation, cohesion or support separation of responsibility. In the end you will have one big blob which is the antithesis of good design.
I mention this because you seem to be placing all of the GUI Logic in this ever growing main function, and its the reason you ended up putting the sort implementation inside your model, which makes no sense to me. (What if you have two views of the model, you are forcing them to be sorted in the same way)
Is it more code? Yes, but it gives you more power which I think is worth mentioning. Below I'm demonstrating how to handle the headers and also any given cell you want. Also note that in my implementation if some OTHER widget exists which also defines a context menu event handler it will potentially get a chance to have crack at handling the event after mine; so that if someone else adds a handler for only certain cases they can do so without complicating my code. Part of doing this is marking if you handled the event or not.
Enough of my rambling and thoughts here's the code:
#Alteration : instead of self.tv = QTableView...
self.tv = MyTableView()
....
# somewhere in your TableView object's __init__ method
# yeah IMHO you should be inheriting and thus extending TableView
class MyTableView(QTableView):
def __init__(self, parent = None):
super(MyTableView, self).__init__()
self.setContextMenuPolicy(Qt.DefaultContextMenu)
## uniform one for the horizontal headers.
self.horizontalHeader().setContextMenuPolicy(Qt.ActionsContextMenu)
''' Build a header action list once instead of every time they click it'''
doSomething = QAction("&DoSomething", self.verticalHeader(),
statusTip = "Do something uniformly for headerss",
triggered = SOME_FUNCTION
self.verticalHeader().addAction(doSomething)
...
return
def contextMenuEvent(self, event)
''' The super function that can handle each cell as you want it'''
handled = False
index = self.indexAt(event.pos())
menu = QMenu()
#an action for everyone
every = QAction("I'm for everyone", menu, triggered = FOO)
if index.column() == N: #treat the Nth column special row...
action_1 = QAction("Something Awesome", menu,
triggered = SOME_FUNCTION_TO_CALL )
action_2 = QAction("Something Else Awesome", menu,
triggered = SOME_OTHER_FUNCTION )
menu.addActions([action_1, action_2])
handled = True
pass
elif index.column() == SOME_OTHER_SPECIAL_COLUMN:
action_1 = QAction("Uh Oh", menu, triggered = YET_ANOTHER_FUNCTION)
menu.addActions([action_1])
handled = True
pass
if handled:
menu.addAction(every)
menu.exec_(event.globalPos())
event.accept() #TELL QT IVE HANDLED THIS THING
pass
else:
event.ignore() #GIVE SOMEONE ELSE A CHANCE TO HANDLE IT
pass
return
pass #end of class
I'm displaying some information from a sql server in a qtableview using a sqlmodel.
I have set up a custom delegate to deal with the editing of the data.
I would like to display my dates in a specific format, when they table is first loaded the dates are displayed as such:
20011-04-30
But when I edit the date and click off the cell to accept the date is then displayed like:
30/04/2011
Which is how its stored in the database and how I would like it to be displayed to start with, I have no idea why it changes format once its been edited.
I'm guessing I have to reimplement the paint method for that column I have done something similar with a progress bar but I have no idea how to do it for text.
Here is my delegate as it stands, note is has some editors set up for other columns but my main question only relates to how to display the date correctly.
import sys
import os
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from PyQt4.QtSql import *
PROJECTID, PROJECTTITLE, CLIENTID, DEADLINE, PROGRESS, ROOT = range(6)
class projectsDelegate(QSqlRelationalDelegate):
def __ini__(self, parent = None):
super(projectsDelegate, self).__init__(parent)
def createEditor(self, parent, option, index):
if index.column() == DEADLINE:
editor = QDateEdit(parent)
#editor.setDisplayFormat("yyyy/MM/dd")
editor.setMinimumDate(QDate.currentDate())
editor.setCalendarPopup(True)
return editor
elif index.column() == PROGRESS:
editor = QSpinBox(parent)
editor.setRange(0, 100)
editor.setSingleStep(5)
editor.setSuffix("%")
return editor
elif index.column() == ROOT:
editor = QFileDialog(parent)
editor.setFileMode(QFileDialog.Directory)
editor.setOptions(QFileDialog.ShowDirsOnly)
editor.setFixedSize(400, 400)
editor.setWindowTitle("Select A Root Folder For The Project")
return editor
else:
return QSqlRelationalDelegate.createEditor(self, parent, option, index)
def setEditorData(self, editor, index):
if index.column() == DEADLINE:
text = index.model().data(index, Qt.DisplayRole).toDate()
editor.setDate(text)
elif index.column() == PROGRESS:
prog = index.model().data(index, Qt.DisplayRole).toInt()[0]
editor.setValue(prog)
elif index.column() == ROOT:
root = index.model().data(index, Qt.DisplayRole).toString()
editor.setDirectory(os.path.dirname(str(root)))
screen = QDesktopWidget().screenGeometry()
size = editor.geometry()
editor.move((screen.width() - size.width()) / 2, (screen.height() - size.height()) / 2)
else:
QSqlRelationalDelegate.setEditorData(self, editor, index)
def setModelData(self, editor, model, index):
if index.column() == DEADLINE:
model.setData(index, QVariant(editor.date()))
elif index.column() == PROGRESS:
model.setData(index, QVariant(editor.value()))
elif index.column() == ROOT:
model.setData(index, QVariant(editor.directory().absolutePath()))
else:
QSqlRelationalDelegate.setModelData(self, editor, model, index)
def paint(self, painter, option, index):
if index.column() == PROGRESS:
bar = QStyleOptionProgressBarV2()
bar.rect = option.rect
bar.minimum = 0
bar.maximum = 100
bar.textVisible = True
percent = index.model().data(index, Qt.DisplayRole).toInt()[0]
bar.progress = percent
bar.text = QString("%d%%" % percent)
QApplication.style().drawControl(QStyle.CE_ProgressBar, bar, painter)
else:
QSqlRelationalDelegate.paint(self, painter, option, index)
def sizeHint(self, options, index):
if index.column() == PROGRESS:
return QSize(150, 30)
elif index.column() == ROOT:
return QSize(400, 800)
else:
return QSqlRelationalDelegate.sizeHint(self, options, index)
Thanks, Tom.
Why is editor.setDisplayFormat("yyyy/MM/dd") commented out? Shouldn't that take care of the formatting?