[PyQt] question about QComboBox

Phillip Feldman phillip.m.feldman at gmail.com
Fri Apr 8 06:04:29 BST 2016


My question was badly worded.  The PyQt4/PySide documentation for QComboBox
does show a setStyleSheet method for the top-level widget, and this works
just fine, but it looks as though there is nothing comparable for the
individual combobox options.  I've written a class that manipulates the
foreround and background colors of individual combobox options, but it does
not work perfectly, and it is also surprising that so much code is needed
to accomplish so little.  There must be a better way.  Any advice will be
appreciated.

Phillip

class UserCombo(UserWidget):
   """
   This class generates a label and a ComboBox (dropdown list) laid out
   horizontally.  The ComboBox can optionally be made editable.  Note:

   - With a ListBox, which is added via the `add_list` method, options are
   visible at all times (unless scrolled off the top or bottom of the
window),
   and it may be possible-depending on the value of the multiselect
argument--
   for the user to select more than one option.

   - With a ComboBox, which is added via the `add_combo` method, the list of
   options is visible only when one activates the widget by clicking on it,
and
   one cannot select more than one option.

   Because of inconsistencies between Qt widget interfaces, this class works
   somewhat differently from the other user dialog widgets.  In particular,
one
   cannot apply html/css stylesheets to the individual options.  The
separate
   arguments `font_size`, `background_colors`, and `foreground_colors` do,
   however, provide some control over the appearance of the individual
options.

   References:


http://stackoverflow.com/questions/22887496/pyqt-how-to-customize-combobox-items-display
   """

   def __init__(self, name, label='', options=[], default=None,
editable=False,
     label_style_sheet=None, field_style_sheet='font-size:12pt;
color:#0000CC',
     font_size='12pt',
     background_colors=None, foreground_colors=None, validator=None):
      """
      `name` is a string that identifies this field when dialog inputs are
      returned.

      `label` is a string containing identifying text to be displayed above
the
      group of radio buttons.

      `options` is a non-empty list or tuple of strings.  Each string
appears as
      an item in the drop-down list.

      `default`--an optional input--indicates the default selection.  If a
      default is provided, it must match one of the strings in `options`.

      `editable`--MISSING TEXT AT THIS POINT!

      `label_style_sheet`--an optional input--is a string containing an html
      style sheet to be applied to the label text (prompt).  The default is
      'font-size:12pt; color:#000000', which causes the label to be
displayed in
      a 12-point black font.

      `field_style_sheet`--an optional argument--is a string containing an
html
      style sheet to be applied to the top-level widget.  The default is
      'font-size:12pt;color:#0000CC'.

      `font_size`--an optional argument--is a string specifying the font
size
      for option text.  The default is '12pt'.

      `background_colors` and `foreground_colors`--optional arguments--are
each
      either a single string or a list of strings.  If a list is provided,
its
      length must match that of `options`.  Each string is an html color
name
      that sets the background or foreground (text) color, respectively, of
the
      corresponding option, or of all options if only a single string (not
      enclosed in a list) is provided.  Both defaults are `None`, indicating
      that widget defaults apply.
      """
      super(UserCombo, self).__init__(name, validator=validator)
      layout= QGridLayout()
      self.setLayout(layout)

      self.label= QLabel(label)
      if label_style_sheet is not None:
         self.label.setStyleSheet(label_style_sheet)

      self.field= QComboBox()
      self.field.setEditable(editable)
      self.field.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
      self.label.setBuddy(self.field)

      # Save calling arguments for later use:
      self.font_size= font_size
      self.background_colors= copy.deepcopy(background_colors)
      self.foreground_colors= copy.deepcopy(foreground_colors)

      # Creating a 'model' allows us to add instances of QStandardItem with
      # individual formatting:
      self.model= self.field.model()
      self.items= []

      for i_option, option in enumerate(options):
         item= QStandardItem(option)
         self.model.appendRow(item)
         self.items.append(item)
         self.index_changed(i_option)

      self.field.setStyleSheet(field_style_sheet)

      # Connect field to method that will be invoked whenever the user
makes a
      # new selection:
      self.field.currentIndexChanged.connect(self.index_changed)

      layout.addWidget(self.label, 0, 0)
      layout.addWidget(self.field, 0, 1)
      layout.addWidget(self.errorLabel, 1, 1)

      # The editTextChanged event is not sent when the box is not
editable.  We
      # need to use currentIndexChanged in that case.
      if editable:
         self.field.editTextChanged.connect(self.field_updated_handler)
      else:
         self.field.currentIndexChanged.connect(self.field_updated_handler)

      self.value= default


   def get_value(self):
      return self.field.currentText().encode('ascii', 'replace')


   def set_value(self, newval):
      # If the new value is one of the items in the list, select it.
      idx= self.field.findText(newval)
      if idx >= 0:
         self.field.setCurrentIndex(idx)
      elif self.field.isEditable():
         # If the new value is not in the list, only set it if
         # the field is editable, otherwise do nothing.
         self.field.setEditText(newval)


   def index_changed(self, i_option):
      """
      This method gets called when the user selects an item from the list.
It
      transfers color attributes from the selected item to the top-level
widget.
      """

      item= self.items[i_option]

      bg= self.background_colors
      if bg is not None:
         if isinstance(bg, list):

            # There is a separate background style for each list item:
            bg= bg[i_option]

         item.setBackground(QColor(bg))

      fg= self.foreground_colors
      if fg is not None:
         if isinstance(fg, list):

            # There is a separate foreground style for each list item:
            fg= fg[i_option]

         item.setForeground(QColor(fg))

      # Set field stylesheet to match the most recent selection:
      field_style_sheet= 'font-size:{fs}; ' \
        'selection-background-color:{bg}; background-color:{bg};
color:{fg}' \
        .format(fs=self.font_size, bg=bg, fg=fg)
      self.field.setStyleSheet(field_style_sheet)

On Thu, Apr 7, 2016 at 3:37 PM, Dave Gradwell <davegradwell at yahoo.co.uk>
wrote:

> Hi Phillip,
>
> > Worse yet, the API documentation does not mention a setStyleSheet method
> QComboBox inherits setStyleSheet() from QWidget (where it may be better
> documented) so this should work:
> QtWidgets.QComboBox(parent).setStyleSheet("color:red;”)
>
> You may be able to style individual items by manipulating the Painter:
>
> http://stackoverflow.com/questions/516032/how-to-make-qcombobox-painting-item-delegate-for-its-current-item-qt-4
> A bit lower-level, but helped me achieve something similar when I needed
> to go beyond style-sheeting..
>
> Best, Dave.
>
>
> > On 7 Apr 2016, at 21:58, Phillip Feldman <phillip.m.feldman at gmail.com>
> wrote:
> >
> > In Qt4 (or at least in PyQt4), it was possible to set a stylesheet for a
> QComboBox, but not possible to set styles on individual options/items in a
> QComboBox.  I was hoping that this would be addressed in Qt5, but it
> hasn't.  Worse yet, the API documentation does not mention a setStyleSheet
> method, so it looks as though the ability to set a stylesheet for the
> top-level widget has been removed.  I hope that I'm wrong on both of these
> points.
> >
> > Phillip
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://www.riverbankcomputing.com/pipermail/pyqt/attachments/20160407/e5bed104/attachment-0001.html>


More information about the PyQt mailing list