From 046a29e1a88d557136a608b4e65f92ff3854d8ba Mon Sep 17 00:00:00 2001
From: Kashiko Koibumi <kashiko@tuta.io>
Date: Thu, 23 May 2024 13:28:40 +0900
Subject: [PATCH] stop using QString

---
 src/bitmessageqt/__init__.py         | 129 ++++++++++++++-------------
 src/bitmessageqt/account.py          |  18 ++--
 src/bitmessageqt/address_dialogs.py  |  31 +++----
 src/bitmessageqt/addressvalidator.py |   7 +-
 src/bitmessageqt/blacklist.py        |  33 +++----
 src/bitmessageqt/dialogs.py          |   7 +-
 src/bitmessageqt/foldertree.py       |  76 ++++++++--------
 src/bitmessageqt/messageview.py      |  15 ++--
 src/bitmessageqt/networkstatus.py    |   4 +-
 src/bitmessageqt/newchandialog.py    |  15 ++--
 src/bitmessageqt/retranslateui.py    |   7 +-
 src/bitmessageqt/safehtmlparser.py   |   6 +-
 src/bitmessageqt/settings.py         |  27 +++---
 src/bitmessageqt/settingsmixin.py    |   5 +-
 src/bitmessageqt/support.py          |  13 +--
 src/bitmessageqt/tests/main.py       |   2 +-
 src/bitmessageqt/tests/support.py    |   6 +-
 src/depends.py                       |  14 +++
 src/tr.py                            |   4 +-
 src/ver.py                           |  30 +++++++
 20 files changed, 252 insertions(+), 197 deletions(-)
 create mode 100644 src/ver.py

diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py
index 122a878c..a4a7bb60 100644
--- a/src/bitmessageqt/__init__.py
+++ b/src/bitmessageqt/__init__.py
@@ -15,6 +15,7 @@ import time
 from datetime import datetime, timedelta
 from sqlite3 import register_adapter
 
+from ver import ustr, unic
 from PyQt4 import QtCore, QtGui
 from PyQt4.QtNetwork import QLocalSocket, QLocalServer
 
@@ -120,7 +121,7 @@ class MyForm(settingsmixin.SMainWindow):
                 paths.codePath(), 'translations', 'qt_' + newlocale)
         else:
             translationpath = os.path.join(
-                str(QtCore.QLibraryInfo.location(
+                ustr(QtCore.QLibraryInfo.location(
                     QtCore.QLibraryInfo.TranslationsPath)), 'qt_' + newlocale)
         self.qsystranslator.load(translationpath)
         QtGui.QApplication.installTranslator(self.qsystranslator)
@@ -1186,11 +1187,11 @@ class MyForm(settingsmixin.SMainWindow):
 
         items = [
             MessageList_AddressWidget(
-                toAddress, unicode(acct.toLabel, 'utf-8')),
+                toAddress, unic(ustr(acct.toLabel))),
             MessageList_AddressWidget(
-                fromAddress, unicode(acct.fromLabel, 'utf-8')),
+                fromAddress, unic(ustr(acct.fromLabel))),
             MessageList_SubjectWidget(
-                str(subject), unicode(acct.subject, 'utf-8', 'replace')),
+                ustr(subject), unic(ustr(acct.subject))),
             MessageList_TimeWidget(
                 statusText, False, lastactiontime, ackdata)]
         self.addMessageListItem(tableWidget, items)
@@ -1211,11 +1212,11 @@ class MyForm(settingsmixin.SMainWindow):
 
         items = [
             MessageList_AddressWidget(
-                toAddress, unicode(acct.toLabel, 'utf-8'), not read),
+                toAddress, unic(ustr(acct.toLabel)), not read),
             MessageList_AddressWidget(
-                fromAddress, unicode(acct.fromLabel, 'utf-8'), not read),
+                fromAddress, unic(ustr(acct.fromLabel)), not read),
             MessageList_SubjectWidget(
-                str(subject), unicode(acct.subject, 'utf-8', 'replace'),
+                ustr(subject), unic(ustr(acct.subject)),
                 not read),
             MessageList_TimeWidget(
                 l10n.formatTimestamp(received), not read, received, msgid)
@@ -1496,7 +1497,7 @@ class MyForm(settingsmixin.SMainWindow):
             self, title, subtitle, category, label=None, icon=None):
         self.playSound(category, label)
         self._notifier(
-            unicode(title), unicode(subtitle), category, label, icon)
+            unic(ustr(title)), unic(ustr(subtitle)), category, label, icon)
 
     # tree
     def treeWidgetKeyPressEvent(self, event):
@@ -1699,7 +1700,7 @@ class MyForm(settingsmixin.SMainWindow):
                 addressVersionNumber, streamNumberForAddress,
                 "regenerated deterministic address",
                 dialog.spinBoxNumberOfAddressesToMake.value(),
