PyQt4: Unterbinden dass etwas zur QComboBox() hinzugefügt wird

Python und das Qt-Toolkit, erstellen von GUIs mittels des Qt-Designers.
Benutzeravatar
Sophus
User
Beiträge: 1031
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

PyQt4: Unterbinden dass etwas zur QComboBox() hinzugefügt wird

Beitragvon Sophus » Samstag 29. Juli 2017, 21:47

Hallo Leute, mir ist etwas Unschönes aufgefallen. Zunächst einmal seht ihr, dass ich mit dem QCompleter() im Zusammenhang mit QComboBox() arbeite, und ich dazu die QComboBox() so eingestellt habe, dass man editieren kann. Tippt man die ersten Buchstaben ein, werden die Treffer in einem PopUp angezeigt. Soweit alles wunderbar. Aber angenommen, ihr tippt einfach wild drauf los, irgendwelche Buchstaben und drückt dann Enter. Das macht ihr mehrmals hintereinander. Und ich klickt dann mit der Maus auf die QComboBox(), dann sieht man, dass die wild eingegebenen Buchstaben ebenfalls in der QComboBox() sind. Wie kann ich das unterbinden? Ich möchte, dass die Benutzer nur mit den Daten aus der Liste arbeiten können, keine neuen Einträge.

