QCString — simple strings in PyQt

Both Python and Qt have two types of strings: simple strings, which are sequences of bytes where every byte represents one character, and complex string objects, which contain characters in the Unicode encoding. Unicode is a complex topic that is treated in the next section; this section deals with simple strings.

QCString is the PyQt equivalent of the Python simple string. The Qt documentation describes QCString as a weak class, which is accurate. The implementation does not feature all the intelligence and care that has gone into QString, and as a consequence it scales poorly to large strings.

As an abstraction of the standard C++ null-terminated string, QCString cannot contain any null bytes ('\0'). In this respect, QCString differs from the simple Python string object. The simple Python string is often used as a container for binary data, and the string object doesn't care whether it contains null bytes. Feeding a Python string that contains null bytes to a QCString provides interesting results:

Example 8-3. empty.py - feeding zero bytes to a QCString

#
# empty.py - feeding zero bytes to a QCString
#

from qt import *

pystring='abc\0def'
print "Python string:", pystring
print "Length:", len(pystring)

qcstring=QCString(pystring)
print "QCString:", qcstring
print "Length:", qcstring.length()
      

Running the previous example produces the following output:

boudewijn@maldar:~/doc/opendoc/ch4 > python empty.py
Python string: abcdef
Length: 7
QCString: abc
Length: 3
    

Except for this proviso, both QCString and the Python string object are equivalent, and you can use the Python string object wherever a QCString is needed as a parameter in a function. You can convert the QCString back to a python string with the str() function. If the QCString is empty, i.e., it contains only one byte with the value zero ('\0'), an empty Python string is returned, not a Python string that contains one zero byte.

The issue of null versus empty strings is an interesting one. A null QCString is constructed as follows:

nullstring=QCString()

This string is conceptually equivalent to the Python None object, except that the null QCString has a type. There is no way to construct a null Python string: a Python string without contents is always empty, i.e. the equivalent of a QCString that contains one byte with the value zero. The following script attempts a few combinations, using Python's built-in assert function.

Assert: The assert statement is one of the more useful tools in the Python developers toolchest. You can use assert to check any statement for truth — and if it fails, an AssertionException is thrown. If you compile your Python scripts to optimized bytecode (.pyo files), then the assertion statements are removed, making assert ideal for checking your code for invalid entry conditions in method calls during development. The use of assert in the following script is more of a hack: this little script wouldn't do anything if run with python -O null.py; only the line print message, "TRUE" would be executed in the assertTrue function.

Example 8-4. null.py - empty and null QCStrings and Python strings

#
# null.py - empty and null QCStrings and Python strings
#
from qt import QCString

# this string is empty
emptypystring=""

# this string contains one byte, zero
nullpystring="\0"

# this string is empty: it contains the empty string, terminated with \0
emptyqcstring=QCString("")

# this string is null: it doesn't contain data
nullqcstring=QCString()

def assertTrue(assertion, message):
    try:
        assert(assertion)
        print message, "TRUE"
    except AssertionError:
        print message, "FALSE"

assertTrue(emptypystring==emptyqcstring,
    "Empty Python string equals empty QCString")
assertTrue(emptypystring==str(emptyqcstring),
    "Empty Python string equals str(empty QCString)")
assertTrue(emptypystring==str(nullqcstring),
    "Empty python string equals str(null QCString)")
assertTrue(nullpystring==emptyqcstring,
    "Python string containing 0 byte equals empty QCString")
assertTrue(nullpystring==str(emptyqcstring),
    "Python string containing 0 byte equals str(empty QCSTRING)")
assertTrue(nullqcstring is None,
    "Null QCString equals None object")
      

Running this gives the following output:

boudewijn@maldar:~/doc/opendoc/ch4 > python null.py
Empty Python string equals empty QCString FALSE
Empty Python string equals str(empty QCString) TRUE
Empty python string equals str(null QCString) TRUE
Python string containing 0 byte equals empty QCString FALSE
Python string containing 0 byte equals str(empty QCSTRING) FALSE
Null QCString equals None object FALSE
    

Of course, some of these concerns hold for QString, too. It is equally possible to have an empty QString or a null QString. Note that embedding a zero byte in a Python string and then feeding it to a QString shows the same behavior as with QCString, even though QString isn't a null-terminated string class:

Example 8-5. emptyqstring.py - feeding zero bytes to a QString

#
# emptyqstring.py - feeding zero bytes to a QString
#

from qt import *

pystring='abc\0def'
print "Python string:", pystring
print "Length:", len(pystring)

qstring=QString(pystring)
print "QString:", qstring
print "Length:", qstring.length()
      

Look at the output:

boudewijn@maldar:~/doc/opendoc/ch4 > python emptyqstring.py
Python string: abcdef
Length: 7
QString: abc
Length: 3
    

The unavoidable conclusion is that you shouldn't try to use Python strings as containers for binary data and then convert them to Qt string objects. Of course, there's a solution: you can use QByteArray to store binary data.