-                dialog.lineEditPassphrase.text().toUtf8(),
+                ustr(dialog.lineEditPassphrase.text()),
                 dialog.checkBoxEighteenByteRipe.isChecked()
             ))
             self.ui.tabWidget.setCurrentIndex(
@@ -1995,9 +1996,9 @@ class MyForm(settingsmixin.SMainWindow):
     def rerenderAddressBook(self):
         def addRow (address, label, type):
             self.ui.tableWidgetAddressBook.insertRow(0)
-            newItem = Ui_AddressBookWidgetItemLabel(address, unicode(label, 'utf-8'), type)
+            newItem = Ui_AddressBookWidgetItemLabel(address, unic(ustr(label)), type)
             self.ui.tableWidgetAddressBook.setItem(0, 0, newItem)
-            newItem = Ui_AddressBookWidgetItemAddress(address, unicode(label, 'utf-8'), type)
+            newItem = Ui_AddressBookWidgetItemAddress(address, unic(ustr(label)), type)
             self.ui.tableWidgetAddressBook.setItem(0, 1, newItem)
 
         oldRows = {}
@@ -2038,7 +2039,7 @@ class MyForm(settingsmixin.SMainWindow):
                 self.ui.tableWidgetAddressBook.removeRow(oldRows[address][2])
         for address in newRows:
             addRow(address, newRows[address][0], newRows[address][1])
-            completerList.append(unicode(newRows[address][0], encoding="UTF-8") + " <" + address + ">")
+            completerList.append(unic(ustr(newRows[address][0]) + " <" + ustr(address) + ">"))
 
         # sort
         self.ui.tableWidgetAddressBook.sortByColumn(
@@ -2076,22 +2077,22 @@ class MyForm(settingsmixin.SMainWindow):
                 self.ui.tabWidgetSend.indexOf(self.ui.sendDirect):
             # message to specific people
             sendMessageToPeople = True
-            fromAddress = str(self.ui.comboBoxSendFrom.itemData(
+            fromAddress = ustr(self.ui.comboBoxSendFrom.itemData(
                 self.ui.comboBoxSendFrom.currentIndex(),
-                QtCore.Qt.UserRole).toString())
-            toAddresses = str(self.ui.lineEditTo.text().toUtf8())
-            subject = str(self.ui.lineEditSubject.text().toUtf8())
-            message = str(
-                self.ui.textEditMessage.document().toPlainText().toUtf8())
+                QtCore.Qt.UserRole))
+            toAddresses = ustr(self.ui.lineEditTo.text())
+            subject = ustr(self.ui.lineEditSubject.text())
+            message = ustr(
+                self.ui.textEditMessage.document().toPlainText())
         else:
             # broadcast message
             sendMessageToPeople = False
-            fromAddress = str(self.ui.comboBoxSendFromBroadcast.itemData(
+            fromAddress = ustr(self.ui.comboBoxSendFromBroadcast.itemData(
                 self.ui.comboBoxSendFromBroadcast.currentIndex(),
-                QtCore.Qt.UserRole).toString())
-            subject = str(self.ui.lineEditSubjectBroadcast.text().toUtf8())
-            message = str(
-                self.ui.textEditMessageBroadcast.document().toPlainText().toUtf8())
+                QtCore.Qt.UserRole))
+            subject = ustr(self.ui.lineEditSubjectBroadcast.text())
+            message = ustr(
+                self.ui.textEditMessageBroadcast.document().toPlainText())
         """
         The whole network message must fit in 2^18 bytes.
         Let's assume 500 bytes of overhead. If someone wants to get that
@@ -2164,7 +2165,7 @@ class MyForm(settingsmixin.SMainWindow):
                     status, addressVersionNumber, streamNumber = decodeAddress(toAddress)[:3]
                     if status != 'success':
                         try:
-                            toAddress = unicode(toAddress, 'utf-8', 'ignore')
+                            toAddress = unic(ustr(toAddress))
                         except:
                             pass
                         logger.error('Error: Could not decode recipient address ' + toAddress + ':' + status)
@@ -2338,7 +2339,7 @@ class MyForm(settingsmixin.SMainWindow):
             ))
 
     def click_pushButtonFetchNamecoinID(self):
-        identities = str(self.ui.lineEditTo.text().toUtf8()).split(";")
+        identities = ustr(self.ui.lineEditTo.text()).split(";")
         err, addr = self.namecoin.query(identities[-1].strip())
         if err is not None:
             self.updateStatusBar(
@@ -2355,7 +2356,7 @@ class MyForm(settingsmixin.SMainWindow):
         self.ui.tabWidgetSend.setCurrentIndex(
             self.ui.tabWidgetSend.indexOf(
                 self.ui.sendBroadcast
-                if config.safeGetBoolean(str(address), 'mailinglist')
+                if config.safeGetBoolean(ustr(address), 'mailinglist')
                 else self.ui.sendDirect
             ))
 
@@ -2368,14 +2369,14 @@ class MyForm(settingsmixin.SMainWindow):
                 addressInKeysFile, 'enabled')
             isMaillinglist = config.safeGetBoolean(addressInKeysFile, 'mailinglist')
             if isEnabled and not isMaillinglist:
-                label = unicode(config.get(addressInKeysFile, 'label'), 'utf-8', 'ignore').strip()
+                label = unic(ustr(config.get(addressInKeysFile, 'label')).strip())
                 if label == "":
                     label = addressInKeysFile
                 self.ui.comboBoxSendFrom.addItem(avatarize(addressInKeysFile), label, addressInKeysFile)
 #        self.ui.comboBoxSendFrom.model().sort(1, Qt.AscendingOrder)
         for i in range(self.ui.comboBoxSendFrom.count()):
-            address = str(self.ui.comboBoxSendFrom.itemData(
-                i, QtCore.Qt.UserRole).toString())
+            address = ustr(self.ui.comboBoxSendFrom.itemData(
+                i, QtCore.Qt.UserRole))
             self.ui.comboBoxSendFrom.setItemData(
                 i, AccountColor(address).accountColor(),
                 QtCore.Qt.ForegroundRole)
@@ -2392,13 +2393,13 @@ class MyForm(settingsmixin.SMainWindow):
                 addressInKeysFile, 'enabled')
             isChan = config.safeGetBoolean(addressInKeysFile, 'chan')
             if isEnabled and not isChan:
-                label = unicode(config.get(addressInKeysFile, 'label'), 'utf-8', 'ignore').strip()
+                label = unic(ustr(config.get(addressInKeysFile, 'label')).strip())
                 if label == "":
                     label = addressInKeysFile
                 self.ui.comboBoxSendFromBroadcast.addItem(avatarize(addressInKeysFile), label, addressInKeysFile)
         for i in range(self.ui.comboBoxSendFromBroadcast.count()):
-            address = str(self.ui.comboBoxSendFromBroadcast.itemData(
-                i, QtCore.Qt.UserRole).toString())
+            address = ustr(self.ui.comboBoxSendFromBroadcast.itemData(
+                i, QtCore.Qt.UserRole))
             self.ui.comboBoxSendFromBroadcast.setItemData(
                 i, AccountColor(address).accountColor(),
                 QtCore.Qt.ForegroundRole)
@@ -2498,7 +2499,7 @@ class MyForm(settingsmixin.SMainWindow):
             self.notifierShow(
                 _translate("MainWindow", "New Message"),
                 _translate("MainWindow", "From {0}").format(
-                    unicode(acct.fromLabel, 'utf-8')),
+                    unic(ustr(acct.fromLabel))),
                 sound.SOUND_UNKNOWN
             )
         if self.getCurrentAccount() is not None and (
@@ -2636,7 +2637,7 @@ class MyForm(settingsmixin.SMainWindow):
         # Only settings remain here
         acct.settings()
         for i in range(self.ui.comboBoxSendFrom.count()):
-            if str(self.ui.comboBoxSendFrom.itemData(i).toPyObject()) \
+            if ustr(self.ui.comboBoxSendFrom.itemData(i)) \
                     == acct.fromAddress:
                 self.ui.comboBoxSendFrom.setCurrentIndex(i)
                 break
@@ -2703,7 +2704,7 @@ class MyForm(settingsmixin.SMainWindow):
         if reply != QtGui.QMessageBox.Yes:
             return
         config.set(
-            'bitmessagesettings', 'dontconnect', str(dontconnect_option))
+            'bitmessagesettings', 'dontconnect', ustr(dontconnect_option))
         config.save()
         self.ui.updateNetworkSwitchMenuLabel(dontconnect_option)
 
@@ -2934,8 +2935,8 @@ class MyForm(settingsmixin.SMainWindow):
                 lines[i] = '<br><br>'
         content = ' '.join(lines) # To keep the whitespace between lines
         content = shared.fixPotentiallyInvalidUTF8Data(content)
-        content = unicode(content, 'utf-8)')
-        textEdit.setHtml(QtCore.QString(content))
+        content = unic(ustr(content))
+        textEdit.setHtml(content)
 
     def on_action_InboxMarkUnread(self):
         tableWidget = self.getCurrentMessagelist()
@@ -3002,7 +3003,7 @@ class MyForm(settingsmixin.SMainWindow):
             self.ui.comboBoxSendFrom, self.ui.comboBoxSendFromBroadcast
         ):
             for i in range(box.count()):
-                if str(box.itemData(i).toPyObject()) == address:
+                if ustr(box.itemData(i)) == ustr(address):
                     box.setCurrentIndex(i)
                     break
             else:
@@ -3093,7 +3094,7 @@ class MyForm(settingsmixin.SMainWindow):
             tableWidget.item(currentInboxRow, column_from).label or (
                 isinstance(acct, GatewayAccount) and
                 fromAddressAtCurrentInboxRow == acct.relayAddress):
-            self.ui.lineEditTo.setText(str(acct.fromAddress))
+            self.ui.lineEditTo.setText(ustr(acct.fromAddress))
         else:
             self.ui.lineEditTo.setText(
                 tableWidget.item(currentInboxRow, column_from).accountString()
@@ -3108,7 +3109,7 @@ class MyForm(settingsmixin.SMainWindow):
                 ' reply to the chan address.')
             if toAddressAtCurrentInboxRow == \
                     tableWidget.item(currentInboxRow, column_to).label:
-                self.ui.lineEditTo.setText(str(toAddressAtCurrentInboxRow))
+                self.ui.lineEditTo.setText(ustr(toAddressAtCurrentInboxRow))
             else:
                 self.ui.lineEditTo.setText(
                     tableWidget.item(currentInboxRow, column_to).accountString()
@@ -3117,7 +3118,7 @@ class MyForm(settingsmixin.SMainWindow):
         self.setSendFromComboBox(toAddressAtCurrentInboxRow)
 
         quotedText = self.quoted_text(
-            unicode(messageAtCurrentInboxRow, 'utf-8', 'replace'))
+            unic(ustr(messageAtCurrentInboxRow)))
         widget['message'].setPlainText(quotedText)
         if acct.subject[0:3] in ('Re:', 'RE:'):
             widget['subject'].setText(
@@ -3262,7 +3263,7 @@ class MyForm(settingsmixin.SMainWindow):
             return
         currentInboxRow = tableWidget.currentRow()
         try:
-            subjectAtCurrentInboxRow = str(tableWidget.item(
+            subjectAtCurrentInboxRow = ustr(tableWidget.item(
                 currentInboxRow, 2).data(QtCore.Qt.UserRole))
         except:
             subjectAtCurrentInboxRow = ''
@@ -3334,7 +3335,7 @@ class MyForm(settingsmixin.SMainWindow):
         addressAtCurrentRow = self.ui.tableWidgetInbox.item(
             currentRow, 0).data(QtCore.Qt.UserRole)
         clipboard = QtGui.QApplication.clipboard()
-        clipboard.setText(str(addressAtCurrentRow))
+        clipboard.setText(ustr(addressAtCurrentRow))
 
     # Group of functions for the Address Book dialog box
     def on_action_AddressBookNew(self):
@@ -3368,8 +3369,8 @@ class MyForm(settingsmixin.SMainWindow):
             return self.updateStatusBar(_translate(
                 "MainWindow", "No addresses selected."))
 
-        addresses_string = unicode(
-            self.ui.lineEditTo.text().toUtf8(), 'utf-8')
+        addresses_string = unic(ustr(
+            self.ui.lineEditTo.text()))
         for item in selected_items:
             address_string = item.accountString()
             if not addresses_string:
@@ -3455,7 +3456,7 @@ class MyForm(settingsmixin.SMainWindow):
     def on_action_SubscriptionsClipboard(self):
         address = self.getCurrentAccount()
         clipboard = QtGui.QApplication.clipboard()
-        clipboard.setText(str(address))
+        clipboard.setText(ustr(address))
 
     def on_action_SubscriptionsEnable(self):
         address = self.getCurrentAccount()
@@ -3607,9 +3608,9 @@ class MyForm(settingsmixin.SMainWindow):
             self.ui.inboxSearchLineEditChans,
         )
         if currentIndex >= 0 and currentIndex < len(messagelistList):
-            return (
+            return ustr(
                 messagelistList[currentIndex] if retObj
-                else messagelistList[currentIndex].text().toUtf8().data())
+                else ustr(messagelistList[currentIndex].text()))
 
     def getCurrentSearchOption(self, currentIndex=None):
         if currentIndex is None:
@@ -3678,7 +3679,7 @@ class MyForm(settingsmixin.SMainWindow):
                         " delete the channel?"
                     ), QtGui.QMessageBox.Yes | QtGui.QMessageBox.No
             ) == QtGui.QMessageBox.Yes:
-                config.remove_section(str(account.address))
+                config.remove_section(ustr(account.address))
             else:
                 return
         else:
@@ -3711,7 +3712,7 @@ class MyForm(settingsmixin.SMainWindow):
         account.setEnabled(False)
 
     def disableIdentity(self, address):
-        config.set(str(address), 'enabled', 'false')
+        config.set(ustr(address), 'enabled', 'false')
         config.save()
         shared.reloadMyAddressHashes()
         self.rerenderAddressBook()
@@ -3719,7 +3720,7 @@ class MyForm(settingsmixin.SMainWindow):
     def on_action_Clipboard(self):
         address = self.getCurrentAccount()
         clipboard = QtGui.QApplication.clipboard()
-        clipboard.setText(str(address))
+        clipboard.setText(ustr(address))
 
     def on_action_ClipboardMessagelist(self):
         tableWidget = self.getCurrentMessagelist()
@@ -3739,7 +3740,7 @@ class MyForm(settingsmixin.SMainWindow):
         if isinstance(account, GatewayAccount) and otherAddress == account.relayAddress and (
                 (currentColumn in [0, 2] and self.getCurrentFolder() == "sent") or
                 (currentColumn in [1, 2] and self.getCurrentFolder() != "sent")):
-            text = str(tableWidget.item(currentRow, currentColumn).label)
+            text = ustr(tableWidget.item(currentRow, currentColumn).label)
         else:
             text = tableWidget.item(currentRow, currentColumn).data(QtCore.Qt.UserRole)
 
@@ -3756,8 +3757,8 @@ class MyForm(settingsmixin.SMainWindow):
 
     def on_action_SetAvatar(self, thisTableWidget):
         currentRow = thisTableWidget.currentRow()
-        addressAtCurrentRow = thisTableWidget.item(
-            currentRow, 1).text()
+        addressAtCurrentRow = ustr(thisTableWidget.item(
+            currentRow, 1).text())
         setToIdenticon = not self.setAvatar(addressAtCurrentRow)
         if setToIdenticon:
             thisTableWidget.item(
@@ -3858,23 +3859,23 @@ class MyForm(settingsmixin.SMainWindow):
 
     def on_action_AddressBookSetSound(self):
         widget = self.ui.tableWidgetAddressBook
-        self.setAddressSound(widget.item(widget.currentRow(), 0).text())
+        self.setAddressSound(ustr(widget.item(widget.currentRow(), 0).text()))
 
     def setAddressSound(self, addr):
-        filters = [unicode(_translate(
+        filters = [unic(_translate(
             "MainWindow", "Sound files (%s)" %
             ' '.join(['*%s%s' % (os.extsep, ext) for ext in sound.extensions])
         ))]
-        sourcefile = unicode(QtGui.QFileDialog.getOpenFileName(
+        sourcefile = unic(ustr(QtGui.QFileDialog.getOpenFileName(
             self, _translate("MainWindow", "Set notification sound..."),
             filter=';;'.join(filters)
-        ))
+        )))
 
         if not sourcefile:
             return
 
         destdir = os.path.join(state.appdata, 'sounds')
-        destfile = unicode(addr) + os.path.splitext(sourcefile)[-1]
+        destfile = unic(ustr(addr) + os.path.splitext(sourcefile)[-1])
         destination = os.path.join(destdir, destfile)
 
         if sourcefile == destination:
@@ -4027,7 +4028,7 @@ class MyForm(settingsmixin.SMainWindow):
 
     def inboxSearchLineEditUpdated(self, text):
         # dynamic search for too short text is slow
-        text = text.toUtf8()
+        text = ustr(text)
         if 0 < len(text) < 3:
             return
         messagelist = self.getCurrentMessagelist()
@@ -4042,7 +4043,7 @@ class MyForm(settingsmixin.SMainWindow):
         logger.debug("Search return pressed")
         searchLine = self.getCurrentSearchLine()
         messagelist = self.getCurrentMessagelist()
-        if messagelist and len(str(searchLine)) < 3:
+        if messagelist and len(ustr(searchLine)) < 3:
             searchOption = self.getCurrentSearchOption()
             account = self.getCurrentAccount()
             folder = self.getCurrentFolder()
@@ -4084,7 +4085,7 @@ class MyForm(settingsmixin.SMainWindow):
         if item.type == AccountMixin.ALL:
             return
 
-        newLabel = unicode(item.text(0), 'utf-8', 'ignore')
+        newLabel = unic(ustr(item.text(0)))
         oldLabel = item.defaultLabel()
 
         # unchanged, do not do anything either
@@ -4155,8 +4156,8 @@ class MyForm(settingsmixin.SMainWindow):
         self.rerenderMessagelistToLabels()
         completerList = self.ui.lineEditTo.completer().model().stringList()
         for i in range(len(completerList)):
-            if unicode(completerList[i]).endswith(" <" + item.address + ">"):
-                completerList[i] = item.label + " <" + item.address + ">"
+            if unic(ustr(completerList[i])).endswith(" <" + ustr(item.address) + ">"):
+                completerList[i] = ustr(item.label) + " <" + ustr(item.address) + ">"
         self.ui.lineEditTo.completer().model().setStringList(completerList)
 
     def tabWidgetCurrentChanged(self, n):
diff --git a/src/bitmessageqt/account.py b/src/bitmessageqt/account.py
index 8c82c6f6..eefb3a7e 100644
--- a/src/bitmessageqt/account.py
+++ b/src/bitmessageqt/account.py
@@ -14,6 +14,7 @@ import re
 import sys
 import time
 
+from ver import ustr
 from PyQt4 import QtGui
 
 import queues
@@ -131,6 +132,8 @@ class BMAccount(object):
         """Get a label for this bitmessage account"""
         if address is None:
             address = self.address
+        else:
+            address = ustr(address)
         label = config.safeGet(address, 'label', address)
         queryreturn = sqlQuery(
             '''select label from addressbook where address=?''', address)
@@ -148,15 +151,12 @@ class BMAccount(object):
     def parseMessage(self, toAddress, fromAddress, subject, message):
         """Set metadata and address labels on self"""
 
-        self.toAddress = toAddress
-        self.fromAddress = fromAddress
-        if isinstance(subject, unicode):
-            self.subject = str(subject)
-        else:
-            self.subject = subject
-        self.message = message
-        self.fromLabel = self.getLabel(fromAddress)
-        self.toLabel = self.getLabel(toAddress)
+        self.toAddress = ustr(toAddress)
+        self.fromAddress = ustr(fromAddress)
+        self.subject = ustr(subject)
+        self.message = ustr(message)
+        self.fromLabel = ustr(self.getLabel(fromAddress))
+        self.toLabel = ustr(self.getLabel(toAddress))
 
 
 class NoAccount(BMAccount):
diff --git a/src/bitmessageqt/address_dialogs.py b/src/bitmessageqt/address_dialogs.py
index bf571041..caf7f687 100644
--- a/src/bitmessageqt/address_dialogs.py
+++ b/src/bitmessageqt/address_dialogs.py
@@ -5,6 +5,7 @@ Dialogs that work with BM address.
 
 import hashlib
 
+from ver import ustr, unic
 from PyQt4 import QtCore, QtGui
 
 import queues
@@ -29,12 +30,12 @@ class AddressCheckMixin(object):
     def _onSuccess(self, addressVersion, streamNumber, ripe):
         pass
 
-    def addressChanged(self, QString):
+    def addressChanged(self, addr):
         """
         Address validation callback, performs validation and gives feedback
         """
         status, addressVersion, streamNumber, ripe = decodeAddress(
-            str(QString))
+            ustr(addr))
         self.valid = status == 'success'
         if self.valid:
             self.labelAddressCheck.setText(
@@ -90,8 +91,8 @@ class AddressDataDialog(QtGui.QDialog, AddressCheckMixin):
         """Callback for QDIalog accepting value"""
         if self.valid:
             self.data = (
-                addBMIfNotPresent(str(self.lineEditAddress.text())),
-                str(self.lineEditLabel.text().toUtf8())
+                addBMIfNotPresent(ustr(self.lineEditAddress.text())),
+                ustr(self.lineEditLabel.text())
             )
         else:
             queues.UISignalQueue.put(('updateStatusBar', _translate(
@@ -142,12 +143,12 @@ class NewAddressDialog(QtGui.QDialog):
                     self.comboBoxExisting.currentText())[2]
             queues.addressGeneratorQueue.put((
                 'createRandomAddress', 4, streamNumberForAddress,
-                str(self.newaddresslabel.text().toUtf8()), 1, "",
+                ustr(self.newaddresslabel.text()), 1, "",
                 self.checkBoxEighteenByteRipe.isChecked()
             ))
         else:
-            if self.lineEditPassphrase.text() != \
-                    self.lineEditPassphraseAgain.text():
+            if ustr(self.lineEditPassphrase.text()) != \
+                    ustr(self.lineEditPassphraseAgain.text()):
                 QtGui.QMessageBox.about(
                     self, _translate("MainWindow", "Passphrase mismatch"),
                     _translate(
@@ -169,7 +170,7 @@ class NewAddressDialog(QtGui.QDialog):
                     'createDeterministicAddresses', 4, streamNumberForAddress,
                     "unused deterministic address",
                     self.spinBoxNumberOfAddressesToMake.value(),
-                    self.lineEditPassphrase.text().toUtf8(),
+                    ustr(self.lineEditPassphrase.text()),
                     self.checkBoxEighteenByteRipe.isChecked()
                 ))
 
@@ -234,7 +235,7 @@ class SpecialAddressBehaviorDialog(QtGui.QDialog):
     def __init__(self, parent=None, config=global_config):
         super(SpecialAddressBehaviorDialog, self).__init__(parent)
         widgets.load('specialaddressbehavior.ui', self)
-        self.address = parent.getCurrentAccount()
+        self.address = ustr(parent.getCurrentAccount())
         self.parent = parent
         self.config = config
 
@@ -259,7 +260,7 @@ class SpecialAddressBehaviorDialog(QtGui.QDialog):
                     self.radioButtonBehaveNormalAddress.click()
                 mailingListName = config.safeGet(self.address, 'mailinglistname', '')
                 self.lineEditMailingListName.setText(
-                    unicode(mailingListName, 'utf-8')
+                    unic(ustr(mailingListName))
                 )
 
         QtGui.QWidget.resize(self, QtGui.QWidget.sizeHint(self))
@@ -271,7 +272,7 @@ class SpecialAddressBehaviorDialog(QtGui.QDialog):
         if self.address_is_chan:
             return
         if self.radioButtonBehaveNormalAddress.isChecked():
-            self.config.set(str(self.address), 'mailinglist', 'false')
+            self.config.set(self.address, 'mailinglist', 'false')
             # Set the color to either black or grey
             if self.config.getboolean(self.address, 'enabled'):
                 self.parent.setCurrentItemColor(
@@ -280,9 +281,9 @@ class SpecialAddressBehaviorDialog(QtGui.QDialog):
             else:
                 self.parent.setCurrentItemColor(QtGui.QColor(128, 128, 128))
         else:
-            self.config.set(str(self.address), 'mailinglist', 'true')
-            self.config.set(str(self.address), 'mailinglistname', str(
-                self.lineEditMailingListName.text().toUtf8()))
+            self.config.set(self.address, 'mailinglist', 'true')
+            self.config.set(self.address, 'mailinglistname', ustr(
+                self.lineEditMailingListName.text()))
             self.parent.setCurrentItemColor(
                 QtGui.QColor(137, 4, 177))  # magenta
         self.parent.rerenderComboBoxSendFrom()
@@ -344,7 +345,7 @@ class EmailGatewayDialog(QtGui.QDialog):
 
         if self.radioButtonRegister.isChecked() \
                 or self.radioButtonRegister.isHidden():
-            email = str(self.lineEditEmail.text().toUtf8())
+            email = ustr(self.lineEditEmail.text())
             self.acct.register(email)
             self.config.set(self.acct.fromAddress, 'label', email)
             self.config.set(self.acct.fromAddress, 'gateway', 'mailchuck')
diff --git a/src/bitmessageqt/addressvalidator.py b/src/bitmessageqt/addressvalidator.py
index dc61b41c..fe939266 100644
--- a/src/bitmessageqt/addressvalidator.py
+++ b/src/bitmessageqt/addressvalidator.py
@@ -5,6 +5,7 @@ Address validator module.
 
 from Queue import Empty
 
+from ver import ustr
 from PyQt4 import QtGui
 
 from addresses import decodeAddress, addBMIfNotPresent
@@ -108,13 +109,13 @@ class AddressPassPhraseValidatorMixin(object):
         if self.addressObject is None:
             address = None
         else:
-            address = str(self.addressObject.text().toUtf8())
+            address = ustr(self.addressObject.text())
             if address == "":
                 address = None
         if self.passPhraseObject is None:
             passPhrase = ""
         else:
-            passPhrase = str(self.passPhraseObject.text().toUtf8())
+            passPhrase = ustr(self.passPhraseObject.text())
             if passPhrase == "":
                 passPhrase = None
 
@@ -152,7 +153,7 @@ class AddressPassPhraseValidatorMixin(object):
 
         # check through generator
         if address is None:
-            addressGeneratorQueue.put(('createChan', 4, 1, str_chan + ' ' + str(passPhrase), passPhrase, False))
+            addressGeneratorQueue.put(('createChan', 4, 1, str_chan + ' ' + ustr(passPhrase), passPhrase, False))
         else:
             addressGeneratorQueue.put(
                 ('joinChan', addBMIfNotPresent(address),
diff --git a/src/bitmessageqt/blacklist.py b/src/bitmessageqt/blacklist.py
index 093f23d8..5db3152f 100644
--- a/src/bitmessageqt/blacklist.py
+++ b/src/bitmessageqt/blacklist.py
@@ -1,3 +1,4 @@
+from ver import ustr, unic
 from PyQt4 import QtCore, QtGui
 
 import widgets
@@ -59,7 +60,7 @@ class Blacklist(QtGui.QWidget, RetranslateMixin):
         if self.NewBlacklistDialogInstance.exec_():
             if self.NewBlacklistDialogInstance.labelAddressCheck.text() == \
                     _translate("MainWindow", "Address is valid."):
-                address = addBMIfNotPresent(str(
+                address = addBMIfNotPresent(ustr(
                     self.NewBlacklistDialogInstance.lineEditAddress.text()))
                 # First we must check to see if the address is already in the
                 # address book. The user cannot add it again or else it will
@@ -73,8 +74,8 @@ class Blacklist(QtGui.QWidget, RetranslateMixin):
                 if queryreturn == []:
                     self.tableWidgetBlacklist.setSortingEnabled(False)
                     self.tableWidgetBlacklist.insertRow(0)
-                    newItem = QtGui.QTableWidgetItem(unicode(
-                        self.NewBlacklistDialogInstance.lineEditLabel.text().toUtf8(), 'utf-8'))
+                    newItem = QtGui.QTableWidgetItem(unic(ustr(
+                        self.NewBlacklistDialogInstance.lineEditLabel.text())))
                     newItem.setIcon(avatarize(address))
                     self.tableWidgetBlacklist.setItem(0, 0, newItem)
                     newItem = QtGui.QTableWidgetItem(address)
@@ -82,7 +83,7 @@ class Blacklist(QtGui.QWidget, RetranslateMixin):
                         QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
                     self.tableWidgetBlacklist.setItem(0, 1, newItem)
                     self.tableWidgetBlacklist.setSortingEnabled(True)
-                    t = (str(self.NewBlacklistDialogInstance.lineEditLabel.text().toUtf8()), address, True)
+                    t = (ustr(self.NewBlacklistDialogInstance.lineEditLabel.text()), address, True)
                     if config.get('bitmessagesettings', 'blackwhitelist') == 'black':
                         sql = '''INSERT INTO blacklist VALUES (?,?,?)'''
                     else:
@@ -111,10 +112,10 @@ class Blacklist(QtGui.QWidget, RetranslateMixin):
             if isinstance(addressitem, QtGui.QTableWidgetItem):
                 if self.radioButtonBlacklist.isChecked():
                     sqlExecute('''UPDATE blacklist SET label=? WHERE address=?''',
-                            str(item.text()), str(addressitem.text()))
+                            ustr(item.text()), ustr(addressitem.text()))
                 else:
                     sqlExecute('''UPDATE whitelist SET label=? WHERE address=?''',
-                            str(item.text()), str(addressitem.text()))
+                            ustr(item.text()), ustr(addressitem.text()))
 
     def init_blacklist_popup_menu(self, connectSignal=True):
         # Popup menu for the Blacklist page
@@ -172,7 +173,7 @@ class Blacklist(QtGui.QWidget, RetranslateMixin):
         for row in queryreturn:
             label, address, enabled = row
             self.tableWidgetBlacklist.insertRow(0)
-            newItem = QtGui.QTableWidgetItem(unicode(label, 'utf-8'))
+            newItem = QtGui.QTableWidgetItem(unic(ustr(label)))
             if not enabled:
                 newItem.setTextColor(QtGui.QColor(128, 128, 128))
             newItem.setIcon(avatarize(address))
@@ -191,18 +192,18 @@ class Blacklist(QtGui.QWidget, RetranslateMixin):
 
     def on_action_BlacklistDelete(self):
         currentRow = self.tableWidgetBlacklist.currentRow()
-        labelAtCurrentRow = self.tableWidgetBlacklist.item(
-            currentRow, 0).text().toUtf8()
+        labelAtCurrentRow = ustr(self.tableWidgetBlacklist.item(
+            currentRow, 0).text())
         addressAtCurrentRow = self.tableWidgetBlacklist.item(
             currentRow, 1).text()
         if config.get('bitmessagesettings', 'blackwhitelist') == 'black':
             sqlExecute(
                 '''DELETE FROM blacklist WHERE label=? AND address=?''',
-                str(labelAtCurrentRow), str(addressAtCurrentRow))
+                ustr(labelAtCurrentRow), ustr(addressAtCurrentRow))
         else:
             sqlExecute(
                 '''DELETE FROM whitelist WHERE label=? AND address=?''',
-                str(labelAtCurrentRow), str(addressAtCurrentRow))
+                ustr(labelAtCurrentRow), ustr(addressAtCurrentRow))
         self.tableWidgetBlacklist.removeRow(currentRow)
 
     def on_action_BlacklistClipboard(self):
@@ -210,7 +211,7 @@ class Blacklist(QtGui.QWidget, RetranslateMixin):
         addressAtCurrentRow = self.tableWidgetBlacklist.item(
             currentRow, 1).text()
         clipboard = QtGui.QApplication.clipboard()
-        clipboard.setText(str(addressAtCurrentRow))
+        clipboard.setText(ustr(addressAtCurrentRow))
 
     def on_context_menuBlacklist(self, point):
         self.popMenuBlacklist.exec_(
@@ -227,11 +228,11 @@ class Blacklist(QtGui.QWidget, RetranslateMixin):
         if config.get('bitmessagesettings', 'blackwhitelist') == 'black':
             sqlExecute(
                 '''UPDATE blacklist SET enabled=1 WHERE address=?''',
-                str(addressAtCurrentRow))
+                ustr(addressAtCurrentRow))
         else:
             sqlExecute(
                 '''UPDATE whitelist SET enabled=1 WHERE address=?''',
-                str(addressAtCurrentRow))
+                ustr(addressAtCurrentRow))
 
     def on_action_BlacklistDisable(self):
         currentRow = self.tableWidgetBlacklist.currentRow()
@@ -243,10 +244,10 @@ class Blacklist(QtGui.QWidget, RetranslateMixin):
             currentRow, 1).setTextColor(QtGui.QColor(128, 128, 128))
         if config.get('bitmessagesettings', 'blackwhitelist') == 'black':
             sqlExecute(
-                '''UPDATE blacklist SET enabled=0 WHERE address=?''', str(addressAtCurrentRow))
+                '''UPDATE blacklist SET enabled=0 WHERE address=?''', ustr(addressAtCurrentRow))
         else:
             sqlExecute(
-                '''UPDATE whitelist SET enabled=0 WHERE address=?''', str(addressAtCurrentRow))
+                '''UPDATE whitelist SET enabled=0 WHERE address=?''', ustr(addressAtCurrentRow))
 
     def on_action_BlacklistSetAvatar(self):
         self.window().on_action_SetAvatar(self.tableWidgetBlacklist)
diff --git a/src/bitmessageqt/dialogs.py b/src/bitmessageqt/dialogs.py
index bba7421e..c8969dae 100644
--- a/src/bitmessageqt/dialogs.py
+++ b/src/bitmessageqt/dialogs.py
@@ -2,6 +2,7 @@
 Custom dialog classes
 """
 # pylint: disable=too-few-public-methods
+from ver import ustr
 from PyQt4 import QtGui
 
 import paths
@@ -36,7 +37,7 @@ class AboutDialog(QtGui.QDialog):
         if commit:
             version += '-' + commit[:7]
         self.labelVersion.setText(
-            self.labelVersion.text().replace(
+            ustr(self.labelVersion.text()).replace(
                 ':version:', version
             ).replace(':branch:', commit or 'v%s' % version)
         )
@@ -44,8 +45,8 @@ class AboutDialog(QtGui.QDialog):
 
         try:
             self.label_2.setText(
-                self.label_2.text().replace(
-                    '2022', str(last_commit.get('time').year)
+                ustr(self.label_2.text()).replace(
+                    '2022', ustr(last_commit.get('time').year)
                 ))
         except AttributeError:
             pass
diff --git a/src/bitmessageqt/foldertree.py b/src/bitmessageqt/foldertree.py
index c50b7d3d..437fe314 100644
--- a/src/bitmessageqt/foldertree.py
+++ b/src/bitmessageqt/foldertree.py
@@ -6,6 +6,7 @@ Folder tree and messagelist widgets definitions.
 
 from cgi import escape
 
+from ver import ustr, unic
 from PyQt4 import QtCore, QtGui
 
 from bmconfigparser import config
@@ -62,7 +63,7 @@ class AccountMixin(object):
 
     def accountString(self):
         """Account string suitable for use in To: field: label <address>"""
-        label = self._getLabel()
+        label = ustr(self._getLabel())
         return (
             self.address if label == self.address
             else '%s <%s>' % (label, self.address)
@@ -73,7 +74,7 @@ class AccountMixin(object):
         if address is None:
             self.address = None
         else:
-            self.address = str(address)
+            self.address = ustr(address)
 
     def setUnreadCount(self, cnt):
         """Set number of unread messages"""
@@ -124,8 +125,8 @@ class AccountMixin(object):
                 AccountMixin.NORMAL,
                 AccountMixin.CHAN, AccountMixin.MAILINGLIST):
             try:
-                retval = unicode(
-                    config.get(self.address, 'label'), 'utf-8')
+                retval = unic(ustr(
+                    config.get(self.address, 'label')))
             except Exception:
                 queryreturn = sqlQuery(
                     '''select label from addressbook where address=?''', self.address)
@@ -136,12 +137,11 @@ class AccountMixin(object):
             if queryreturn != []:
                 for row in queryreturn:
                     retval, = row
-                    retval = unicode(retval, 'utf-8')
+                    retval = unic(ustr(retval))
         elif self.address is None or self.type == AccountMixin.ALL:
-            return unicode(
-                str(_translate("MainWindow", "All accounts")), 'utf-8')
+            return unic(_translate("MainWindow", "All accounts"))
 
-        return retval or unicode(self.address, 'utf-8')
+        return retval or unic(self.address)
 
 
 class BMTreeWidgetItem(QtGui.QTreeWidgetItem, AccountMixin):
@@ -154,7 +154,7 @@ class BMTreeWidgetItem(QtGui.QTreeWidgetItem, AccountMixin):
         self._setup(parent, pos)
 
     def _getAddressBracket(self, unreadCount=False):
-        return " (" + str(self.unreadCount) + ")" if unreadCount else ""
+        return " (" + ustr(self.unreadCount) + ")" if unreadCount else ""
 
     def data(self, column, role):
         """Override internal QT method for returning object data"""
@@ -191,7 +191,7 @@ class Ui_FolderWidget(BMTreeWidgetItem):
 
     def setFolderName(self, fname):
         """Set folder name (for QT UI)"""
-        self.folderName = str(fname)
+        self.folderName = ustr(fname)
 
     def data(self, column, role):
         """Override internal QT method for returning object data"""
@@ -232,15 +232,14 @@ class Ui_AddressWidget(BMTreeWidgetItem, SettingsMixin):
 
     def _getLabel(self):
         if self.address is None:
-            return unicode(_translate(
-                "MainWindow", "All accounts").toUtf8(), 'utf-8', 'ignore')
+            return unic(_translate(
+                "MainWindow", "All accounts"))
         else:
             try:
-                return unicode(
-                    config.get(self.address, 'label'),
-                    'utf-8', 'ignore')
+                return unic(ustr(
+                    config.get(self.address, 'label')))
             except:
-                return unicode(self.address, 'utf-8')
+                return unic(self.address)
 
     def _getAddressBracket(self, unreadCount=False):
         ret = "" if self.isExpanded() \
@@ -264,8 +263,8 @@ class Ui_AddressWidget(BMTreeWidgetItem, SettingsMixin):
         if role == QtCore.Qt.EditRole \
                 and self.type != AccountMixin.SUBSCRIPTION:
             config.set(
-                str(self.address), 'label',
-                str(value.toString().toUtf8())
+                self.address, 'label',
+                ustr(value)
                 if isinstance(value, QtCore.QVariant)
                 else value.encode('utf-8')
             )
@@ -311,8 +310,8 @@ class Ui_SubscriptionWidget(Ui_AddressWidget):
         if queryreturn != []:
             for row in queryreturn:
                 retval, = row
-            return unicode(retval, 'utf-8', 'ignore')
-        return unicode(self.address, 'utf-8')
+            return unic(ustr(retval))
+        return unic(self.address)
 
     def setType(self):
         """Set account type"""
@@ -323,10 +322,10 @@ class Ui_SubscriptionWidget(Ui_AddressWidget):
         """Save subscription label to database"""
         if role == QtCore.Qt.EditRole:
             if isinstance(value, QtCore.QVariant):
-                label = str(
-                    value.toString().toUtf8()).decode('utf-8', 'ignore')
+                label = ustr(
+                    value)
             else:
-                label = unicode(value, 'utf-8', 'ignore')
+                label = unic(ustr(value))
             sqlExecute(
                 '''UPDATE subscriptions SET label=? WHERE address=?''',
                 label, self.address)
@@ -407,9 +406,8 @@ class MessageList_AddressWidget(BMAddressWidget):
                 AccountMixin.NORMAL,
                 AccountMixin.CHAN, AccountMixin.MAILINGLIST):
             try:
-                newLabel = unicode(
-                    config.get(self.address, 'label'),
-                    'utf-8', 'ignore')
+                newLabel = unic(ustr(
+                    config.get(self.address, 'label')))
             except:
                 queryreturn = sqlQuery(
                     '''select label from addressbook where address=?''', self.address)
@@ -418,7 +416,7 @@ class MessageList_AddressWidget(BMAddressWidget):
                 '''select label from subscriptions where address=?''', self.address)
         if queryreturn:
             for row in queryreturn:
-                newLabel = unicode(row[0], 'utf-8', 'ignore')
+                newLabel = unic(ustr(row[0]))
 
         self.label = newLabel
 
@@ -454,9 +452,9 @@ class MessageList_SubjectWidget(BMTableWidgetItem):
     def data(self, role):
         """Return object data (QT UI)"""
         if role == QtCore.Qt.UserRole:
-            return self.subject
+            return ustr(self.subject)
         if role == QtCore.Qt.ToolTipRole:
-            return escape(unicode(self.subject, 'utf-8'))
+            return escape(unic(ustr(self.subject)))
         return super(MessageList_SubjectWidget, self).data(role)
 
     # label (or address) alphabetically, disabled at the end
@@ -491,9 +489,9 @@ class MessageList_TimeWidget(BMTableWidgetItem):
         """
         data = super(MessageList_TimeWidget, self).data(role)
         if role == TimestampRole:
-            return int(data.toPyObject())
+            return int(data)
         if role == QtCore.Qt.UserRole:
-            return str(data.toPyObject())
+            return ustr(data)
         return data
 
 
@@ -513,8 +511,8 @@ class Ui_AddressBookWidgetItem(BMAddressWidget):
     def setData(self, role, value):
         """Set data"""
         if role == QtCore.Qt.EditRole:
-            self.label = str(
-                value.toString().toUtf8()
+            self.label = ustr(
+                value
                 if isinstance(value, QtCore.QVariant) else value
             )
             if self.type in (
@@ -546,7 +544,7 @@ class Ui_AddressBookWidgetItem(BMAddressWidget):
 class Ui_AddressBookWidgetItemLabel(Ui_AddressBookWidgetItem):
     """Addressbook label item"""
     def __init__(self, address, label, acc_type):
-        self.address = address
+        self.address = ustr(address)
         super(Ui_AddressBookWidgetItemLabel, self).__init__(label, acc_type)
 
     def data(self, role):
@@ -558,7 +556,7 @@ class Ui_AddressBookWidgetItemLabel(Ui_AddressBookWidgetItem):
 class Ui_AddressBookWidgetItemAddress(Ui_AddressBookWidgetItem):
     """Addressbook address item"""
     def __init__(self, address, label, acc_type):
-        self.address = address
+        self.address = ustr(address)
         super(Ui_AddressBookWidgetItemAddress, self).__init__(address, acc_type)
 
     def data(self, role):
@@ -584,14 +582,14 @@ class AddressBookCompleter(QtGui.QCompleter):
 
     def splitPath(self, path):
         """Split on semicolon"""
-        text = unicode(path.toUtf8(), 'utf-8')
+        text = unic(ustr(path))
         return [text[:self.widget().cursorPosition()].split(';')[-1].strip()]
 
     def pathFromIndex(self, index):
         """Perform autocompletion (reimplemented QCompleter method)"""
-        autoString = unicode(
-            index.data(QtCore.Qt.EditRole).toString().toUtf8(), 'utf-8')
-        text = unicode(self.widget().text().toUtf8(), 'utf-8')
+        autoString = unic(ustr(
+            index.data(QtCore.Qt.EditRole)))
+        text = unic(ustr(self.widget().text()))
 
         # If cursor position was saved, restore it, else save it
         if self.cursorPos != -1:
diff --git a/src/bitmessageqt/messageview.py b/src/bitmessageqt/messageview.py
index 4c17569c..fe58cc0f 100644
--- a/src/bitmessageqt/messageview.py
+++ b/src/bitmessageqt/messageview.py
@@ -5,6 +5,7 @@ zoom and URL click warning popup
 
 """
 
+from ver import ustr, unic
 from PyQt4 import QtCore, QtGui
 
 from safehtmlparser import SafeHTMLParser
@@ -56,7 +57,7 @@ class MessageView(QtGui.QTextBrowser):
         ) == QtCore.Qt.ControlModifier and event.orientation() == QtCore.Qt.Vertical:
             zoom = self.currentFont().pointSize() * 100 / self.defaultFontPointSize
             QtGui.QApplication.activeWindow().statusBar().showMessage(_translate(
-                "MainWindow", "Zoom level {0}%").format(str(zoom)))
+                "MainWindow", "Zoom level {0}%").format(ustr(zoom)))
 
     def setWrappingWidth(self, width=None):
         """Set word-wrapping width"""