Habt ihr eine Idee?


  1. # -*- coding: cp1252 -*-
  2. import sys
  3.  
  4. from PyQt4.QtCore import Qt, QVariant, SIGNAL, QEvent
  5. from PyQt4.QtGui import QApplication, QStandardItemModel, QStandardItem, QTreeView, QComboBox, QDialog, \
  6.                          QVBoxLayout, QPushButton, QAbstractItemView, QCompleter, QSortFilterProxyModel, \
  7.                          QKeyEvent
  8.  
  9. class MyCustomDialog(QDialog):
  10.     def __init__(self, app=None, parent=None):
  11.         QDialog.__init__(self, parent)
  12.  
  13.         self.app = app
  14.  
  15.         # Second, we need our QTreeView() and
  16.         # the settings
  17.         self.init_tree_view()
  18.  
  19.         # Create an empty model for the TreeViews' data
  20.         _standard_item_model = QStandardItemModel(0,2)
  21.  
  22.        # Add some textual items
  23.         self.food_list = [
  24.                     ["0", 'Cookie dough'],
  25.                     ["1", 'Hummus'],
  26.                     ["2", 'Spaghetti'],
  27.                     ["3", 'Dal makhani'],
  28.                     ["6", 'Blolonese'],
  29.                     ["4", 'Hachfleisch'],
  30.                     ["3", 'Nudeln'],
  31.                     ["666", 'Flösch'],
  32.                     ["4", 'Chocolate whipped cream']
  33.                 ]
  34.         # Now its time to populate data
  35.         self.populate(model=_standard_item_model)
  36.  
  37.         # Apply the model to the list view
  38.         self.set_tree_view_model(_standard_item_model)
  39.  
  40.         # QComboBox() will be created
  41.         self.combo_box = QComboBox(self)
  42.  
  43.         self.init_complete(model=_standard_item_model)
  44.  
  45.         # layout is a defined QVBoxLayout()
  46.         layout = QVBoxLayout(self)
  47.         layout.addWidget(self.combo_box)
  48.         self.setLayout(layout)
  49.  
  50.     def init_tree_view(self):
  51.         self.tree_view = QTreeView()
  52.         self.tree_view.setRootIsDecorated(False)
  53.         self.tree_view.setWordWrap(True)
  54.  
  55.         self.tree_view.setAlternatingRowColors(True)
  56.  
  57.         self.tree_view.setSelectionMode(QTreeView.ExtendedSelection)
  58.        
  59.         self.tree_view.header().hide()
  60.  
  61.         self.tree_me = QTreeView()
  62.         self.tree_me.setRootIsDecorated(False)
  63.         self.tree_me.setWordWrap(True)
  64.         self.tree_me.setAlternatingRowColors(True)
  65.  
  66.         self.tree_me.setSelectionMode(QTreeView.ExtendedSelection)
  67.        
  68.         self.tree_me.header().hide()
  69.  
  70.     def init_complete(self, model=None):
  71.  
  72.         # add a completer, which uses the filter model
  73.         # add a filter model to filter matching items
  74.         filterModel = QSortFilterProxyModel(self.combo_box)
  75.         filterModel.setFilterCaseSensitivity(Qt.CaseInsensitive)
  76.         filterModel.setSourceModel(model)
  77.         filterModel.setFilterKeyColumn(0)
  78.  
  79.         completer = QCompleter(self.combo_box)
  80.  
  81.         # Set the model that the QCompleter uses
  82.         # on model column change, update the model
  83.         # column of the filter and completer as well
  84.         completer.setModel(filterModel)
  85.         completer.setCompletionColumn(0)
  86.         completer.popup().installEventFilter(self)
  87.         completer.popup().selectionModel().selectionChanged.connect(lambda new_index:
  88.                     self.get_id_tree_view(new_index=new_index))
  89.  
  90.         # always show all (filtered) completions
  91.         completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion)
  92.        
  93.         completer.setPopup( self.tree_me )
  94.  
  95.         self.tree_me.setColumnHidden(1, True)
  96.  
  97.         self.combo_box.setEditable(True)
  98.         self.combo_box.setCompleter(completer)
  99.  
  100.         # on model column change, update the model
  101.         # column of the filter and completer as well        
  102.         self.combo_box.setModel(model)
  103.         self.combo_box.setView(self.tree_view)
  104.  
  105.         # on model column change, update the model column of
  106.         # the filter and completer as well
  107.         self.combo_box.setModelColumn(0)
  108.        
  109.         self.tree_view.resizeColumnToContents(0)
  110.        
  111.         self.tree_view.setColumnHidden(1, True)
  112.  
  113.         if self.combo_box.isEditable():
  114.             self.app.connect(self.combo_box.lineEdit(), SIGNAL('textEdited(QString)'), filterModel.setFilterFixedString)
  115.  
  116.     def set_tree_view_model(self, model):
  117.        
  118.         self.tree_view.setModel(model)
  119.  
  120.     def generator_header_data(self, list_header):
  121.  
  122.         for header_data in list_header:
  123.             yield header_data
  124.            
  125.     def set_header_data(self, list_header_data=None, model=None):
  126.  
  127.         count_column = 0
  128.  
  129.         for header_data in self.generator_header_data(list_header_data):
  130.             model.setHeaderData(count_column, Qt.Horizontal, header_data)
  131.  
  132.             count_column +=1
  133.  
  134.     def get_id_tree_view(self, new_index=None):
  135.  
  136.         try:
  137.             if not new_index is None:
  138.  
  139.                 index = new_index.indexes()[1].data()#.toPyObject()
  140.  
  141.                 if isinstance(index, QVariant):
  142.                     print "get_id_tree_view, index", index.toString()
  143.                    
  144.         except IndexError as InErr:
  145.             pass#print "InErr", InErr
  146.  
  147.     def populate_data_item(self, item_list=None, model=None):
  148.        
  149.         count_items = len(item_list)
  150.  
  151.         if count_items == 2:
  152.  
  153.             item_first, item_second = item_list
  154.  
  155.         two_columns_item = [QStandardItem(item_second), QStandardItem(str(item_first))]
  156.  
  157.         model.appendRow(two_columns_item)
  158.  
  159.     def populate(self, model=None):
  160.        
  161.         for single_list in self.food_list:
  162.             self.populate_data_item(item_list=single_list, model=model)
  163.  
  164. def main():
  165.     app = QApplication(sys.argv)
  166.     window = MyCustomDialog(app=app)
  167.     window.resize(300, 50)
  168.     window.show()
  169.     try:
  170.         sip.setdestroyonexit(False)
  171.     except:
  172.         # missing in older versions
  173.         pass
  174.     sys.exit(app.exec_())
  175.  
  176. if __name__ == "__main__":
  177.     main()
Benutzeravatar
Sophus
User
Beiträge: 1031
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

