docs: def str2key(self, keyString):
          """
          Parse a human readable key sequence.
  
          If no error is raised, then ``keyString`` could be
          successfully converted into a valid key sequence and is
          henceforth represented by this object.
  
          |Args|
  
          * ``keyString`` (**QtmacsKeysequence**): eg. "<ctrl>+f"
  
          |Returns|
  
          **None**
  
          |Raises|
  
          * **QtmacsKeysequenceError** if ``keyString`` could not be parsed.
          """
  
          # Ensure the string is non-empty.
          if keyString == '':
              raise QtmacsKeysequenceError('Cannot parse empty string')
  
          tmp = str(keyString)
          tmp = tmp.replace('<', '&lt;')
          tmp = tmp.replace('>', '&gt;')
          keyStringHtml = '<b>{}</b>.'.format(tmp)
          del tmp
  
          # Remove leading and trailing white spaces, and reduce
          # sequences of white spaces to a single white space. If this
          # results in an emtpy string (typically the case when the user
          # tries to register a white space with ' ' instead of with
          # '<space>') then raise an error.
          rawKeyStr = keyString.strip()
          if len(rawKeyStr) == 0:
              msg = 'Cannot parse the key combination {}.'.format(keyStringHtml)
              raise QtmacsKeysequenceError(msg)
  
          # Split the string at these white spaces and convert eg.
          # " <ctrl>+x <ctrl>+f " first into
          # "<ctrl>+x <ctrl>+f" and from there into the list of
          # individual key combinations ["<ctrl>+x", "<ctrl>+f"].
          rawKeyStr = re.sub(' +', ' ', rawKeyStr)
          rawKeyStr = rawKeyStr.split(' ')
  
          # Now process the key combinations one by one. By definition.
          for key in rawKeyStr:
              # Find all bracketed keys in the key combination
              # (eg. <ctrl>, <space>).
              desc_keys = re.findall('<.*?>', key)
  
              # There are four possibilities:
              #   * no bracketed key (eg. "x" or "X")
              #   * one bracketed key (eg. "<ctrl>+x", or just "<space>")
              #   * two bracketed keys (eg. "<ctrl>+<space>" or "<ctrl>+<alt>+f")
              #   * three bracketed keys (eg. <ctrl>+<alt>+<space>).
              if len(desc_keys) == 0:
                  # No bracketed key means no modifier, so the key must
                  # stand by itself.
                  modStr = ['<NONE>']
                  keyStr = key
              elif len(desc_keys) == 1:
                  if '+' not in key:
                      # If no '+' sign is present then it must be
                      # bracketed key without any modifier
                      # (eg. "<space>").
                      modStr = ['<NONE>']
                      keyStr = key
                  else:
                      # Since a '+' sign and exactly one bracketed key
                      # is available, it must be a modifier plus a
                      # normal key (eg. "<ctrl>+f", "<alt>++").
                      idx = key.find('+')
                      modStr = [key[:idx]]
                      keyStr = key[idx + 1:]
              elif len(desc_keys) == 2:
                  # There are either two modifiers and a normal key
                  # (eg. "<ctrl>+<alt>+x") or one modifier and one
                  # bracketed key (eg. "<ctrl>+<space>").
                  if (key.count('+') == 0) or (key.count('+') > 3):
                      # A valid key combination must feature at least
                      # one- and at most three "+" symbols.
                      msg = 'Cannot parse the key combination {}.'
                      msg = msg.format(keyStringHtml)
                      raise QtmacsKeysequenceError(msg)
                  elif key.count('+') == 1:
                      # One modifier and one bracketed key
                      # (eg. "<ctrl>+<space>").
                      idx = key.find('+')
                      modStr = [key[:idx]]
                      keyStr = key[idx + 1:]
                  elif (key.count('+') == 2) or (key.count('+') == 3):
                      # Two modifiers and one normal key
                      # (eg. "<ctrl>+<alt>+f", "<ctrl>+<alt>++").
                      idx1 = key.find('+')
                      idx2 = key.find('+', idx1 + 1)
                      modStr = [key[:idx1], key[idx1 + 1:idx2]]
                      keyStr = key[idx2 + 1:]
              elif len(desc_keys) == 3:
                  if key.count('+') == 2:
                      # There are two modifiers and one bracketed key
                      # (eg. "<ctrl>+<alt>+<space>").
                      idx1 = key.find('+')
                      idx2 = key.find('+', idx1 + 1)
                      modStr = [key[:idx1], key[idx1 + 1:idx2]]
                      keyStr = key[idx2 + 1:]
                  else:
                      # A key combination with three bracketed entries
                      # must have exactly two '+' symbols. It cannot be
                      # valid otherwise.
                      msg = 'Cannot parse the key combination {}.'
                      msg = msg.format(keyStringHtml)
                      raise QtmacsKeysequenceError(msg)
              else:
                  msg = 'Cannot parse the key combination {}.'
                  msg = msg.format(keyStringHtml)
                  raise QtmacsKeysequenceError(msg)
  
              # The dictionary keys that map the modifiers and bracketed
              # keys to Qt constants are all upper case by
              # convention. Therefore, convert all modifier keys and
              # bracketed normal keys.
              modStr = [_.upper() for _ in modStr]
              if (keyStr[0] == '<') and (keyStr[-1] == '>'):
                  keyStr = keyStr.upper()
  
              # Convert the text version of the modifier key into the
              # QFlags structure used by Qt by "or"ing them
              # together. The loop is necessary because more than one
              # modifier may be active (eg. <ctrl>+<alt>).
              modQt = QtCore.Qt.NoModifier
              for mod in modStr:
                  # Ensure that the modifier actually exists (eg. the
                  # user might have made type like "<ctlr>" instead of
                  # "<ctrl>"). Also, the keys in the dictionary consist
                  # of only upper case letter for the modifier keys.
                  if mod not in self.modDict:
                      msg = 'Cannot parse the key combination {}.'
                      msg = msg.format(keyStringHtml)
                      raise QtmacsKeysequenceError(msg)
  
                  # Since the modifier exists in the dictionary, "or"
                  # them with the other flags.
                  modQt = modQt | self.modDict[mod]
  
              # Repeat the modifier procedure for the key. However,
              # unlike for the modifiers, no loop is necessary here
              # because only one key can be pressed at the same time.
              if keyStr in self.keyDict:
                  modQt_shift, keyQt = self.keyDict[keyStr]
              else:
                  msg = 'Cannot parse the key combination {}.'
                  msg = msg.format(keyStringHtml)
                  raise QtmacsKeysequenceError(msg)
  
              # Construct a new QKeyEvent. Note that the general
              # modifier (ie. <ctrl> and <alt>) still need to be
              # combined with shift modifier if the key demands it. This
              # combination is a simple "or" on the QFlags structure.
              # Also note that the "text" argument is omitted because Qt
              # is smart enough to determine it internally.
              key_event = QtGui.QKeyEvent(QtCore.QEvent.KeyPress, keyQt,
                                          modQt | modQt_shift)
  
              # Finally, append this key to the key sequence represented
              # by this object.
              self.appendQKeyEvent(key_event)