@@ -91,7 +92,7 @@ class MessageView(QtGui.QTextBrowser):
             QtGui.QApplication.translate(
                 "MessageView",
                 "The link \"{0}\" will open in a browser. It may be a security risk, it could de-anonymise you"
-                " or download malicious data. Are you sure?").format(unicode(link.toString())),
+                " or download malicious data. Are you sure?").format(unic(ustr(link))),
             QtGui.QMessageBox.Yes,
             QtGui.QMessageBox.No)
         if reply == QtGui.QMessageBox.Yes:
@@ -124,7 +125,7 @@ class MessageView(QtGui.QTextBrowser):
                 if pos > self.outpos:
                     self.outpos = pos + 1
             cursor.movePosition(QtGui.QTextCursor.End, QtGui.QTextCursor.MoveAnchor)
-            cursor.insertHtml(QtCore.QString(self.out[startpos:self.outpos]))
+            cursor.insertHtml(unic(self.out[startpos:self.outpos]))
         self.verticalScrollBar().setValue(position)
         self.rendering = False
 
@@ -133,9 +134,9 @@ class MessageView(QtGui.QTextBrowser):
         self.mode = MessageView.MODE_PLAIN
         out = self.html.raw
         if self.html.has_html:
-            out = "<div align=\"center\" style=\"text-decoration: underline;\"><b>" + unicode(
+            out = "<div align=\"center\" style=\"text-decoration: underline;\"><b>" + unic(ustr(
                 QtGui.QApplication.translate(
-                    "MessageView", "HTML detected, click here to display")) + "</b></div><br/>" + out
+                    "MessageView", "HTML detected, click here to display")) + "</b></div><br/>" + out)
         self.out = out
         self.outpos = 0
         self.setHtml("")
