пятница, 14 октября 2011 г.

Очистка значений внешних ключей в модели

В предыдущем посте я благополучно боролся с отбрасыванием записей, содержащих пустые внешние ключи. Практически сразу возникла в некотором роде обратная задача - уметь очищать поля внешних ключей в модели, как в коде, так и из интерфейса (я встречал мнение, что пустые внешние ключи - грубая ошибка проектирования и этого надо всячески избегать; интересно, в каком мире эти люди живут?).

Сначала рассмотрим более сложную задачу - очистка поля внешнего ключа модели из пользовательского интерфейса. Для редактирования внешних ключей я использую QComboBox, связанный посредством QDataWidgetMapper и QSqlRelationalDelegate c QSqlRelationalTableModel. К сожалению, родной комбобокс не умеет очищать значение, поэтому придется его научить. Пусть он очищается по нажатию кнопки Delete. Очевидно, что комбобокс очистится, если установить его текущий индекс в -1. Напишем виджет:

class ResettableComboBox(QComboBox):

    def keyPressEvent(self, event):
        if event.key() == Qt.Key_Delete:
            self.setCurrentIndex(-1)
        else:
            QComboBox.keyPressEvent(self, event)

и в дальнейшем будем использовать именно его.

К сожалению, это работает совсем не так, как ожидалось. Комбобокс действительно благополучно очищается, но при потере фокуса в модель пустое значение не попадает, восстанавливается предыдущее. В чем же дело?

пятница, 7 октября 2011 г.

Некорректный SQL-запрос SELECT в QSqlRelationalTableModel

Было замечено, что в QTableView, настроенном на QRelationalTableModel отображает не все строки таблицы, в частности строки, где значение хотя бы одного поля внешнего ключа (поля, у которых есть QRelation) равно NULL. Более того, оказалось, что эти строки отсутствуют и в модели.

Исследование показало, что QRelationalTableMode.selectStatement возвращает запрос следующего вида:
SELECT tbl.fld1,
       relTblAl_1.fld1 AS reftbl1_fld1_2,
       relTblAl_2.fld1,
       relTblAl_3.fld1,
       tbl.fld2,
       tbl.fld3,
FROM   tbl,
       reftbl1 relTblAl_1,
       reftbl1 relTblAl_2,
       reftbl2 relTblAl_3
WHERE  ( tbl.fkfld1 = relTblAl_1.pkfld
         AND tbl.fkfld2 = relTblAl_2.pkfld
         AND tbl.fkfld3 = relTblAl_3.pkfld )
ORDER  BY tbl.sortfld ASC 

Это INNER JOIN, который действительно не выбирает строки с пустыми ссылками. Выход напрашивается - переписать selectStatement, что бы он возвращал правильный запрос, использующий LEFT JOIN. Тогда выберутся все строки таблицы.

среда, 28 сентября 2011 г.

Тюнинг вкладок QMdiArea

Главное окно проекта должно быть многодокументным и в соответствии с современным стандартом де-факто хочется отображать дочерние окна на отдельных вкладках. Для этого в качестве центрального виджета главного окна нужно использовать QMdiArea и установить режим отображения TabbedView

from PyQt4.QtGui import QMainWindow, QMdiArea

class MainWnd(QMainWindow):
    def __init__(self, parent=None):
        QMainWindow.__init__(self, parent)
        self.setCentralWidget(QMdiArea(self))
        self.centralWidget().setViewMode(QMdiArea.TabbedView)

Все бы хорошо, но добавив одну-две вкладки начинаешь понимать, что вещь из коробки снова придется допиливать напильником:

понедельник, 6 июня 2011 г.

Мелкие грабли при работе с PyWin32

Первые грабли


Возникла необходимость написать простую службу Windows. Для этой цели была избрана известная библиотека PyWin32. На просторах интернета было найдено несколько релевантных примеров и быстренько слеплена служба. Тут я напоролся на первую проблему.

суббота, 2 апреля 2011 г.

Тестирование пользовательского интерфейса в PyQt

Мне всегда была интересна тема автоматизации тестирования GUI, ибо задача, мягко скажем нетривиальная. А сейчас возник практический интерес к тестированию UI приложений на PyQT.

Как выяснилось, разработчики впихнули в QT целый фреймворк для автоматического тестирования, который в том числе умеет и эмулировать внешние действия пользователя на интерфейсе. Это называется QtTestLib. Другие разработчики не стали все это добро целиком тащить в PyQt, так как для питона и так предостаточно библиотек тестирования. А вот механизм эмуляции внешних телодвижений пользователя перенесли, спасибо.

Итак PyQt4.QtTest. Что это такое и с чем его едят читаем в документации. А я расскажу, как тыкал это пальцем на конкретном примере.

среда, 9 июня 2010 г.

Поиск похожих участков в питоновском коде

Когда-то довольно давно отложилось в памяти, что существует полезный инструмент, способный качественно найти в коде на python похожие куски для их дальнейшего рефакторинга.

Вчера выдалась относительно свободная минута, и я решил его таки найти. После долгих поисков наконец нарыл. Итак, знакомимся - CloneDigger

четверг, 27 мая 2010 г.

PyQt: Добавление записей в связанную модель (relationModel) во время редактирования записи основной

Абстрактная задача


Существует таблица main, содержащая помимо прочего поле-ссылку на справочник classifier_id. Код на sqlite:
CREATE TABLE classifier
(
    id INTEGER NOT NULL,
    name TEXT
);

CREATE TABLE main 
(
    id INTEGER NOT NULL,
    name TEXT,
    classifier_id INTEGER REFERENCES classisfier (id)
);

Необходимо написать на PyQt диалог редактирования записи таблицы main с возможностью добавления новых записей в таблицу classifier в процессе редактирования.
Модель для main - QSqlRelationalTableModel, добавлена связка c третьим полем (индекс 2) QRelation('classifier', 'id', 'name').
Для редактирования поля classifier_id необходимо использовать QComboBox, виджеты диалога связываются с редактируемой моделью с помощью QDataWidgetMapper (SubmitPolicy = ManualSubmit)

суббота, 8 мая 2010 г.

VirtualBox

Недавно потребовалось протестировать программку на чистой Windows XP. Дома на машине стоит Windows 7 x64, поэтому пришлось посмотреть в сторону виртуальных машин. На слуху как-то больше VMWare, но мне она почему-то не нравится. Остались от нее какие-то смутные негативные впечатления, когда игрался несколько лет назад.

Кто-то из знакомых в разговоре вскользь упомянул VirtualBox, причем хорошо отозвался. Решил попробовать и я.

четверг, 29 апреля 2010 г.

Детальные метаданные sqlite3

В одном из предыдущих постов я описывал, как получить список таблиц в базе sqlite, и сокрушался, что непонятно, как получить детальную информацию о них.

Знающие люди ткнули носом в раздел документации sqlite, где описан набор прагм (pragma) для получения исчерпывающей информации о структуре базы. Подробно расписывать не буду, разработчики sqlite прекрасно сделали это сами.

Как выяснилось, эта метаинформация может быть довольно полезной при разработке интерфейсов к базе данных.

Нюансы разработки GUI базы данных на PyQt

При разработке интерфейса к базе данных на PyQt существует несколько моментов, за которыми нужно следить, чтобы программа вела себя соответственно ожиданиям. Их немного и они, в общем-то, логичны, но с непривычки что-то можно упустить и долго ломать голову, почему нихрена не работает.

Мне попортили определенное количество крови следующие вещи:

суббота, 24 апреля 2010 г.

PyQt: Генерация кода по ресурсу интерфейса

В подавляющем большинстве инструментов для создания GUI, которые мне доводилось использовать, для нормальной работы программы положено из ресурса некоторым образом сгенерировать код. Естественно, полученную рыбу надо наполнить прикладной логикой.

Веселуха начинается, когда возникает необходимость изменить ресурс. Путей при этом два: описания новых контролов ресурса добавлять в ранее сгенерированный код руками по образу и подобию существующих или понадеяться на корректную перегенерацию.

Метаданные sqlite3

Как выяснилось, у sqlite очень просто получить список существующих таблиц, индексов, представлений и триггеров.

Глюк стандартных SQL-моделей PyQt4

При написании простенькой базы данных (PyQt4+sqlite) наткнулся на неприятный глюк связки QSqlTableModel и QTableView: при добавлении во вьюху новых строк активна всегда только первая строка и в базу ничего не пишется.

Выяснилось, что PyQt ни при чем, проблему создавало еще одно соединение с базой средствами стандартного питоновского модуля sqlite3, использовавшееся вспомогательным функционалом. После перевода функционала на соединение средствами PyQt все заработало.

Глубоко копаться в причинах конфликта не стал, вероятно что-то с блокировками по умолчанию в разных реализациях, проще было избавиться от зоопарка соединений.