Re: PyQt4: Unterbinden dass etwas zur QComboBox() hinzugefügt wird

Beitragvon Sophus » Samstag 29. Juli 2017, 22:00

Ok, dass war jetzt ein Schnellschuss, aber es klappt erst einmal. In Zeile 166 habe ich die eventFilter()-Methode re-implementiert, um die Enter-Taste zu ignorieren. In Zeile 105 sieht man, dass ich diesen eventFilter bei der QComboBox() installiert habe. Der Plan scheint erst einmal aufzugehen. Keine weitere, neuen Eintragungen in die QComboBox() mehr, und der Benutzer ist gezwungen, mit den Daten aus der Liste zu arbeiten.

Nur habe ich gehofft und geglaubt, dies wäre auch ohne diesen eventFilter möglich. Vielleicht eine kleine Einstellung bei der QComboBox() selbst, die solche Situation verhindert?

  1. # -*- coding: cp1252 -*-
  2. import sys
  3.  
  4. from PyQt4.QtCore import Qt, QVariant, SIGNAL, QEvent
  5. from PyQt4.QtGui import QApplication, QStandardItemModel, QStandardItem, QTreeView, QComboBox, QDialog, \
  6.                          QVBoxLayout, QPushButton, QAbstractItemView, QCompleter, QSortFilterProxyModel, \
  7.                          QKeyEvent
  8.  
  9. class MyCustomDialog(QDialog):
  10.     def __init__(self, app=None, parent=None):
  11.         QDialog.__init__(self, parent)
  12.  
  13.         self.app = app
  14.  
  15.         # Second, we need our QTreeView() and
  16.         # the settings
  17.         self.init_tree_view()
  18.  
  19.         # Create an empty model for the TreeViews' data
  20.         _standard_item_model = QStandardItemModel(0,2)
  21.  
  22.        # Add some textual items
  23.         self.food_list = [
  24.                     ["0", 'Cookie dough'],
  25.                     ["1", 'Hummus'],
  26.                     ["2", 'Spaghetti'],
  27.                     ["3", 'Dal makhani'],
  28.                     ["6", 'Blolonese'],
  29.                     ["4", 'Hachfleisch'],
  30.                     ["3", 'Nudeln'],
  31.                     ["666", 'Flösch'],
  32.                     ["4", 'Chocolate whipped cream']
  33.                 ]
  34.         # Now its time to populate data
  35.         self.populate(model=_standard_item_model)
  36.  
  37.         # Apply the model to the list view
  38.         self.set_tree_view_model(_standard_item_model)
  39.  
  40.         # QComboBox() will be created
  41.         self.combo_box = QComboBox(self)
  42.  
  43.         self.init_complete(model=_standard_item_model)
  44.  
  45.         # layout is a defined QVBoxLayout()
  46.         layout = QVBoxLayout(self)
  47.         layout.addWidget(self.combo_box)
  48.         self.setLayout(layout)
  49.  
  50.     def init_tree_view(self):
  51.         self.tree_view = QTreeView()
  52.         self.tree_view.setRootIsDecorated(False)
  53.         self.tree_view.setWordWrap(True)
  54.  
  55.         self.tree_view.setAlternatingRowColors(True)
  56.  
  57.         self.tree_view.setSelectionMode(QTreeView.ExtendedSelection)
  58.        
  59.         self.tree_view.header().hide()
  60.  
  61.         self.tree_me = QTreeView()
  62.         self.tree_me.setRootIsDecorated(False)
  63.         self.tree_me.setWordWrap(True)
  64.         self.tree_me.setAlternatingRowColors(True)
  65.  
  66.         self.tree_me.setSelectionMode(QTreeView.ExtendedSelection)
  67.        
  68.         self.tree_me.header().hide()
  69.  
  70.     def init_complete(self, model=None):
  71.  
  72.         # add a completer, which uses the filter model
  73.         # add a filter model to filter matching items
  74.         filterModel = QSortFilterProxyModel(self.combo_box)
  75.         filterModel.setFilterCaseSensitivity(Qt.CaseInsensitive)
  76.         filterModel.setSourceModel(model)
  77.         filterModel.setFilterKeyColumn(0)
  78.  
  79.         completer = QCompleter(self.combo_box)
  80.  
  81.         # Set the model that the QCompleter uses
  82.         # on model column change, update the model
  83.         # column of the filter and completer as well
  84.         completer.setModel(filterModel)
  85.         completer.setCompletionColumn(0)
  86.         completer.popup().installEventFilter(self)
  87.         completer.popup().selectionModel().selectionChanged.connect(lambda new_index:
  88.                     self.get_id_tree_view(new_index=new_index))
  89.  
  90.         # always show all (filtered) completions
  91.         completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion)
  92.        
  93.         completer.setPopup( self.tree_me )
  94.  
  95.         self.tree_me.setColumnHidden(1, True)
  96.  
  97.         self.combo_box.setEditable(True)
  98.         self.combo_box.setCompleter(completer)
  99.  
  100.         # on model column change, update the model
  101.         # column of the filter and completer as well        
  102.         self.combo_box.setModel(model)
  103.         self.combo_box.setView(self.tree_view)
  104.  
  105.         self.combo_box.installEventFilter(self)
  106.  
  107.         # on model column change, update the model column of
  108.         # the filter and completer as well
  109.         self.combo_box.setModelColumn(0)
  110.        
  111.         self.tree_view.resizeColumnToContents(0)
  112.        
  113.         self.tree_view.setColumnHidden(1, True)
  114.  
  115.         if self.combo_box.isEditable():
  116.             self.app.connect(self.combo_box.lineEdit(), SIGNAL('textEdited(QString)'), filterModel.setFilterFixedString)
  117.  
  118.     def set_tree_view_model(self, model):
  119.        
  120.         self.tree_view.setModel(model)
  121.  
  122.     def generator_header_data(self, list_header):
  123.  
  124.         for header_data in list_header:
  125.             yield header_data
  126.            
  127.     def set_header_data(self, list_header_data=None, model=None):
  128.  
  129.         count_column = 0
  130.  
  131.         for header_data in self.generator_header_data(list_header_data):
  132.             model.setHeaderData(count_column, Qt.Horizontal, header_data)
  133.  
  134.             count_column +=1
  135.  
  136.     def get_id_tree_view(self, new_index=None):
  137.  
  138.         try:
  139.             if not new_index is None:
  140.  
  141.                 index = new_index.indexes()[1].data()#.toPyObject()
  142.  
  143.                 if isinstance(index, QVariant):
  144.                     print "get_id_tree_view, index", index.toString()
  145.                    
  146.         except IndexError as InErr:
  147.             pass#print "InErr", InErr
  148.  
  149.     def populate_data_item(self, item_list=None, model=None):
  150.        
  151.         count_items = len(item_list)
  152.  
  153.         if count_items == 2:
  154.  
  155.             item_first, item_second = item_list
  156.  
  157.         two_columns_item = [QStandardItem(item_second), QStandardItem(str(item_first))]
  158.  
  159.         model.appendRow(two_columns_item)
  160.  
  161.     def populate(self, model=None):
  162.        
  163.         for single_list in self.food_list:
  164.             self.populate_data_item(item_list=single_list, model=model)
  165.  
  166.     def eventFilter(self, widget, event):
  167.         """Reimplemented to handle key press events"""
  168.        
  169.         if (event.type() == QEvent.KeyPress):
  170.             # use the in operator to test if the key is in a list or tuple
  171.             if event.key() in (Qt.Key_Enter, Qt.Key_Return):
  172.                 return True
  173.            
  174.         return QDialog.eventFilter(self, widget, event)
  175.  
  176. def main():
  177.     app = QApplication(sys.argv)
  178.     window = MyCustomDialog(app=app)
  179.     window.resize(300, 50)
  180.     window.show()
  181.     try:
  182.         sip.setdestroyonexit(False)
  183.     except:
  184.         # missing in older versions
  185.         pass
  186.     sys.exit(app.exec_())
  187.  
  188. if __name__ == "__main__":
  189.     main()
