2013-08-05 22 views
10

Tło: Nie mogę znaleźć pełnego działającego przykładu combobox wewnątrz QTableView. Napisałem więc ten kod na podstawie kilku innych, bardziej wymyślnych przykładów. Problem polega jednak na tym, że ten przykład wymaga dwukrotnego kliknięcia na combobox, zanim zostanie włączony, a następnie trzeba kliknąć ponownie, aby go upuścić. To nie jest bardzo przyjazne dla użytkownika. Jeśli wykonam model inny niż model/widok za pomocą QTableWidget, combobox spadnie po pierwszym kliknięciu.PyQt - najprostszy działający przykład combobox wewnątrz QTableView

Pytanie: Czy ktoś może spojrzeć na to i powiedz mi, co należy zrobić, aby odpowiedzieć tak jak QTableWidget? Jeśli jest coś, co robię, nie jest konieczne, proszę również o tym powiedzieć. Na przykład, czy absolutnie konieczne jest odwoływanie się do stylu aplikacji?

import sys 
from PyQt4 import QtGui, QtCore 

rows = "ABCD" 
choices = ['apple', 'orange', 'banana'] 

class Delegate(QtGui.QItemDelegate): 
    def __init__(self, owner, items): 
     super(Delegate, self).__init__(owner) 
     self.items = items 
    def createEditor(self, parent, option, index): 
     self.editor = QtGui.QComboBox(parent) 
     self.editor.addItems(self.items) 
     return self.editor 
    def paint(self, painter, option, index): 
     value = index.data(QtCore.Qt.DisplayRole).toString() 
     style = QtGui.QApplication.style() 
     opt = QtGui.QStyleOptionComboBox() 
     opt.text = str(value) 
     opt.rect = option.rect 
     style.drawComplexControl(QtGui.QStyle.CC_ComboBox, opt, painter) 
     QtGui.QItemDelegate.paint(self, painter, option, index) 
    def setEditorData(self, editor, index): 
     value = index.data(QtCore.Qt.DisplayRole).toString() 
     num = self.items.index(value) 
     editor.setCurrentIndex(num) 
    def setModelData(self, editor, model, index): 
     value = editor.currentText() 
     model.setData(index, QtCore.Qt.DisplayRole, QtCore.QVariant(value)) 
    def updateEditorGeometry(self, editor, option, index): 
     editor.setGeometry(option.rect) 

class Model(QtCore.QAbstractTableModel): 
    def __init__(self): 
     super(Model, self).__init__() 
     self.table = [[row, choices[0]] for row in rows] 
    def rowCount(self, index=QtCore.QModelIndex()): 
     return len(self.table) 
    def columnCount(self, index=QtCore.QModelIndex()): 
     return 2 
    def flags(self, index): 
     return QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable 
    def data(self, index, role): 
     if role == QtCore.Qt.DisplayRole: 
      return self.table[index.row()][index.column()] 
    def setData(self, index, role, value): 
     if role == QtCore.Qt.DisplayRole: 
      self.table[index.row()][index.column()] = value 

class Main(QtGui.QMainWindow): 
    def __init__(self, parent=None): 
     super(Main, self).__init__(parent) 
     self.model = Model() 
     self.table = QtGui.QTableView() 
     self.table.setModel(self.model) 
     self.table.setItemDelegateForColumn(1, Delegate(self, ["apple", "orange", "banana"])) 
     self.setCentralWidget(self.table) 
     self.setWindowTitle('Delegate Test') 
     self.show() 

if __name__ == '__main__': 
    app = QtGui.QApplication(sys.argv) 
    main = Main() 
    app.exec_() 
+0

można znaleźć odpowiedź na [to pytanie] (http://stackoverflow.com/questions/17615997/pyqt-how-to-set-qcombobox-in- a-table-view-using-qitemdelegate) pomocne. –

+1

Dziękuję, teraz widzę, że nadpisanie 'paint' jest niepotrzebne i potrzebuję' openPersistentEditor'. Ale wywołanie 'openPersistentEditor' wydaje się przekreślać cel modelu/widoku, jeśli muszę go wywołać spoza modelu. Dodatkowo, wydaje się nieefektywne narysowanie wszystkich tych comboboxów, kiedy można operować tylko jednym na raz. Czy istnieje sposób, aby pozbyć się wymogu podwójnego kliknięcia, aby pojawił się po wybraniu komórki? – user2120303

+0

Nie musisz wywoływać tego z modelu. Możesz użyć innego obiektu (np. Twojego podklasowanego widoku lub formularza) do śledzenia zmiany modelu i wywołać edytora, jeśli to konieczne. W drugim pytaniu połącz sygnał 'selectionChanged' z' view-> selectionModel() 'z twoim gniazdem. W tym oknie otwórz edytor w wybranej komórce i zamknij poprzednie edycje, jeśli to konieczne. –

Odpowiedz

5

Korzystanie QTableWiget.setCellWidget

import sys 
from PyQt4 import QtGui 
app = QtGui.QApplication(sys.argv) 
table = QtGui.QTableWidget(1,1) 
combobox = QtGui.QComboBox() 
combobox.addItem("Combobox item") 
table.setCellWidget(0,0, combobox) 
table.show() 
app.exec() 
+0

Pytanie prosi o combo-box wewnątrz QTableView, a nie QTableWidget. – ekhumoro

+0

@ekhumoro Tak, widziałem to, ale myślałem, że po prostu chciał, aby uzyskać widget w tabeli. Prawdopodobnie usunę tę odpowiedź. –

1

Jeśli próbujesz zmieniać, jeśli widok wyświetla edytor, trzeba zmienić spust edycji zdefiniowane w QAbstractItemView. Domyślną wartością jest edycja na DoubleClick, ale myślę, że to, co robisz, to QAbstractItemView.CurrentChanged. Ustawić ją dzwoniąc myView.setEditTrigger()

-1

Jeśli ktoś jest zainteresowany, poniżej jest taki sam przykład zmodyfikowane do PyQt5 i Python 3. Kluczowe aktualizacje obejmują:

  • Python 3: super().__init__()
  • PyQt5: większość klasy są w QtWidgets ; QtGui nie jest potrzebny do tego przykładu
  • Model.setData: wejście zamówienie argumentem zmienione na: index, value, role i True zwrócony zamiast None
  • Pole kombi choices stołowych określonych treści teraz wewnątrz Main; to sprawia Delegate i Model bardziej ogólnego
from PyQt5 import QtWidgets, QtCore 

class Delegate(QtWidgets.QItemDelegate): 
    def __init__(self, owner, choices): 
     super().__init__(owner) 
     self.items = choices 
    def createEditor(self, parent, option, index): 
     self.editor = QtWidgets.QComboBox(parent) 
     self.editor.addItems(self.items) 
     return self.editor 
    def paint(self, painter, option, index): 
     value = index.data(QtCore.Qt.DisplayRole) 
     style = QtWidgets.QApplication.style() 
     opt = QtWidgets.QStyleOptionComboBox() 
     opt.text = str(value) 
     opt.rect = option.rect 
     style.drawComplexControl(QtWidgets.QStyle.CC_ComboBox, opt, painter) 
     QtWidgets.QItemDelegate.paint(self, painter, option, index) 
    def setEditorData(self, editor, index): 
     value = index.data(QtCore.Qt.DisplayRole) 
     num = self.items.index(value) 
     editor.setCurrentIndex(num) 
    def setModelData(self, editor, model, index): 
     value = editor.currentText() 
     model.setData(index, QtCore.Qt.DisplayRole, QtCore.QVariant(value)) 
    def updateEditorGeometry(self, editor, option, index): 
     editor.setGeometry(option.rect) 

class Model(QtCore.QAbstractTableModel): 
    def __init__(self, table): 
     super().__init__() 
     self.table = table 
    def rowCount(self, parent): 
     return len(self.table) 
    def columnCount(self, parent): 
     return len(self.table[0]) 
    def flags(self, index): 
     return QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable 
    def data(self, index, role): 
     if role == QtCore.Qt.DisplayRole: 
      return self.table[index.row()][index.column()] 
    def setData(self, index, value, role): 
     if role == QtCore.Qt.EditRole: 
      self.table[index.row()][index.column()] = value 
     return True 

class Main(QtWidgets.QMainWindow): 
    def __init__(self, parent=None): 
     super().__init__(parent) 
     # set combo box choices: 
     choices = ['apple', 'orange', 'banana'] 
     # create table data: 
     table = [] 
     table.append(['A', choices[0]]) 
     table.append(['B', choices[0]]) 
     table.append(['C', choices[0]]) 
     table.append(['D', choices[0]]) 
     # create table view: 
     self.model  = Model(table) 
     self.tableView = QtWidgets.QTableView() 
     self.tableView.setModel(self.model) 
     self.tableView.setItemDelegateForColumn(1, Delegate(self,choices)) 
     # make combo boxes editable with a single-click: 
     for row in range(len(table)): 
      self.tableView.openPersistentEditor(self.model.index(row, 1)) 
     # initialize 
     self.setCentralWidget(self.tableView) 
     self.setWindowTitle('Delegate Test') 
     self.show() 

if __name__ == '__main__': 
    import sys 
    app = QtWidgets.QApplication(sys.argv) 
    main = Main() 
    app.exec_() 
Powiązane problemy