Разговариваем про PyQt4 — Посиделка первая

    image

    Небольшое вступление


        Собственно, тогда, давно, я решил попробовать Qt, потому что часто слышал об удобстве разработки под него и своими глазами видел, какая шикарная документация представлена на сайте производителя. Не могу сказать, что это далось легко (я раньше немного писал на GTK), особенно путался в этих бесконечных классах на "Q", но постепенно начало нравиться все больше и больше. В частности потому, что есть отличная привязка к нему для языка Python, на котором я, собственно, в основном и пишу.
        Еще почему? Ну, я мог бы рассказать и о том, что он работает как на почти всех настольных системах, так и на многих мобильных, рассказать про совершенно гениальную объектную систему виджетов и т. п. Но — зачем? Не люблю холивары с приверженцами других визуальных библиотек :) Поэтому давайте считать этот топик чем-то вроде дележки опытом и рассуждений на тему.

    Ну, поехали...


        В силу особенностей современного образования, многие программисты молодого поколения (к коим себя причисляет и ваш покорный слуга) живут с вколоченными в голову Pascal'ем и Delphi. Ну а что, ведь удобно — рисуешь мышкой окошки, связываешь компоненты, прописываешь им методы — и в минимальные сроки получаешь красивое оконное приложение. Я сам довольно долго сидел на них, даже делал пару фриланс-проектов. Но в один прекрасный день что-то щелкнуло в голове — и на ноутбуке вместо сверкающей Vista поселилась коричневая Ubuntu. Не буду рассказывать, почему и как я выбрал Python, но однажды возникла потребность вылезти из черных недр консоли и написать кое-что оконное.
        Как для Qt, так и для GTK существуют свои визуальные редакторы, интерфейс которых буквально интуитивно понятен не только бывшему делфятнику, но и разработчику, решившему сунуться в десктопную разработку первый раз. Для GTK это Glade, для Qt — идущий в комплекте с SDK инструмент Qt Designer. Причем оба они существуют как в виде самостоятельных приложений, так и как плагины к небезызвестной и нежно мной любимой среде Eclipse, а еще для Qt недавно появилась очень и очень неплохая нативная IDE — Qt Creator, которую тоже включили в SDK. Единственный минус — пока что не существует вменяемого плагина, позволяющего использовать ее для разработки на Python. На выходе у обоих — файл с xml-структурой, чем-то напоминающий по своему назначению dfm-файлы Delphi. То есть их можно положить в папку с проектом и подключить несколькими строками кода — и все практически готово.
        Для пущего удобства существует пакет-посредник между Qt Designer и Python и носит он имя pyqt4-dev-tools, а внутри него лежит программка pyuic4, служащая для удобной трансляции ui-файлов «дизайнера» в чистенький и опрятненький Python-код. НО! Как обычно в этой жизни, здесь не работает старый добрый принцип «нажмешь кнопку и красиво». Более того, я очень не советую вам употреблять pyuic4 для серьезных проектов. Почему? Сейчас расскажу.
        Pyuic4 — совершенно незаменимая вещь при освоении PyQt4. Что может быть удобнее — бросил пару виджетов на форму, транслировал получившийся файл одной командой в Python-скрипт — и уже ковыряешь код, смотря, какие методы вызываются при создании виджетов, обращении к ним, создании надписей и т. д. Но pyuic4 также генерирует кучу ненужного, на мой взгляд, кода, без которого можно обойтись и сделать все удобнее и компактнее без потери читабельности и удобства. Вот пример кода, генерируемого pyuic4 для простейшей формы с двумя кнопками и полем ввода (и да, не ругайте меня за стандартные имена для виджетов, это всего лишь пример :) ):
    Copy Source | Copy HTML
    1. # -*- coding: utf-8 -*-
    2.  
    3. # Form implementation generated from reading ui file '/home/username/Рабочий стол/habr.ui'
    4. #
    5. # Created: Fri Nov 13 23:52:05 2009
    6. #      by: PyQt4 UI code generator 4.6
    7. #
    8. # WARNING! All changes made in this file will be lost!
    9.  
    10. from PyQt4 import QtCore, QtGui
    11.  
    12. class Ui_MainWindow(object):
    13.     def setupUi(self, MainWindow):
    14.         MainWindow.setObjectName("MainWindow")
    15.         MainWindow.resize(226, 146)
    16.         self.centralwidget = QtGui.QWidget(MainWindow)
    17.         self.centralwidget.setObjectName("centralwidget")
    18.         self.lineEdit = QtGui.QLineEdit(self.centralwidget)
    19.         self.lineEdit.setGeometry(QtCore.QRect(10, 10, 201, 26))
    20.         self.lineEdit.setObjectName("lineEdit")
    21.         self.pushButton = QtGui.QPushButton(self.centralwidget)
    22.         self.pushButton.setGeometry(QtCore.QRect(10, 50, 92, 28))
    23.         self.pushButton.setObjectName("pushButton")
    24.         self.pushButton_2 = QtGui.QPushButton(self.centralwidget)
    25.         self.pushButton_2.setGeometry(QtCore.QRect(120, 50, 92, 28))
    26.         self.pushButton_2.setObjectName("pushButton_2")
    27.         MainWindow.setCentralWidget(self.centralwidget)
    28.         self.menubar = QtGui.QMenuBar(MainWindow)
    29.         self.menubar.setGeometry(QtCore.QRect( 0,  0, 226, 25))
    30.         self.menubar.setObjectName("menubar")
    31.         MainWindow.setMenuBar(self.menubar)
    32.         self.statusbar = QtGui.QStatusBar(MainWindow)
    33.         self.statusbar.setObjectName("statusbar")
    34.         MainWindow.setStatusBar(self.statusbar)
    35.  
    36.         self.retranslateUi(MainWindow)
    37.         QtCore.QMetaObject.connectSlotsByName(MainWindow)
    38.  
    39.     def retranslateUi(self, MainWindow):
    40.         MainWindow.setWindowTitle(QtGui.QApplication.translate("MainWindow", "ХабраОкно", None, QtGui.QApplication.UnicodeUTF8))
    41.         self.pushButton.setText(QtGui.QApplication.translate("MainWindow", "PushButton", None, QtGui.QApplication.UnicodeUTF8))
    42.         self.pushButton_2.setText(QtGui.QApplication.translate("MainWindow", "PushButton", None, QtGui.QApplication.UnicodeUTF8))
    43.  
    44.  
    45. if __name__ == "__main__":
    46.     import sys
    47.     app = QtGui.QApplication(sys.argv)
    48.     MainWindow = QtGui.QMainWindow()
    49.     ui = Ui_MainWindow()
    50.     ui.setupUi(MainWindow)
    51.     MainWindow.show()
    52.     sys.exit(app.exec_())
    53.  

        Во-первых, как мы видим, класс наследуется не от QMainWindow, а от object — это так называемая «новая» версия классов Python, при этом объект типа QMainWindow передается в метод класса как параметр. Это создает некоторую путаницу при работе с виджетами — объекты виджетов являются полями не класса окна, а родительского класса Ui_MainWindow. Но как с удобством, так и с неудобством этого, естественно, можно и поспорить. Тут уж каждому свое.
        Каждому объекту присваивается внутреннее имя, и это довольно любопытная вещь — нечто среднее между делфовскими свойствами Caption и Name. По сути, это имя требуется в том случае, когда к виджету не очень удобно обращаться как к полю класса. Если же у вас нет сложных генерируемых на лету форм и больших цепочек наследования — без этих имен можно спокойно обойтись, что я обычно и делаю. Кроме того, для создания интерфейса с поддержкой перевода на разные языки все текстовые данные прогоняются через транслятор и приводятся к кодировке UTF-8, причем это вынесено в отдельный метод класса. И все бы хорошо, только для каждого объекта копируется вот эта здоровенная строчка:
    QtGui.QApplication.translate(«MainWindow», «ХабраОкно», None, QtGui.QApplication.UnicodeUTF8)

    Почему бы не вынести ее в отдельный метод класса? :) Давайте в своем коде (который будем писать на основе сгенеренного файла) сделаем, например, так:
    Copy Source | Copy HTML
    1. def toUtf(self, text):
    2.     return QtGui.QApplication.translate("MainWindow", text, None, QtGui.QApplication.UnicodeUTF8)

        Код не ухудшится с точки зрения читабельности, зато возвращаемые из сторонних функций строки будет легко преобразовывать к нужному типу.
        Кстати, то, что в генерируемом pyuic4 коде указывается полный адрес каждого модуля первого уровня — это в данном случае плюс, так как для разработки действительно полезно знать родителя для отдельного метода. Хотя баталии между любителями полных адресов и приверженцами строчек типа "from module import *" не утихают никогда.

    Даешь прааактику!


        Чувствую, я уже надоел вам своей болтовней на отвлеченные темы, поэтому давайте рванем с места в карьер и разберем несколько типовых примеров, с которыми сталкиваются люди, изучающие PyQt4. Нет, я не буду здесь писать руководство для новичков, а просто опишу свой собственный опыт и подводные камни, с которыми пришлось столкнуться.
        Для начала давайте сделаем такую вещь — вынесем форму в отдельный модуль, который подключается к проекту. Из-за двойной классовой структуры, описанной выше, из функции создания окна придется возвращать два аргумента, а при создании главного окна — целых три. Итак, на повестке дня два файла — запускающий:
    Copy Source | Copy HTML
    1. #!/usr/bin/python
    2. # -*- coding: utf-8 -*-
    3. import sys
    4. from forms import MainForm
    5.  
    6. def main():
    7.     app, mainForm, window = MainForm.init()
    8.     window.show()
    9.     sys.exit(app.exec_())
    10.  
    11. if __name__ == "__main__":
    12.     main()

    и файл, собственно, формы:
    Copy Source | Copy HTML
    1. # -*- coding: utf-8 -*-
    2. import sys
    3. from PyQt4 import QtCore, QtGui, Qt
    4.  
    5. class mainWindow(object):
    6.     def setupUi(self, MainWindow):
    7.         # установим для окна фиксированный размер (знание метода лишним не будет)
    8.         MainWindow.setFixedSize(950, 550)
    9.         # я главный, а все виджеты от меня наследуются
    10.         self.main = QtGui.QWidget(MainWindow)
    11.         MainWindow.setCentralWidget(self.main)
    12.         # для полноты ощущений создадим меню "Файл"
    13.         self.menubar = QtGui.QMenuBar(MainWindow)
    14.         self.menubar.setGeometry(QtCore.QRect( 0,  0, 559, 25))
    15.         # создаем триггер-действие QAction, чтобы привязать его к пункту меню
    16.         self.menu_file_exit = QtGui.QAction(self.main)
    17.         self.menu_file_exit.setText(self.toUtf("&Выход"))
    18.         MainWindow.connect(self.menu_file_exit, QtCore.SIGNAL('triggered()'), sys.exit)
    19.         # создаем пункт меню и добавляем в него наш QAction
    20.         self.menu_file = self.menubar.addMenu(self.toUtf('&Файл'))
    21.         self.menu_file.addAction(self.menu_file_exit)
    22.         MainWindow.setMenuBar(self.menubar)
    23.         # для солидности приделываем статусбар
    24.         self.statusbar = QtGui.QStatusBar(MainWindow)
    25.         MainWindow.setStatusBar(self.statusbar)
    26.         # двигаем окно куда нам хочется
    27.         MainWindow.move(140, 80)
    28.         self.retranslateUi(MainWindow)
    29.  
    30.     def retranslateUi(self, MainWindow):
    31.         # даем окну название
    32.         MainWindow.setWindowTitle(self.toUtf("ХабраОкно 2.0"))
    33.  
    34.     def toUtf(self, text):
    35.         # та самая функция перевода
    36.         return QtGui.QApplication.translate("MainWindow", text, None, QtGui.QApplication.UnicodeUTF8)
    37.  
    38. def init():
    39.     # инициализируем Qt
    40.     app = QtGui.QApplication(sys.argv)
    41.     # создаем отдельный, независимый объект окна...
    42.     MainWindow = QtGui.QMainWindow()
    43.     # ...и прогоняем его через наш класс
    44.     form = mainWindow()
    45.     form.setupUi(MainWindow)
    46.     return app, form, MainWindow

        В данном случае проект имеет такую структуру:
    main.py
    forms/
    >    __init__.py (пустой файл, нужен, чтобы папка распознавалась как Python-пакет)
    >    MainForm.py

    Я знаю, что такой способ разделения можно ругать и ругать, однако ж сколько полезного мы узнали о создании окна! :) А теперь давайте не будем останавливаться на достигнутом и создадим дочернее окно, чтобы нашему было не так одиноко (не зря же я создавал отдельный каталог forms). Более того, сделаем его модальным (родительское окно не будет реагировать на действия пользователя, пока открыто дочернее), дабы шаловливые ручки юзеров не привели к плохим последствиям. Для этого создадим в каталоге forms файл ChildForm.py, в котором опишем дочернюю форму:
    Copy Source | Copy HTML
    1. # -*- coding: utf-8 -*-
    2. from PyQt4 import QtCore, QtGui
    3.  
    4. class childWindow(object):
    5.     def setupUi(self, SmallWindow):
    6.         SmallWindow.setFixedSize(330, 200)
    7.         SmallWindow.setWindowFlags(QtCore.Qt.Window)
    8.         self.retranslateUi(SmallWindow)
    9.         SmallWindow.setWindowModality(QtCore.Qt.WindowModal)
    10.  
    11.     def retranslateUi(self, Form):
    12.         Form.setWindowTitle(self.toUtf("Я - дочернее окно"))
    13.  
    14.     def toUtf(self, text):
    15.         return QtGui.QApplication.translate("SmallWindow", text, None, QtGui.QApplication.UnicodeUTF8)
    16.  
    17. def init(parentwindow):
    18.     SmallWindow = QtGui.QWidget(parentwindow)
    19.     form = childWindow()
    20.     form.setupUi(SmallWindow)
    21.     return form, SmallWindow

    а также внесем некоторые изменения в файл основной формы:
    Copy Source | Copy HTML
    1. ...
    2. from forms import ChildForm
    3. ...
    4. # создаем свой класс окна, это нужно, чтобы решить кое-какие проблемы с наследованием
    5. class myQMainWindow(QtGui.QMainWindow):
    6.     def __init__(self, parent=None):
    7.         QtGui.QMainWindow.__init__(self, parent)
    8. ...
    9. def setupUi(self, MainWindow):
    10.         ...
    11.         # выносим окно в поле класса для более удобного наследования
    12.         self.mainwindow = MainWindow
    13.         # рисуем кнопку, по которой будет отображаться дочернее окно
    14.         self.btnHello = QtGui.QPushButton(self.main)
    15.         self.btnHello.setGeometry(QtCore.QRect(20, 19, 92, 28))
    16.         MainWindow.connect(self.btnHello, QtCore.SIGNAL('clicked()'), self.showChildWindow)
    17.         ...
    18.  
    19. def retranslateUi(self, MainWindow):
    20.         ...
    21.         self.btnHello.setText(self.toUtf("Жми!"))
    22.         ...
    23.  
    24. def showChildWindow(self):
    25.         self.childForm, self.childWindow = ChildForm.init(self.mainwindow)
    26.         self.childWindow.show()
    27.  
    28. def init():
    29.     ...
    30.     #MainWindow = QtGui.QMainWindow()
    31.     MainWindow = myQMainWindow()
    32.     ...

        Вуаля! У нас есть родительское и дочернее окна.

    Думаю, на сегодня посиделку завершим, но это только начало :) Я знаю, что этот топик содержит не так много полезной информации, как хотелось бы, но обещаю исправиться — на следующей посиделке будет сплошная практика.
    И да, спасибу юзернейму poltergeist с форума python.su за кучу полезных советов!
    Метки:
    Поделиться публикацией
    Похожие публикации
    Реклама помогает поддерживать и развивать наши сервисы

    Подробнее
    Реклама
    Комментарии 28
    • +11
      Этих разговоров уже) Вступительных топиков — уйма, а когда дальше идешь, тогда проблемы и начинаются. Вы бы лучше поведали про использование QGraphicsView или про то, как вы структурируете свои проекты(все слоты пишете в одном классе? :) ).
      • +4
        Продолжение будет, обещаю, я уже начал его писать :) Фиксирую типовые вещи, которые обычно хочется реализовать в приложениях и описываю, как их сделал я. И нет, естественно, я не пишу все слоты в одном классе, иначе смысла в вынесении форм в отдельный файл не было бы никакого)
        • –1
          Спасибо за отличную статью, очень ждём продолжения более детального ;)
      • –2
        Прекрасный старт!
        Поведайте, пожалуйста, о деталях и тонкостях написания истинно кроссплатформенных GUI applications или там неужели в дейсвтительности всё гладко и можно не задумываться о том, что мой код будет компиляться под маком или виндами;
        • +2
          Ну, в питоне нет понятия «компилиться» и это уже плюс :) а для работы всех примеров достаточно установить бинарный пакет с сайта PyQt4. Ну или можете поступить совсем по-гиковски и собрать из исходников Sip и PyQt, установив MinGW — компилятор на винду. Если вам интересно — могу рассказать об этом на следующей посиделке.
          • 0
            Хорошо ;-) я имел ввиду, конечно, запускаться и работать; Я имею ввиду следующее: стоит ли мне заботиться о нюансах или можно написать приложение дать ссылку на svn, из которого пользователи могут скачивать мой код, далее python main.py и всё, и это одинаково хорошо и корректно работает на известных платформах (ну или на тех, где установлен кит); или мне придётся таки делать ос-специфичные билды?; в теории проблем не должно быть, но хочется узнать у человека, который непосредственно разрабатывал, как это на практике?

            ну а трансляцию в промежуточный код *.pyc можно также назвать компиляцией, хотя корректность этого утверждения под сомнением;
            • +1
              В данном случае все, что требуется для работы простого оконного приложения — это интерпретатор питона и, собственно, PyQt4. Но приложение, написанное c использованием последних версий PyQt4, не всегда будет хорошо работать на более старых. В частности, сейчас стандартом де-факто является версия не ниже 4.5. Таким образом, можно поступить несколькими способами — например, в описании к программе давать ссылку на закачку нужной версии, собирать все в один инсталлятор (например, exe для винды, собранный чем-то потипу Inno Setup, или deb-пакет для систем наподобие Debian GNU/Linux или Ubuntu, в котором все зависимости определяются и устанавливаются автоматически), ну или использовать вещи потипу py2exe, даром что собранные им Qt-приложения отлично работают под виндой без всяких дополнительных телодвижений. В остальном нюансов для Qt практически нет, все зависит именно от программиста и переносимости написанного им Python-кода. Но, повторюсь, при установленных интерпретаторе и PyQt4 проблем ни на какой системе быть не должно.
            • +3
              Не надо, пожалуйста, лучше обещанную практику. Тот, кто не сможет поставить под винду питон+pyqt+qt, безнадежен, куда уж ему программировать то?
          • +5
            Неплохая статья, радует что код грамотно подсвечен и не слишком перегружен деталями.

            Высказывания в первой части спорны. В частности вы выступаете портив 3-4 лишних генерируемых строк, забывая о том, что сам по себе PyQt содержит десятки тысяч строк на python и C++, не говоря уж о самом Qt. Но в любом случае стремление к чистоте похвально:) Не надо лишь делать столь категоричных выводов о том, что pyuic4 плохо.

            По поводу того, зачем класс интерфейса наследуется от object:
            Стиль кода Qt и рекомендации по стилю для Python программ очень отличаются. Вы не можете полностью отказаться от питон-стиля (т.к. он используется стандартной библиотеке), а так же не можете отказаться от стиля Qt (иначе вы потеряете связь с интерфейсом).

            В серьезных программах единство кода является серьезной проблемой. Разделение кода формы и генерируемого кода для этой формы на разные классы и разные файлы позволяют изолировать визуальный Qt-код от кода программы и свести использование Qt-стиля к минимуму, сохраняя единство и целостность проекта.

            Единственный минус, который вижу в статье — отсутствие выводов. Здесь, возможно, уместно добавить области использования PyQt и наоборот те случаи, когда его использование не целесообразно.

            Впрочем, коли вы обозначили стиль как беседу, то это простительно, но все равно могло бы значительно улучшить полезность текста.
            • +1
              Спасибо за подробный отзыв, замечания учел, думаю, в следующих топиках еще вернусь к обсуждению данной темы :)
            • +1
              оффтоп не совсем в тему, а для общего кругозора: www.pyside.org, Википедия,
              PySide — привязка языка Python к инструментарию Qt, совместимая на уровне API с PyQt, но, в отличие от последней, доступная для свободного использования как в открытых, так и закрытых (чем чаще всего являются коммерческие) проектах, поскольку лицензирована по LGPL.
              • +1
                Да, точно, хотел упомянуть в конце, но забыл) Все никак руки не дойдут попробовать.
              • 0
                Я почему-то всегда встречаю такой код для навешивания обработчиков:

                MainWindow.connect(self.btnHello, QtCore.SIGNAL('clicked()'), self.showChildWindow)

                А сам использую такой код:

                self.cb_scale.currentIndexChanged.connect(self.cbScaleChange)

                По-моему второй способ проще, но как-будто нигде не используется?..
                • НЛО прилетело и опубликовало эту надпись здесь
              • 0
                А прикручивал ui динамически. На знаю насколько это оправдано вообще в этом конкретном случае, но может быть весьма полезным для сложных приложений, где формы могут добавляться (ну типа 1С).

                И потом можно же использовать метод передачи формы с Qtscript в ответе сервера — может быть очень удобно.
                • НЛО прилетело и опубликовало эту надпись здесь
                  • +2
                    Сейчас пару строк со своей колокольни скажу:
                    Потихоньку кодю на родном для Qt языке C++ и могу сказать, что все эти PyQt PySide и так далее не слишком подходят для серьезных проектов. Это все можно применять в мелких графических утилитках, но писать на PyQt или PyGTK нечто большое и объемное это неоправданное безумие. Так как на выходе получится прожорливый монстрик. Более того скажу, что освоение C++/Qt4 не многим сложнее Питона.
                    Конечно мне возразят, что если писать на C++, то придется компилировать код под каждую платформу, а питонью программу можно просто запускать. Это да, но для серьезных проектов это не проблема. Также как и не проблема для маленьких проектов падение скорости даже в 1000 раз по сравнению с C++.
                    А те, кому нужна расширяемость приложения за счет скриптов, могут обратить внимание на обычный javascript, за счет обьектной модели, его можно легко и очень глубоко интегрировать в свое Qt приложение.
                    Но даже если этого мало, то скоро придет QML, который перевернёт все ваши представления о гуестроении. Фактически дни традиционных виджетов уже сочтены.
                    • 0
                      Согласен с предыдущими ораторами, что в большинстве случаев использование форм — скорее добро чем зло. Генерируемые файлы редактировать не надо, надо вызывать их setupUi из кода с логикой приложения.

                      Я же возражу по поводу toUtf — вы как вообще приложение переводить потом собрались? Ведь делается это так: с использованием pylupdate4 сканируется код на предмет вызовов QtGui.QApplication.translate, параметры этих вызовов собираются в единую базу и переводятся в Qt Linguist.
                      • 0
                        Я с вами согласен, но неужели вы сто процентов приложений пишете с поддержкой многоязычности? И потом, именно для того и существует функция retranslateUI — необязательно ковыряться с лингвистом, просто в случае крайней необходимости можно сделать подгрузку языкового файла из внешнего плагина, а функция уже любые строки переварит. Но если писать что-то большое и серьезное (хотя меня выше уже начали убеждать, что это почти невозможно :) ) с поддержкой кучи языков — тогда я с вами полностью согласен.
                        • +1
                          кстати для интересующихся темой — нашел на микрохабре отличную статейку по переводу в Qt с примерами на C++. Кому понадобится — переписать на питон легко, ибо фактически все одно и то же.
                          • 0
                            Если вы не собираетесь писать локализуемое приложение, то тем более непонятно зачем делать метод toUtf, то возможно проще писать так, да и работать будет явно быстрее:
                            MainWindow.setWindowTitle(QString.toUtf8("ХабраОкно 2.0"))

                            Следующий вопрос — редактирование автосгенерированного файла — это неверный подход. pyuic4 как я вижу написан с таким же рассчетом, что и оригинальная утилита uic4 из Qt4 SDK, т.е. код генерируется таким образом чтобы максимально отделить бизнес логику приложения от графического интерфейса пользователя. Пример: вы захотите поменять две кнопки местами, что для этого делать? Если вы оставите файл как есть, то просто в ui форме поменяете две кнопки местами и снова сгенерируете код на питоне, логику приложения менять не придется. В вашем же случае нужно будет либо а) править код построения gui руками б) заново генерировать код из ui файла и опять же приводить его к нужному виду. А теперь представим ситуацию, что надо не просто переставить две кнопки, но и добавить еще пару причем с хитрым расположением Layout'ов… Как показывает практика ручное редактирование такого кода довольно дорогое удовольствие…

                            PS извиняюсь за возможные ошибки синтаксиса — я пишу все таки не на питоне, а на c++ :)
                            • 0
                              ну я не то чтобы использую полностью ui-конвертированный код, я пишу на его основе свой (не без копипасты), однако ничего не мешает провести действия в дизайнере, опять сгенерить в кодовый файл одной командой и посмотреть, что необходимо изменить, это не так долго и неудобно, как может показаться.
                              А насчет QString.toUtf8() — этот метод я сам нашел буквально через час после написания топика :) Спасибо за совет, попробую. Все попробую!)
                              • 0
                                Извиняюсь, там fromUtf8 :)
                                что по поводу правок ручных — у меня в проекте некоторые файлы, сгенерированные на основе ui достигают в размере до полутора тысяч строк — думается такое руками править довольно сложно, не находите?
                                • 0
                                  >ну я не то чтобы использую полностью ui-конвертированный код, я пишу на его основе свой (не без копипасты), однако ничего не мешает провести действия в дизайнере, опять сгенерить в кодовый файл одной командой и посмотреть, что необходимо изменить, это не так долго и неудобно, как может показаться.

                                  После тысячной правки у вас мнение поменяется на противоположное, к тому же ui файлы помогают лучшей кастомизации. Для обучения может и сгодится, но для чего-то серьезного только ui файлы.
                                • 0
                                  Если вы не собираетесь писать локализуемое приложение, то тем более непонятно зачем делать метод toUtf, то возможно проще писать так, да и работать будет явно быстрее:
                                  MainWindow.setWindowTitle(QString.toUtf8("ХабраОкно 2.0"))


                                  А можно еще проще — MainWindow.setWindowTitle(u"ХабраОкно 2.0")

                                  PyQt отлично умеет мэппить unicode в QString :-)
                                  • 0
                                    PyQt-то умеет, но у такой программы, как ни странно, возникают проблемы при запуске под виндой. В частности, я столкнулся с тем, что виндовый питон начал ругаться на DecodeError после сборки программы с помощью py2exe.
                            • 0
                              Не очень понял, чем вам uic не угодил. Код, который он генерит не нужно изменять. Его даже читать не нужно, это код «оживляющий» xml-представление формы, которое создается в дизайнере.

                              Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.