Benutzeravatar
Sophus
User
Beiträge: 1031
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

Re: PyQt4: Unterbinden dass etwas zur QComboBox() hinzugefügt wird

Beitragvon Sophus » Mittwoch 2. August 2017, 22:46

Ich glaube, ich kann jetzt auf die re-implementierte evenFilter()-Methode verzichten. Nach langem Suchen fand ich einen Hinweis.

Allgemeines Verhalten der QComboBox(), welche man editieren kann:
When the user enters a new string in an editable combobox, the widget may or may not insert it, and it can insert it in several locations. The default policy is is AtBottom but you can change this using setInsertPolicy().
(Quelle: Qt-Dokumentation - Detailed Description).

Und genau das wollte ich ja unterbinden, dass keine neuen Daten in die QComboBox() hinzugefügt werden, sobald der Benutzer die Enter-Taste betätigt. Also fand ich das hier:
void setInsertPolicy(InsertPolicy policy)

QComboBox::NoInsert - The string will not be inserted into the combobox.
(Quelle: Qt-Dokumentation - enum QComboBox::InsertPolicy)

Also sieht mein Quelltext wie folgt aus: In Zeile 42 habe ich die QComboBox() so eingestellt, dass keine weiteren Strings hinzugefügt werden soll.
  1. # -*- coding: cp1252 -*-
  2. import sys
  3.  
  4. from PyQt4.QtCore import Qt, QVariant, SIGNAL, QEvent
  5. from PyQt4.QtGui import QApplication, QStandardItemModel, QStandardItem, QTreeView, QComboBox, QDialog, \
  6.                          QVBoxLayout, QPushButton, QAbstractItemView, QCompleter, QSortFilterProxyModel, \
  7.                          QKeyEvent
  8.  
  9. class MyCustomDialog(QDialog):
  10.     def __init__(self, app=None, parent=None):
  11.         QDialog.__init__(self, parent)
  12.  
  13.         self.app = app
  14.  
  15.         # Second, we need our QTreeView() and
  16.         # the settings
  17.         self.init_tree_view()
  18.  
  19.         # Create an empty model for the TreeViews' data
  20.         _standard_item_model = QStandardItemModel(0,2)
  21.  
  22.        # Add some textual items
  23.         self.food_list = [
  24.                     ["0", 'Cookie dough'],
  25.                     ["1", 'Hummus'],
  26.                     ["2", 'Spaghetti'],
  27.                     ["3", 'Dal makhani'],
  28.                     ["6", 'Blolonese'],
  29.                     ["4", 'Hachfleisch'],
  30.                     ["3", 'Nudeln'],
  31.                     ["666", 'Flösch'],
  32.                     ["4", 'Chocolate whipped cream']
  33.                 ]
  34.         # Now its time to populate data
  35.         self.populate(model=_standard_item_model)
  36.  
  37.         # Apply the model to the list view
  38.         self.set_tree_view_model(_standard_item_model)
  39.  
  40.         # QComboBox() will be created
  41.         self.combo_box = QComboBox(self)
  42.         self.combo_box.setInsertPolicy(QComboBox.NoInsert) #The string will not be inserted into the combobox.
  43.  
  44.         self.init_complete(model=_standard_item_model)
  45.  
  46.         # layout is a defined QVBoxLayout()
  47.         layout = QVBoxLayout(self)
  48.         layout.addWidget(self.combo_box)
  49.         self.setLayout(layout)
  50.  
  51.     def init_tree_view(self):
  52.         self.tree_view = QTreeView()
  53.         self.tree_view.setRootIsDecorated(False)
  54.         self.tree_view.setWordWrap(True)
  55.  
  56.         self.tree_view.setAlternatingRowColors(True)
  57.  
  58.         self.tree_view.setSelectionMode(QTreeView.ExtendedSelection)
  59.        
  60.         self.tree_view.header().hide()
  61.  
  62.         self.tree_me = QTreeView()
  63.         self.tree_me.setRootIsDecorated(False)
  64.         self.tree_me.setWordWrap(True)
  65.         self.tree_me.setAlternatingRowColors(True)
  66.  
  67.         self.tree_me.setSelectionMode(QTreeView.ExtendedSelection)
  68.        
  69.         self.tree_me.header().hide()
  70.  
  71.     def init_complete(self, model=None):
  72.  
  73.         # add a completer, which uses the filter model
  74.         # add a filter model to filter matching items
  75.         filterModel = QSortFilterProxyModel(self.combo_box)
  76.         filterModel.setFilterCaseSensitivity(Qt.CaseInsensitive)
  77.         filterModel.setSourceModel(model)
  78.         filterModel.setFilterKeyColumn(0)
  79.  
  80.         completer = QCompleter(self.combo_box)
  81.  
  82.         # Set the model that the QCompleter uses
  83.         # on model column change, update the model
  84.         # column of the filter and completer as well
  85.         completer.setModel(filterModel)
  86.         completer.setCompletionColumn(0)
  87.         completer.popup().selectionModel().selectionChanged.connect(lambda new_index:
  88.                     self.get_id_tree_view(new_index=new_index))
  89.  
  90.         # always show all (filtered) completions
  91.         completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion)
  92.        
  93.         completer.setPopup( self.tree_me )
  94.  
  95.         self.tree_me.setColumnHidden(1, True)
  96.  
  97.         self.combo_box.setEditable(True)
  98.         self.combo_box.setCompleter(completer)
  99.  
  100.         # on model column change, update the model
  101.         # column of the filter and completer as well        
  102.         self.combo_box.setModel(model)
  103.         self.combo_box.setView(self.tree_view)
  104.  
  105.         # on model column change, update the model column of
  106.         # the filter and completer as well
  107.         self.combo_box.setModelColumn(0)
  108.        
  109.         self.tree_view.resizeColumnToContents(0)
  110.        
  111.         self.tree_view.setColumnHidden(1, True)
  112.  
  113.         if self.combo_box.isEditable():
  114.             self.app.connect(self.combo_box.lineEdit(), SIGNAL('textEdited(QString)'), filterModel.setFilterFixedString)
  115.  
  116.     def set_tree_view_model(self, model):
  117.        
  118.         self.tree_view.setModel(model)
  119.  
  120.     def generator_header_data(self, list_header):
  121.  
  122.         for header_data in list_header:
  123.             yield header_data
  124.            
  125.     def set_header_data(self, list_header_data=None, model=None):
  126.  
  127.         count_column = 0
  128.  
  129.         for header_data in self.generator_header_data(list_header_data):
  130.             model.setHeaderData(count_column, Qt.Horizontal, header_data)
  131.  
  132.             count_column +=1
  133.  
  134.     def get_id_tree_view(self, new_index=None):
  135.  
  136.         try:
  137.             if not new_index is None:
  138.  
  139.                 index = new_index.indexes()[1].data()#.toPyObject()
  140.  
  141.                 if isinstance(index, QVariant):
  142.                     print "get_id_tree_view, index", index.toString()
  143.                    
  144.         except IndexError as InErr:
  145.             pass#print "InErr", InErr
  146.  
  147.     def populate_data_item(self, item_list=None, model=None):
  148.        
  149.         count_items = len(item_list)
  150.  
  151.         if count_items == 2:
  152.  
  153.             item_first, item_second = item_list
  154.  
  155.         two_columns_item = [QStandardItem(item_second), QStandardItem(str(item_first))]
  156.  
  157.         model.appendRow(two_columns_item)
  158.  
  159.     def populate(self, model=None):
  160.        
  161.         for single_list in self.food_list:
  162.             self.populate_data_item(item_list=single_list, model=model)
  163.  
  164. def main():
  165.     app = QApplication(sys.argv)
  166.     window = MyCustomDialog(app=app)
  167.     window.resize(300, 50)
  168.     window.show()
  169.     try:
  170.         sip.setdestroyonexit(False)
  171.     except:
  172.         # missing in older versions
  173.         pass
  174.     sys.exit(app.exec_())
  175.  
  176. if __name__ == "__main__":
  177.     main()

Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder