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)
Related
I am trying to create a tableWidget in PyQt that has copy-paste functionality and the ability to create new tabs. My table is loaded with a initialized sheet but gives the user the ability to create new tabs with new qTableWidgets which I handled using a for loop to create and initialize the widget everytime a new tab is created in my add_sheet() function.
I wanted to also add functionality for copying and pasting inside each tab and across the tabs. Now when I added the key press event function to do this, I kept getting errors when trying to copy and paste in new tabs as out of index. I tried to fix this by keeping a pointer of which tab the selected indexes come from but this only allows me to edit on the first new tab created. The initial spreadsheet crashes when trying to do operations and the other tabs just do not work. It also does not copy and paste universally amongst the tabs.
I feel I have made my handling too complicated and I have a flaw or am missing something in my design pattern.
How can I properly implement my copy-paste function to work amongst all dynamically tabs with their own QTableWidget instances including the initial QTableWidget created both locally and universally?
import sys
from PyQt6.QtWidgets import (QApplication, QMainWindow,
QTableWidget, QTableWidgetItem,QVBoxLayout,QTabWidget,QWidget,QToolButton,QToolBar)
from PyQt6.QtCore import Qt
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.tab_widget = MyTabWidget(self)
self.setCentralWidget(self.tab_widget)
self.initializeUI()
def initializeUI(self):
"""Set up the application's GUI."""
self.setMinimumSize(1200, 500)
self.setWindowTitle("Spreadsheet - QTableWidget Example")
# Used for copy and paste actions
self.item_text = None
self.setUpMainWindow()
self.show()
def setUpMainWindow(self):
"""Create and arrange widgets in the main window."""
# Set initial row and column values
main_spreadsheet_widget.setRowCount(10)
main_spreadsheet_widget.setColumnCount(10)
class MyTabWidget(QWidget):
def __init__(self, parent):
super(QWidget, self).__init__(parent)
self.layout = QVBoxLayout(self)
# Initialize tab screen
self.sheets = QTabWidget()
self.main_sheet = QWidget()
self.sheets.resize(300, 200)
self.extra_sheets_tracker = list()
self.tab_index = []
self.copied_cells_list = []
self.paste_index = []
# Add sheets
self.sheets.addTab(self.main_sheet, "Main Sheet")
#self.sheets.addTab(self.tab3, "Geeks")
# Create first tab
self.main_sheet.layout = QVBoxLayout(self)
self.main_sheet.layout.addWidget(main_spreadsheet_widget)
self.main_sheet.setLayout(self.main_sheet.layout)
self.tabButton = QToolButton(self)
self.tabButton.setText('+')
font = self.tabButton.font()
font.setBold(True)
self.tabButton.setFont(font)
self.sheets.setCornerWidget(self.tabButton)
self.tabButton.clicked.connect(self.add_sheet)
# Add sheets to widget
self.layout.addWidget(self.sheets)
self.setLayout(self.layout)
def add_sheet(self):
self.sheet = QWidget()
self.main_tab_sheet_widget = QTableWidget()
self.extra_sheets_tracker.append(self.main_tab_sheet_widget)
self.main_tab_sheet_widget.setRowCount(10)
self.main_tab_sheet_widget.setColumnCount(10)
self.sheet.layout = QVBoxLayout(self)
self.sheet.layout.addWidget(self.main_tab_sheet_widget)
self.sheet.setLayout(self.sheet.layout)
self.sheets.addTab(self.main_tab_sheet_widget, "Sheet" + str(len(self.extra_sheets_tracker)))
def keyPressEvent(self, event):
super().keyPressEvent(event)
if event.key() == Qt.Key.Key_C and (event.modifiers() & Qt.KeyboardModifier.ControlModifier):
self.tab_index = []
for i in self.extra_sheets_tracker:
if i.selectedIndexes() is not None:
self.tab_index.append(self.extra_sheets_tracker.index(i))
self.copied_cells = sorted(self.extra_sheets_tracker[self.tab_index[0]].selectedIndexes())
self.copied_cells_list.append(self.copied_cells)
self.copied_cells = None
elif event.key() == Qt.Key.Key_V and (event.modifiers() & Qt.KeyboardModifier.ControlModifier):
self.paste_index = []
for i in self.extra_sheets_tracker:
self.paste_index.append(self.extra_sheets_tracker.index(i))
r = self.extra_sheets_tracker[self.paste_index[0]].currentRow() - self.copied_cells_list[0][0].row()
c = self.extra_sheets_tracker[self.paste_index[0]].currentColumn() - self.copied_cells_list[0][0].column()
for cell in self.copied_cells_list[0]:
self.extra_sheets_tracker[self.paste_index[0]].setItem(cell.row() + r, cell.column() + c, QTableWidgetItem(cell.data()))
if __name__ == "__main__":
app = QApplication(sys.argv)
main_spreadsheet_widget = QTableWidget()
window = MainWindow()
window.show()
sys.exit(app.exec())
I have managed to do the following:
1- I treated the main sheet as if it was "just another sheet" and made sure it follows the same relative routine. This included removing unnecessary functions like setUpMainWindow and introducing static values like MAX_ROWS and MAX_COLS for maximum rows and columns respectively. This also entailed renaming extra_sheets_tracker to become sheets_tracker since it will hold all of the sheets.
2- I have created two strategies for pasting: 1st-cell for selecting the first cell in a table, or 1-to-1 for selecting the exact count of cells at the pasting side. This meant, also, checking for the dimensions beforehand and raising the exception Unmatching pasting size if the sizes did not match.
3- I removed self.tab_index and self.copied_cells_list since they are not used. copy_list is used globally and it is not a bad idea to stay in self. However paste_list is not used anywhere else than the paste routine and I removed it from self. You need to look into your self variables, you seem to overuse them.
import sys
from PyQt6.QtWidgets import QApplication, QMainWindow, QTableWidget, QTableWidgetItem, QVBoxLayout, QTabWidget, QWidget, QToolButton, QToolBar
from PyQt6.QtCore import Qt
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.tab_widget = MyTabWidget(self)
self.setCentralWidget(self.tab_widget)
self.initializeUI()
def initializeUI(self):
"""Set up the application's GUI."""
self.setMinimumSize(1200, 500)
self.setWindowTitle("Spreadsheet - QTableWidget Example")
# Used for copy and paste actions
self.item_text = None
self.show()
class MyTabWidget(QWidget):
def __init__(self, parent):
super(QWidget, self).__init__(parent)
self.layout = QVBoxLayout(self)
# Initialize tab screen
self.MAX_ROWS = 10
self.MAX_COLS = 10
self.STRATEGY = {0:'1st-cell', 1:'1-to-1'}
self.sheets = QTabWidget()
self.main_sheet = QWidget()
self.sheets.resize(300, 200)
self.sheets_tracker = list()
self.copy_list = []
#self.copied_cells_list = []
#self.paste_list = []
# Add a sheet and create first tab
self.main_sheet.layout = QVBoxLayout(self)
# Setup main window
self.add_sheet()
tabButton = QToolButton(self)
tabButton.setText('+')
font = tabButton.font()
font.setBold(True)
tabButton.setFont(font)
tabButton.clicked.connect(self.add_sheet)
self.sheets.setCornerWidget(tabButton)
# Add sheets to widget
self.layout.addWidget(self.sheets)
self.setLayout(self.layout)
def add_sheet(self):
sheets_count = len(self.sheets_tracker)
if sheets_count < 1:
label = "Main sheet"
else:
label = f"Sheet{sheets_count}"
self.main_tab_sheet_widget = QTableWidget()
self.main_tab_sheet_widget.setRowCount(self.MAX_ROWS)
self.main_tab_sheet_widget.setColumnCount(self.MAX_COLS)
self.sheets_tracker.append(self.main_tab_sheet_widget)
self.sheet = QWidget()
self.sheet.layout = QVBoxLayout(self)
self.sheet.layout.addWidget(self.main_tab_sheet_widget)
self.sheet.setLayout(self.sheet.layout)
self.sheets.addTab(self.main_tab_sheet_widget, label)
def keyPressEvent(self, event):
super().keyPressEvent(event)
if event.key() == Qt.Key.Key_C and (event.modifiers() & Qt.KeyboardModifier.ControlModifier):
self.copy_list = sorted([(e.row(), e.column(), e.data()) for e in self.sheets_tracker[self.sheets.currentIndex()].selectedIndexes()])
if len(self.copy_list) != 0:
# recalculate the indicies to absolute values
least_row = self.copy_list[0][0]
least_col = sorted(self.copy_list, key=lambda tup: tup[1])[0][1]
self.copy_list = [(e[0]-least_row, e[1]-least_col, e[2]) for e in self.copy_list]
print(self.copy_list)
elif event.key() == Qt.Key.Key_V and (event.modifiers() & Qt.KeyboardModifier.ControlModifier):
paste_list = sorted([(element.row(), element.column(), element.data()) for element in self.sheets_tracker[self.sheets.currentIndex()].selectedIndexes()])
if len(paste_list) != 0:
if len(paste_list) == 1:
# The given paste position is the first cell only
current_r, current_c, _ = paste_list[-1]
last_r, last_c, _ = self.copy_list[-1]
if last_r + current_r - 1 < self.MAX_ROWS and last_c + current_c - 1 < self.MAX_COLS:
strategy = self.STRATEGY[0]
else:
raise Exception('Unmatching pasting size')
elif len(self.copy_list) == len(paste_list):
# You can paste in one-to-one correspondence
strategy = self.STRATEGY[1]
else:
raise Exception('Unmatching pasting size')
if strategy == self.STRATEGY[0]:
r, c, _ = paste_list[0]
for index, e in enumerate(self.copy_list):
_, _, d = self.copy_list[index]
d = '' if d is None else d
print(f"Pasting at index [{self.sheets.currentIndex()}] cell ({e[0]+r}, {e[1]+c})")
textCell = QTableWidgetItem(); textCell.setText(f'{d}')
self.sheets_tracker[self.sheets.currentIndex()].setItem(e[0]+r, e[1]+c, textCell)
if strategy == self.STRATEGY[1]:
for index, e in enumerate(paste_list):
_, _, d = self.copy_list[index]
d = '' if d is None else d
print(f"Pasting at index [{self.sheets.currentIndex()}] cell ({e[0]}, {e[1]})")
textCell = QTableWidgetItem(); textCell.setText(f'{d}')
self.sheets_tracker[self.sheets.currentIndex()].setItem(e[0], e[1], textCell)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec())
Once an item is selected on a combo, it should get removed from the others, unless it's "No use".
I made three options of QComboBox, and each of those contains the same items.
The explanation is this:
QCombobox Tug 1 has total 4 items ('No Use', '207HR', '306DR', 'Jupiter')
QCombobox Tug 2 has total 4 items ('No Use', '207HR', '306DR', 'Jupiter')
QCombobox Tug 3 has total 4 items ('No Use', '207HR', '306DR', 'Jupiter')
The default value of those Qcombobox is 'No Use'.
How can I remove the selected value of QComboBox Tug 1 from QComboBox Tug 2?
The point is that 'No Use' shall not be removed; only an item from among '207HR', '306DR', and 'Jupiter'.
The Code i made is below:
class Ship_Use_Tug_Input_Program(QWidget):
def __init__(self, master):
super().__init__()
self.initUI()
def initUI(self):
tug1_cb = QComboBox(self)
jeju_tug = ['No use','207HR (2,500HP)', '306DR (3,600HP)', 'Jupiter (3,600HP)']
tug1_cb.addItems(jeju_tug)
tug2_cb = QComboBox(self)
tug2_cb.addItems(jeju_tug)
tug3_cb = QComboBox(self)
tug3_cb.addItems(jeju_tug)
self.setGeometry(100,100,1000,500)
self.setWindowTitle('Ship_Use_Tug_Input_Program')
self.show()
app = QApplication(sys.argv)
exc = Ship_Use_Tug_Input_Program(master=Ship_Use_Tug_Input_Program)
app.exec_()
The explanation photo is below:
The view-widget of the combo-box can be used to hide the rows, and the item-data can be used to keep track of which combo-box is showing which row. A slot connected to the activated signal can then update the items whenever one of the current-items change.
Below is a complete demo script that implements that. The ExclusiveComboGroup class can be used with any group of combo-boxes. To use it, just create an instance and then add all your combo-boxes using its addCombo method.
Demo Script:
import sys
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
class ExclusiveComboGroup(QObject):
def __init__(self, parent=None):
super().__init__(parent)
self._combos = []
self._role = Qt.UserRole + 500
def addCombo(self, combo):
combo.activated.connect(
lambda: self.handleActivated(combo))
self._combos.append(combo)
def handleActivated(self, target):
index = target.currentIndex()
groupid = id(target)
for combo in self._combos:
if combo is target:
continue
previous = combo.findData(groupid, self._role)
if previous >= 0:
combo.view().setRowHidden(previous, False)
combo.setItemData(previous, None, self._role)
if index > 0:
combo.setItemData(index, groupid, self._role)
combo.view().setRowHidden(index, True)
class Window(QWidget):
def __init__(self):
super().__init__()
self.group = QGroupBox('Selected Tug')
layout = QVBoxLayout(self)
layout.addWidget(self.group)
layout = QFormLayout(self.group)
layout.setVerticalSpacing(15)
layout.setHorizontalSpacing(50)
jeju_tug = [
'No use',
'207HR (2,500HP)',
'306DR (3,600HP)',
'Jupiter (3,600HP)',
]
# create a combo-group
self.tugs = ExclusiveComboGroup(self)
for index in range(3):
combo = QComboBox(self)
combo.addItems(jeju_tug)
layout.addRow(f'Tug {index + 1}', combo)
# add the combo-box
self.tugs.addCombo(combo)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = Window()
window.setWindowTitle('Demo')
window.setGeometry(800, 200, 100, 50)
window.show()
sys.exit(app.exec_())
PS: here is how to use it in your own example:
def initUI(self):
tug1_cb = QComboBox(self)
jeju_tug = ['No use','207HR (2,500HP)', '306DR (3,600HP)', 'Jupiter (3,600HP)']
tug1_cb.addItems(jeju_tug)
tug2_cb = QComboBox(self)
tug2_cb.addItems(jeju_tug)
tug3_cb = QComboBox(self)
tug3_cb.addItems(jeju_tug)
# copy the ExclusiveComboGroup class into
# your code and then add this section
tugs = ExclusiveComboGroup(self)
tugs.addCombo(tug1_cb)
tugs.addCombo(tug2_cb)
tugs.addCombo(tug3_cb)
I am implement my project using pyqt5. Currently, I have a window including many widget. Now, I want to remove some widgets. The window looks like:
Now, I want to remove the 'name1' widget including the QLabel and QPushButton.
However, after removing all 'name1' widgets, the 'name2' widgets including QLabel and QPushButton can not self-adapte with the window, like:
All my code is:
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
import sys
class Window(QDialog):
def __init__(self):
super().__init__()
self.initGUI()
self.show()
def initGUI(self):
layout = QVBoxLayout()
self.setLayout(layout)
removeLayout = QHBoxLayout()
self.__removeText = QLineEdit()
self.__removeBtn = QPushButton('Remove')
self.__removeBtn.clicked.connect(self.remove)
removeLayout.addWidget(self.__removeText)
removeLayout.addWidget(self.__removeBtn)
ROIsLayout = QVBoxLayout()
for name in ['name1', 'name2']:
subLayout = QHBoxLayout()
subText = QLabel(name)
subText.setObjectName(name)
subBtn = QPushButton(name)
subBtn.setObjectName(name)
subLayout.addWidget(subText)
subLayout.addWidget(subBtn)
ROIsLayout.addLayout(subLayout)
layout.addLayout(removeLayout)
layout.addLayout(ROIsLayout)
self.__ROIsLayout = ROIsLayout
def remove(self, checked=False):
name = self.__removeText.text()
while True:
child = self.__ROIsLayout.takeAt(0)
if child == None:
break
while True:
subChild = child.takeAt(0)
if subChild == None:
break
obName = subChild.widget().objectName()
if name == obName:
widget = subChild.widget()
widget.setParent(None)
child.removeWidget(widget)
self.__ROIsLayout.removeWidget(widget)
del widget
if __name__ == '__main__':
app = QApplication(sys.argv)
window = Window()
sys.exit(app.exec_())
update:
Actually, the issue may be the takeAt. The following code is workable:
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
import sys
class Window(QDialog):
def __init__(self):
super().__init__()
self.initGUI()
self.show()
def initGUI(self):
layout = QVBoxLayout()
self.setLayout(layout)
removeLayout = QHBoxLayout()
self.__removeText = QLineEdit()
self.__removeBtn = QPushButton('Remove')
self.__removeBtn.clicked.connect(self.remove)
removeLayout.addWidget(self.__removeText)
removeLayout.addWidget(self.__removeBtn)
ROIsLayout = QVBoxLayout()
for name in ['name1', 'name2']:
subLayout = QHBoxLayout()
subLayout.setObjectName(name)
subText = QLabel(name, parent=self)
subText.setObjectName(name)
subBtn = QPushButton(name, parent=self)
subBtn.setObjectName(name)
subLayout.addWidget(subText)
subLayout.addWidget(subBtn)
ROIsLayout.addLayout(subLayout)
print(name, subLayout, subText, subBtn)
layout.addLayout(removeLayout)
layout.addLayout(ROIsLayout)
self.__ROIsLayout = ROIsLayout
self.record = [subLayout, subText, subBtn]
def remove(self, checked=False):
layout = self.record[0]
txt = self.record[1]
btn = self.record[2]
layout.removeWidget(txt)
txt.setParent(None)
txt.deleteLater()
layout.removeWidget(btn)
btn.setParent(None)
btn.deleteLater()
if __name__ == '__main__':
app = QApplication(sys.argv)
window = Window()
sys.exit(app.exec_())
But, I have printed the QLabel/QPushButton in the self.record, and I find it is the same with that from child.takeAt(0).widget().
The main issue in your code is that you're constantly using takeAt(). The result is that all items in the __ROIsLayout layout will be removed from it (but not deleted), which, in your case, are the sub layouts. This is clearly not a good approach: only the widgets with the corresponding object name will be actually deleted, while the others will still be "owned" by their previous parent, will still be visible at their previous position and their geometries won't be updated since they're not managed by the layout anymore.
There are multiple solutions to your question, all depending on your needs.
If you need to remove rows from a layout, I'd consider setting the object name on the layout instead, and look for it using self.findChild().
Also consider that, while Qt allows setting the same object name for more than one object, that's not suggested.
Finally, while using del is normally enough, it's usually better to call deleteLater() for all Qt objects, which ensures that Qt correctly removes all objects (and related parentship/connections).
Another possibility, for this specific case, is to use a QFormLayout.
I have a usb barcode scanner which I am connecting to my computer. Everytime it scans a barcode, it types the data into the computer like a keyboard. My goal was to have the data be typed into PyQT5 Table widget.
I have created the table below and I simply scan the items into it. The problem is that when I scan an item, it edits the first cell, but the cursor does not move automatically to the next row so I can scan a new item into the table. I have to click on the second cell and then scan the item. Then click on the third cell and scan the item and so on.
I was wondering how I can automate it so that after an item is scanned into the first cell, it automatically moves to the next cell and waits for input from the scanner?
import sys
from PyQt5.QtWidgets import *
#Main Window
class App(QWidget):
def __init__(self):
super().__init__()
self.title = 'Specimen Dashboard'
self.setWindowTitle(self.title)
self.tableWidget = QTableWidget()
self.createTable()
self.tableWidget.itemChanged.connect(self.go_to_next_row)
self.layout = QVBoxLayout()
self.layout.addWidget(self.tableWidget)
self.setLayout(self.layout)
self.show()
def go_to_next_row(self):
#Not working
#Trying to see if I can automatically move to next cell, but editing it
self.tableWidget.setItem(1,0, QTableWidgetItem("Name"))
#Create table
def createTable(self):
self.tableWidget.setRowCount(4)
self.tableWidget.setColumnCount(2)
self.tableWidget.horizontalHeader().setStretchLastSection(True)
self.tableWidget.horizontalHeader().setSectionResizeMode(
QHeaderView.Stretch)
app = QApplication(sys.argv)
ex = App()
sys.exit(app.exec_())
By default the scanners send an endline("\n") that is translated a Return or Enter key and this by default closes the editor, in this case that event must be intercepted, move the cursor and open the editor:
import sys
from PyQt5 import QtCore, QtWidgets
class TableWidget(QtWidgets.QTableWidget):
def keyPressEvent(self, event):
if (
event.key() in (QtCore.Qt.Key_Enter, QtCore.Qt.Key_Return)
and self.state() == QtWidgets.QAbstractItemView.EditingState
):
index = self.moveCursor(
QtWidgets.QAbstractItemView.MoveNext, QtCore.Qt.NoModifier
)
self.selectionModel().setCurrentIndex(
index, QtCore.QItemSelectionModel.ClearAndSelect
)
self.edit(index)
else:
super().keyPressEvent(event)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.tableWidget = TableWidget(4, 2)
self.setCentralWidget(self.tableWidget)
self.tableWidget.horizontalHeader().setStretchLastSection(True)
self.tableWidget.horizontalHeader().setSectionResizeMode(
QtWidgets.QHeaderView.Stretch
)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
You can subclass the table and overwrite closeEditor(): the hint argument tells the view what should happen when the editor has been closed; by default, when pressing Enter the current cell data is submitted, but you can override this behavior like this:
from PyQt5 import QtGui, QtWidgets
class Table(QtWidgets.QTableView):
# leave to False for the default behavior (the next cell is the one at the
# right of the current, or the first of the next row; when set to True it
# will always go to the next row, while keeping the same column
useNextRow = False
def closeEditor(self, editor, hint):
if hint == QtWidgets.QAbstractItemDelegate.SubmitModelCache:
if self.useNextRow:
super().closeEditor(editor, hint)
current = self.currentIndex()
newIndex = current.sibling(current.row() + 1, current.column())
if newIndex.isValid():
self.setCurrentIndex(newIndex)
self.edit(newIndex)
return
else:
hint = QtWidgets.QAbstractItemDelegate.EditNextItem
super().closeEditor(editor, hint)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
test = Table()
test.show()
model = QtGui.QStandardItemModel(10, 5)
test.setModel(model)
sys.exit(app.exec_())
I would like to know how to edit only one column of a QTreeWidgetItem.
Currently, I can edit all columns of a QTreeWidgetItem.
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
import sys
app = QApplication(sys.argv)
qApp = app
pointListBox = QTreeWidget()
header=QTreeWidgetItem(["Tree","First","secondo"])
pointListBox.setHeaderItem(header)
root = QTreeWidgetItem(pointListBox, ["root"])
A = QTreeWidgetItem(root, ["A"])
barA = QTreeWidgetItem(A, ["bar", "i", "ii"])
bazA = QTreeWidgetItem(A, ["baz", "a", "b"])
barA.setFlags(barA.flags() | Qt.ItemIsEditable)
bazA.setFlags(bazA.flags() | Qt.ItemIsEditable)
pointListBox.show()
sys.exit(app.exec_())
EDIT
I have added some changes but I don't know how to edit the second column of my QtreeWidgetItem.
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
import sys
class MainWindow(QMainWindow):
def __init__(self, parent=None):
super(MainWindow,self).__init__(parent)
self.initUI()
def createGroup(self):
groupBox = QGroupBox()
self.treeWidget = QTreeWidget()
header=QTreeWidgetItem(["Tree","First","secondo"])
#...
self.treeWidget.setHeaderItem(header) #Another alternative is setHeaderLabels(["Tree","First",...])
root = QTreeWidgetItem(self.treeWidget, ["root"])
A = QTreeWidgetItem(root, ["A"])
barA = QTreeWidgetItem(A, ["bar", "i", "ii"])
bazA = QTreeWidgetItem(A, ["baz", "a", "b"])
barA.setFlags(barA.flags() | Qt.ItemIsEditable)
bazA.setFlags(bazA.flags() | Qt.ItemIsEditable)
# switch off "default" editing behaviour
# as it does not allow to configure only an individual
# column as editable
self.treeWidget.setEditTriggers(self.treeWidget.NoEditTriggers)
# to be able to decide on your own whether a particular item
# can be edited, connect e.g. to itemDoubleClicked
self.treeWidget.itemDoubleClicked.connect(self.checkEdit)
vbox = QVBoxLayout()
vbox.addWidget(self.treeWidget)
vbox.addStretch(1)
groupBox.setLayout(vbox)
return groupBox
# in your connected slot, you can implement any edit-or-not-logic
# you want
def checkEdit(self, item, column):
# e.g. to allow editing only of column 1:
if column == 1:
self.treeWidget.editItem(item[1])
def initUI(self):
self.resize(300, 220)
self.grid = QGridLayout()
self.widget = QWidget()
self.widget.setLayout(self.grid)
self.setCentralWidget(self.widget)
self.grid.addWidget(self.createGroup(),1,0,1,2)
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
app.setStyle(QStyleFactory.create("Fusion"))
form = MainWindow()
form.show()
sys.exit(app.exec_())
Try this, it's work for me.
self.treeWidget.itemDoubleClicked.connect(self.checkEdit)
def checkEdit(self, item, column):
tmp = item.flags()
if column == 1 :
item.setFlags(tmp | QtCore.Qt.ItemIsEditable)
elif tmp & QtCore.Qt.ItemIsEditable:
item.setFlags(tmp ^ QtCore.Qt.ItemIsEditable)
There are no methods / flags to configure this directly.
You can solve this by switching off Qt's EditTriggers completely and implementing your own function that decides whether an item should be edited or not:
class MyWidget(QWidget):
def __init__(parent):
self.treeWidget = ...
# switch off "default" editing behaviour
# as it does not allow to configure only an individual
# column as editable
self.treeWidget.setEditTriggers(self._treeWidget.NoEditTriggers)
# to be able to decide on your own whether a particular item
# can be edited, connect e.g. to itemDoubleClicked
self.treeWidget.itemDoubleClicked.connect(self.checkEdit)
# in your connected slot, you can implement any edit-or-not-logic
# you want
def checkEdit(self, item, column):
# e.g. to allow editing only of column 1:
if column == 1:
self.treeWidget.editItem(item, column)
If you'd like to editing on other occasions as well, simply connect checkEdit to the according signals, such as itemClicked, itemEntered etc.