@@ -145,8 +146,8 @@ class MessageView(QtGui.QTextBrowser):
         """Render message as HTML"""
         self.mode = MessageView.MODE_HTML
         out = self.html.sanitised
-        out = "<div align=\"center\" style=\"text-decoration: underline;\"><b>" + unicode(
-            QtGui.QApplication.translate("MessageView", "Click here to disable HTML")) + "</b></div><br/>" + out
+        out = "<div align=\"center\" style=\"text-decoration: underline;\"><b>" + unic(ustr(
+            QtGui.QApplication.translate("MessageView", "Click here to disable HTML")) + "</b></div><br/>" + out)
         self.out = out
         self.outpos = 0
         self.setHtml("")
diff --git a/src/bitmessageqt/networkstatus.py b/src/bitmessageqt/networkstatus.py
index dbaf8fd0..8dd6dea7 100644
--- a/src/bitmessageqt/networkstatus.py
+++ b/src/bitmessageqt/networkstatus.py
@@ -204,9 +204,9 @@ class NetworkStatus(QtGui.QWidget, RetranslateMixin):
             if not connectionpool.pool.inboundConnections:
                 self.window().setStatusIcon('yellow')
             for i in range(self.tableWidgetConnectionCount.rowCount()):
-                if self.tableWidgetConnectionCount.item(i, 0).data(QtCore.Qt.UserRole).toPyObject() != destination:
+                if self.tableWidgetConnectionCount.item(i, 0).data(QtCore.Qt.UserRole) != destination:
                     continue
-                if self.tableWidgetConnectionCount.item(i, 1).data(QtCore.Qt.UserRole).toPyObject() == outbound:
+                if self.tableWidgetConnectionCount.item(i, 1).data(QtCore.Qt.UserRole) == outbound:
                     self.tableWidgetConnectionCount.removeRow(i)
                     break
 
diff --git a/src/bitmessageqt/newchandialog.py b/src/bitmessageqt/newchandialog.py
index 35087543..46e06959 100644
--- a/src/bitmessageqt/newchandialog.py
+++ b/src/bitmessageqt/newchandialog.py
@@ -4,6 +4,7 @@ src/bitmessageqt/newchandialog.py
 
 """
 
+from ver import ustr, unic
 from PyQt4 import QtCore, QtGui
 
 import widgets
@@ -52,21 +53,21 @@ class NewChanDialog(QtGui.QDialog):
         self.timer.stop()
         self.hide()
         apiAddressGeneratorReturnQueue.queue.clear()
-        if self.chanAddress.text().toUtf8() == "":
+        if ustr(self.chanAddress.text()) == "":
             addressGeneratorQueue.put(
-                ('createChan', 4, 1, str_chan + ' ' + str(self.chanPassPhrase.text().toUtf8()),
-                 self.chanPassPhrase.text().toUtf8(),
+                ('createChan', 4, 1, str_chan + ' ' + ustr(self.chanPassPhrase.text()),
+                 ustr(self.chanPassPhrase.text()),
                  True))
         else:
             addressGeneratorQueue.put(
-                ('joinChan', addBMIfNotPresent(self.chanAddress.text().toUtf8()),
-                 str_chan + ' ' + str(self.chanPassPhrase.text().toUtf8()),
-                 self.chanPassPhrase.text().toUtf8(),
+                ('joinChan', addBMIfNotPresent(ustr(self.chanAddress.text())),
+                 str_chan + ' ' + ustr(self.chanPassPhrase.text()),
+                 ustr(self.chanPassPhrase.text()),
                  True))
         addressGeneratorReturnValue = apiAddressGeneratorReturnQueue.get(True)
         if addressGeneratorReturnValue and addressGeneratorReturnValue[0] != 'chan name does not match address':
             UISignalQueue.put(('updateStatusBar', _translate(
-                "newchandialog", "Successfully created / joined chan {0}").format(unicode(self.chanPassPhrase.text()))))
+                "newchandialog", "Successfully created / joined chan {0}").format(unic(ustr(self.chanPassPhrase.text())))))
             self.parent.ui.tabWidget.setCurrentIndex(
                 self.parent.ui.tabWidget.indexOf(self.parent.ui.chans)
             )
diff --git a/src/bitmessageqt/retranslateui.py b/src/bitmessageqt/retranslateui.py
index c7676f77..5b1df839 100644
--- a/src/bitmessageqt/retranslateui.py
+++ b/src/bitmessageqt/retranslateui.py
@@ -1,4 +1,5 @@
 from os import path
+from ver import ustr
 from PyQt4 import QtGui
 from debug import logger
 import widgets
@@ -10,11 +11,11 @@ class RetranslateMixin(object):
         for attr, value in defaults.__dict__.iteritems():
             setTextMethod = getattr(value, "setText", None)
             if callable(setTextMethod):
-                getattr(self, attr).setText(getattr(defaults, attr).text())
+                getattr(self, attr).setText(ustr(getattr(defaults, attr).text()))
             elif isinstance(value, QtGui.QTableWidget):
                 for i in range (value.columnCount()):
                     getattr(self, attr).horizontalHeaderItem(i).setText(
-                        getattr(defaults, attr).horizontalHeaderItem(i).text())
+                        ustr(getattr(defaults, attr).horizontalHeaderItem(i).text()))
                 for i in range (value.rowCount()):
                     getattr(self, attr).verticalHeaderItem(i).setText(
-                        getattr(defaults, attr).verticalHeaderItem(i).text())
+                        ustr(getattr(defaults, attr).verticalHeaderItem(i).text()))
diff --git a/src/bitmessageqt/safehtmlparser.py b/src/bitmessageqt/safehtmlparser.py
index d408d2c7..42c71486 100644
--- a/src/bitmessageqt/safehtmlparser.py
+++ b/src/bitmessageqt/safehtmlparser.py
@@ -7,6 +7,7 @@ from HTMLParser import HTMLParser
 from urllib import quote_plus
 from urlparse import urlparse
 
+from ver import ustr, unic
 
 class SafeHTMLParser(HTMLParser):
     """HTML parser with sanitisation"""
@@ -123,10 +124,7 @@ class SafeHTMLParser(HTMLParser):
         self.sanitised += "&" + name + ";"
 
     def feed(self, data):
-        try:
-            data = unicode(data, 'utf-8')
-        except UnicodeDecodeError:
-            data = unicode(data, 'utf-8', errors='replace')
+        data = unic(ustr(data))
         HTMLParser.feed(self, data)
         tmp = SafeHTMLParser.replace_pre(data)
         tmp = self.uriregex1.sub(r'<a href="\1">\1</a>', tmp)
diff --git a/src/bitmessageqt/settings.py b/src/bitmessageqt/settings.py
index 3d05db25..50e8ebdb 100644
--- a/src/bitmessageqt/settings.py
+++ b/src/bitmessageqt/settings.py
@@ -7,6 +7,7 @@ import sys
 import tempfile
 
 import six
+from ver import ustr
 from PyQt4 import QtCore, QtGui
 
 import debug
@@ -175,7 +176,7 @@ class SettingsDialog(QtGui.QDialog):
             else:
                 if self.checkBoxOnionOnly.isChecked():
                     self.checkBoxOnionOnly.setText(
-                        self.checkBoxOnionOnly.text() + ", " + _translate(
+                        ustr(self.checkBoxOnionOnly.text()) + ", " + _translate(
                             "MainWindow", "may cause connection problems!"))
                     self.checkBoxOnionOnly.setStyleSheet(
                         "QCheckBox { color : red; }")
@@ -312,10 +313,10 @@ class SettingsDialog(QtGui.QDialog):
             _translate("MainWindow", "Testing..."))
         nc = namecoin.namecoinConnection({
             'type': self.getNamecoinType(),
-            'host': str(self.lineEditNamecoinHost.text().toUtf8()),
-            'port': str(self.lineEditNamecoinPort.text().toUtf8()),
-            'user': str(self.lineEditNamecoinUser.text().toUtf8()),
-            'password': str(self.lineEditNamecoinPassword.text().toUtf8())
+            'host': ustr(self.lineEditNamecoinHost.text()),
+            'port': ustr(self.lineEditNamecoinPort.text()),
+            'user': ustr(self.lineEditNamecoinUser.text()),
+            'password': ustr(self.lineEditNamecoinPassword.text())
         })
         status, text = nc.test()
         self.labelNamecoinTestResult.setText(text)
@@ -349,7 +350,7 @@ class SettingsDialog(QtGui.QDialog):
             self.checkBoxReplyBelow.isChecked()))
 
         lang = str(self.languageComboBox.itemData(
-            self.languageComboBox.currentIndex()).toString())
+            self.languageComboBox.currentIndex()))
         self.config.set('bitmessagesettings', 'userlocale', lang)
         self.parent.change_translation()
 
@@ -448,13 +449,13 @@ class SettingsDialog(QtGui.QDialog):
 
         self.config.set(
             'bitmessagesettings', 'namecoinrpctype', self.getNamecoinType())
-        self.config.set('bitmessagesettings', 'namecoinrpchost', str(
+        self.config.set('bitmessagesettings', 'namecoinrpchost', ustr(
             self.lineEditNamecoinHost.text()))
-        self.config.set('bitmessagesettings', 'namecoinrpcport', str(
+        self.config.set('bitmessagesettings', 'namecoinrpcport', ustr(
             self.lineEditNamecoinPort.text()))
-        self.config.set('bitmessagesettings', 'namecoinrpcuser', str(
+        self.config.set('bitmessagesettings', 'namecoinrpcuser', ustr(
             self.lineEditNamecoinUser.text()))
-        self.config.set('bitmessagesettings', 'namecoinrpcpassword', str(
+        self.config.set('bitmessagesettings', 'namecoinrpcpassword', ustr(
             self.lineEditNamecoinPassword.text()))
         self.parent.resetNamecoinConnection()
 
@@ -472,11 +473,11 @@ class SettingsDialog(QtGui.QDialog):
                     float(self.lineEditSmallMessageDifficulty.text())
                     * defaults.networkDefaultPayloadLengthExtraBytes)))
 
-        if self.comboBoxOpenCL.currentText().toUtf8() != self.config.safeGet(
-                'bitmessagesettings', 'opencl'):
+        if ustr(self.comboBoxOpenCL.currentText()) != ustr(self.config.safeGet(
+                'bitmessagesettings', 'opencl')):
             self.config.set(
                 'bitmessagesettings', 'opencl',
-                str(self.comboBoxOpenCL.currentText()))
+                ustr(self.comboBoxOpenCL.currentText()))
             queues.workerQueue.put(('resetPoW', ''))
 
         acceptableDifficultyChanged = False
diff --git a/src/bitmessageqt/settingsmixin.py b/src/bitmessageqt/settingsmixin.py
index 3d5999e2..5a24837d 100644
--- a/src/bitmessageqt/settingsmixin.py
+++ b/src/bitmessageqt/settingsmixin.py
@@ -5,6 +5,7 @@ src/settingsmixin.py
 
 """
 
+from ver import ustr
 from PyQt4 import QtCore, QtGui
 
 
@@ -40,7 +41,7 @@ class SettingsMixin(object):
         self.warnIfNoObjectName()
         settings = QtCore.QSettings()
         try:
-            geom = settings.value("/".join([str(self.objectName()), "geometry"]))
+            geom = settings.value("/".join([ustr(self.objectName()), "geometry"]))
             target.restoreGeometry(geom.toByteArray() if hasattr(geom, 'toByteArray') else geom)
         except Exception:
             pass
@@ -50,7 +51,7 @@ class SettingsMixin(object):
         self.warnIfNoObjectName()
         settings = QtCore.QSettings()
         try:
-            state = settings.value("/".join([str(self.objectName()), "state"]))
+            state = settings.value("/".join([ustr(self.objectName()), "state"]))
             target.restoreState(state.toByteArray() if hasattr(state, 'toByteArray') else state)
         except Exception:
             pass
diff --git a/src/bitmessageqt/support.py b/src/bitmessageqt/support.py
index a84affa4..8bb80b02 100644
--- a/src/bitmessageqt/support.py
+++ b/src/bitmessageqt/support.py
@@ -6,6 +6,7 @@ import ssl
 import sys
 import time
 
+from ver import ustr, unic
 from PyQt4 import QtCore
 
 import account
@@ -72,7 +73,7 @@ def checkAddressBook(myapp):
     if queryreturn == []:
         sqlExecute(
             'INSERT INTO addressbook VALUES (?,?)',
-            SUPPORT_LABEL.toUtf8(), SUPPORT_ADDRESS)
+            ustr(SUPPORT_LABEL), SUPPORT_ADDRESS)
         myapp.rerenderAddressBook()
 
 
@@ -88,7 +89,7 @@ def createAddressIfNeeded(myapp):
     if not checkHasNormalAddress():
         queues.addressGeneratorQueue.put((
             'createRandomAddress', 4, 1,
-            str(SUPPORT_MY_LABEL.toUtf8()),
+            ustr(SUPPORT_MY_LABEL),
             1, "", False,
             defaults.networkDefaultProofOfWorkNonceTrialsPerByte,
             defaults.networkDefaultPayloadLengthExtraBytes
@@ -122,7 +123,7 @@ def createSupportMessage(myapp):
     os = sys.platform
     if os == "win32":
         windowsversion = sys.getwindowsversion()
-        os = "Windows " + str(windowsversion[0]) + "." + str(windowsversion[1])
+        os = "Windows " + ustr(windowsversion[0]) + "." + ustr(windowsversion[1])
     else:
         try:
             from os import uname
@@ -141,7 +142,7 @@ def createSupportMessage(myapp):
         frozen = paths.frozen
     portablemode = "True" if state.appdata == paths.lookupExeFolder() else "False"
     cpow = "True" if proofofwork.bmpow else "False"
-    openclpow = str(
+    openclpow = ustr(
         config.safeGet('bitmessagesettings', 'opencl')
     ) if openclEnabled() else "None"
     locale = getTranslationLanguage()
@@ -149,9 +150,9 @@ def createSupportMessage(myapp):
     upnp = config.safeGet('bitmessagesettings', 'upnp', "N/A")
     connectedhosts = len(network.stats.connectedHostsList())
 
-    myapp.ui.textEditMessage.setText(unicode(SUPPORT_MESSAGE, 'utf-8').format(
+    myapp.ui.textEditMessage.setText(unic(ustr(SUPPORT_MESSAGE).format(
         version, os, architecture, pythonversion, opensslversion, frozen,
-        portablemode, cpow, openclpow, locale, socks, upnp, connectedhosts))
+        portablemode, cpow, openclpow, locale, socks, upnp, connectedhosts)))
 
     # single msg tab
     myapp.ui.tabWidgetSend.setCurrentIndex(
diff --git a/src/bitmessageqt/tests/main.py b/src/bitmessageqt/tests/main.py
index b3aa67fa..91f0ae11 100644
--- a/src/bitmessageqt/tests/main.py
+++ b/src/bitmessageqt/tests/main.py
@@ -41,7 +41,7 @@ class TestMain(unittest.TestCase):
         """Check the results of _translate() with various args"""
         self.assertIsInstance(
             _translate("MainWindow", "Test"),
-            QtCore.QString
+            str
         )
 
 
diff --git a/src/bitmessageqt/tests/support.py b/src/bitmessageqt/tests/support.py
index ba28b73a..f611a0e7 100644
--- a/src/bitmessageqt/tests/support.py
+++ b/src/bitmessageqt/tests/support.py
@@ -6,6 +6,8 @@ from shared import isAddressInMyAddressBook
 
 from main import TestBase
 
+from ver import ustr
+
 
 class TestSupport(TestBase):
     """A test case for support module"""
@@ -26,8 +28,8 @@ class TestSupport(TestBase):
         self.assertEqual(
             ui.tabWidget.currentIndex(), ui.tabWidget.indexOf(ui.send))
         self.assertEqual(
-            ui.lineEditTo.text(), self.SUPPORT_ADDRESS)
+            ustr(ui.lineEditTo.text()), ustr(self.SUPPORT_ADDRESS))
         self.assertEqual(
-            ui.lineEditSubject.text(), self.SUPPORT_SUBJECT)
+            ustr(ui.lineEditSubject.text()), ustr(self.SUPPORT_SUBJECT))
         self.assertIn(
             sys.version, ui.textEditMessage.toPlainText())
diff --git a/src/depends.py b/src/depends.py
index d966d5fe..4212d654 100755
--- a/src/depends.py
+++ b/src/depends.py
@@ -383,6 +383,15 @@ def check_pyqt():
     Here we are checking for PyQt4 with its version, as for it require
     PyQt 4.8 or later.
     """
+    sip_found = False
+    try:
+        import sip
+        sip.setapi("QString", 2)
+        sip.setapi("QVariant", 2)
+        sip_found = True
+    except ImportError:
+        pass
+
     QtCore = try_import(
         'PyQt4.QtCore', 'PyBitmessage requires PyQt 4.8 or later and Qt 4.7 or later.')
 
@@ -402,6 +411,11 @@ def check_pyqt():
             'This version of Qt is too old. PyBitmessage requries'
             ' Qt 4.7 or later.')
         passed = False
+
+    if passed and not sip_found:
+        logger.info("sip is not found although PyQt is found")
+        return False
+
     return passed
 
 
diff --git a/src/tr.py b/src/tr.py
index eec82c37..66920197 100644
--- a/src/tr.py
+++ b/src/tr.py
@@ -3,6 +3,8 @@ Translating text
 """
 import os
 
+from ver import ustr
+
 try:
     import state
 except ImportError:
@@ -30,7 +32,7 @@ class translateClass:
 
 def _translate(context, text, disambiguation=None, encoding=None, n=None):
     # pylint: disable=unused-argument
-    return translateText(context, text, n)
+    return ustr(translateText(context, text, n))
 
 
 def translateText(context, text, n=None):
diff --git a/src/ver.py b/src/ver.py
new file mode 100644
index 00000000..50e49609
--- /dev/null
+++ b/src/ver.py
@@ -0,0 +1,30 @@
+import sys
+
+if not hasattr(sys, "hexversion"):
+    sys.exit("Python version: {0}\n"
+             "PyBitmessage requires Python 2.7.4 or greater"
+             .format(sys.version))
+
+if sys.hexversion < 0x3000000:
+    VER = 2
+else:
+    VER = 3
+
+def ustr(v):
+    if VER == 3:
+        if isinstance(v, str):
+            return v
+        else:
+            return str(v)
+    # assume VER == 2
+    if isinstance(v, unicode):
+        return v.encode("utf-8", "replace")
+    return str(v)
+
+def unic(v):
+    if VER == 3:
+        return v
+    # assume VER == 2
+    if isinstance(v, unicode):
+        return v
+    return unicode(v, "utf-8", "replace")