В этой главе мы поговорим о:
- создании списков в различных стилях
- работа с элементами в списке
- реакции на выбор пользователем элемента из списка
- редактирование меток и сортировка списка
- создание большого списка (large list)
13.1 Создание элемента управления list
- иконка (плитка)
- маленькая иконка (значки)
- список
- отчёт
13.1.1 Что такое режим «иконка»
Код для отображения этого примера приведён в листинге 13.1. Обратите внимание, что этот код зависит от некоторых .png файлов, которые расположены в той же папке, что и наш модуль. Эти файлы можно получить с сайта этой книги.
![]() |
| Рисунок 13.1 Простой список в режиме «иконка» |
В листинге 13.1 демонстрационный фрейм создаёт список изображений (image list), который содержит ссылки на изображения, которые мы будем отображать; затем мы создаём и заполняем элемент list. Список изображения мы обсудим позже в этой главе.
13.1.2 Что такое режим «маленькая иконка»
![]() |
| Рисунок 13.2 Простой list в режиме «маленькая иконка» |
13.1.3 Что такое режим списка (list mode)?
13.1.4 Что такое режим отчёта (report mode)?
![]() |
| Рисунок 13.4 Простой list в режиме «отчёт» |
Этот режим настолько отличается от режима «иконка», что стоит привести код, который нужен для создания такого отображения:
import wx
import sys, glob, random
import data
class DemoFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1,
«wx.ListCtrl in wx.LC_REPORT mode»,
size=(600,400))
il = wx.ImageList(16,16, True)
for name in glob.glob(«smicon??.png»):
bmp = wx.Bitmap(name, wx.BITMAP_TYPE_PNG)
il_max = il.Add(bmp)
# Создаём list
self.list = wx.ListCtrl(self, -1, style=wx.LC_REPORT)
self.list.AssignImageList(il, wx.IMAGE_LIST_SMALL)
# Добавляем колонки
for col, text in enumerate(data.columns):
self.list.InsertColumn(col, text)
# Добавляем строки
for item in data.rows:
index = self.list.InsertStringItem(sys.maxint, item[0])
for col, text in enumerate(item[1:]):
self.list.SetStringItem(index, col+1, text)
# даём каждому элементу случайное изображение
img = random.randint(0, il_max)
self.list.SetItemImage(index, img, img)
# Настраиваем ширину колонок
self.list.SetColumnWidth(0, 120)
self.list.SetColumnWidth(1, wx.LIST_AUTOSIZE)
self.list.SetColumnWidth(2, wx.LIST_AUTOSIZE)
self.list.SetColumnWidth(3, wx.LIST_AUTOSIZE_USEHEADER)
app = wx.PySimpleApp()
frame = DemoFrame()
frame.Show()
app.MainLoop()
13.1.5 Как я могу создать list?
| Стиль | Описание |
|---|---|
| wx.LC_ICON | Режим «иконка» |
| wx.LC_LIST | Режим «список» |
| wx.LC_REPORT | Режим «отчёт» |
| wx.LC_SMALL_ICON | Режим «маленькая иконка» |
| Стиль | Значение по умолчанию |
|---|---|
| wx.LC_HRULES | Рисуе т линии между строками list'a |
| wx.LC_NO_HEADER | Не отображает заголовки колонок |
| wx.LC_VRULES | Рисует линии между колонками list'a |
В отличие от остальных виджетов, которые мы видели до сих пор, list имеет ещё несколько методов, которые позволяют Вам изменить его флаги стилей уже в процессе выполнения. Метод SetSingleStyle(style, add=True) позволяет Вам добавить или удалить один флаг стиля, в зависимости от параметра add. Вызов listCtrl.SetSingleStyle(LC_HRULES, True) добавит горизонтальную линейку, тогда как listCtrl.SetSingleStyle(LC_HRULES, False) удалит её. Вызов SetWindowStyleFlag(style) позволяет Вам переустановить весь стиль, задав новые флаги, например, SetWindowsStyleFlag(LC_REPORT | LC_NO_HEADER). Эти методы полезны для изменения list'a на лету.
13.2 Управление элементами в list'e
13.2.1 Что такое image list и как к нему добавлять изображения
Создание image list
image list является экземпляром wx.ImageList и для его создания используется следующий конструктор:
wx.ImageList(width, height, mask=True, initialCount=1)
Параметры width и height задают в пикселях размер изображения, которое будет добавлено в list. Изображения больше, чем указанный размер добавить нельзя. Если параметр mask = True, nj изображение рисуется с его маской, если она у него есть. Параметр initialCount задаёт начальный внутренний размер list'a. Если Вы знаете, что list будет большой, то указание этого размера может сэкономить память и время позже.
Добавление и удаление изображений
for name in glob.glob(«icon??.png»):
bmp = wx.Bitmap(name, wx.BITMAP_TYPE_PNG)
il_max = il.Add(bmp)
Django 1.5 и Python 3 (Перевод)
Автор: Ishayahu Lastov
Исключения в Питоне
Поговорим об исключениях.
Всё нижеизложенное относится к Python 3.3, хотя отчасти справедливо и для более ранних версий.
Для чего они применяются, наверное, все и так прекрасно знают: для передачи сообщений об ошибках внутри программы.
Рассмотрим простейший пример: открытие файла. Если всё нормально — open(filename, 'r') возвращает объект этого самого файла, с которым можно делать всякие полезные вещи: читать из него данные и т.д.
Если файл не может быть открыт — выбрасывается исключение:
try:
f = open(filename, 'r')
try:
print(f.read())
finally:
f.close()
except OSError as ex:
print("Cannot process file", filename, ": Error is", ex)
Открываем файл и печатаем его содержимое.
Обратите внимание: файл нужно не только открыть но и закрыть после использования. Исключение может выбросить open (например, если файла нет на диске или нет прав на его чтение).
Если файл открыт — читаем его через f.read(). Этот вызов тоже может выбросить исключение, но файл закрывать всё равно нужно. Поэтому необходим блок finally: f.close() должен быть вызван даже если f.read() сломался. В этом месте удобней было бы воспользоваться конструкцией with но мы же сейчас говорим об исключениях а не о контекстных менеджерах, верно?
Исключения из обоих мест попадут в except OSError, где можно будет что-то сделать с ошибкой.
Питон делает явный выбор в пользу исключений перед возвратом кода ошибки в своём ядре и стандартной библиотеке. Прикладному программисту стоит придерживаться этих же правил.
Введение закончено. Теперь сконцентрируемся на том что происходит в except.
Типы исключений
Все исключения составляют иерархию с простым наследованием. Вот простой небольшой кусочек от довольно обширного набора исключений ядра Питона:
BaseException
+-- SystemExit
+-- KeyboardInterrupt
+-- GeneratorExit
+-- Exception
+-- StopIteration
+-- AssertionError
+-- AttributeError
+-- BufferError
Самый базовый класс — BaseException. Он и его простые потомки (SystemExit, KeyboardInterrupt, GeneratorExit) не предназначены для перехвата обыкновенным программистом — только Питон и редкие библиотеки должны работать с этими типами. Нарушение правила ведет, например, к тому что программу невозможно корректно завершить — что совсем не хорошо.
Также не нужно перехватывать все исключения:
try:
...
except:
...
работает как
try:
...
except BaseException:
...
Всё, что может быть нужно программисту — это Exception и унаследованные от него классы.
Вообще-то лучше ловить как можно более конкретные классы исключений. Например, в случае с файлом это OSError или даже может быть FileNotFoundError. Таким образом мы не перехватим AttributeErrorили ValueError, которые в этом примере означали бы ошибку или опечатку программиста.
Кстати, обратите внимание: StopIteration порожден от Exception а GeneratorExit от BaseException. Подробности, почему сделано именно так, можно найти в PEP 342.
Цепочки исключений
Прочитав предыдущую главку все прониклись необходимостью указывать правильный класс исключений и пообещали никогда не использовать BaseException.
Идем дальше. Следующий пример:
try:
user = get_user_from_db(login)
except DBError as ex:
raise UserNotFoundError(login) from ex
Получаем пользователя из базы данных чтобы что-то потом с ним сделать. get_user_from_db может выбросить ошибку базы данных. Для нас это скорее всего означает что такого пользователя нет. Но для логики приложения полезней наш собственный тип UserNotFoundError с указанием логина проблемного пользователя, а не обезличенная ошибка БД — что мы и выбрасываем в обработчике исключения.
Проблема в том, что программисту часто хотелось бы знать, а почему это пользователь не найден. Например, чтобы сохранить в логах для дальнейшего разбирательства.
Для таких целей служит конструкция raise ... from ....
По PEP 3134 у объекта исключения имеется несколько обязательных атрибутов.
В первую очередь это __traceback__, содержащий кусочек стека от места возникновения исключения до места его обработки.
Затем — __context__. Если исключение было создано в ходе обработки другого исключения (выброшено из except блока) — __context__будет содержать то самое породившее исключение. Которое, в свою очередь тоже может иметь установленный __context__. Этот атрибут равен None если наше исключение — самое первое и н
е имеет предшественников.
__context__ устанавливается автоматически.
В отличие от контекста __cause__ устанавливается только если исключение было выброшено конструкцией raise ... from ... и равно значению from.
Если исключение выбрасывалось простым raise ... то __cause__ будет равно None в то время как __context__ всегда будет содержать породившее исключение если оно существует.
Для вывода исключения со всей информацией служит набор функций из модуля traceback, например traceback.print_exc().
И тут тоже есть проблема: печатается либо явная цепочка если есть установленный __cause__ или неявная, тогда используется __context__.
Иногда программисту может быть нужно отбросить породившие исключения как не имеющие смысла при выводе traceback. Для этого появилась форма записи
raise exc from None
PEP 409 и PEP 415 рассказывают как это работает:
У исключения всегда есть атрибут __supress_context__. По умолчанию он равен False.
Конструкция raise ... from ... записывает fromв __cause__ и устанавливает __supress_context__ в True.
Тогда семейство функций traceback.print_exc() печатают цепочку если явно указан (не равен None) __cause__ или есть __context__ и при этом __supress_context__ равен False.
Изложение получилось несколько длинным, но сократить текст без потери смысла у меня не вышло.
Семейство OSError
Последняя проблема о которой хотелось бы рассказать — это типы исключений порожденные вызовами операционной системы.
До Python 3.3 существовало много разных типов таких исключений: os.error, socket.error, IOError, WindowsError, select.errorи т.д.
Это приводило к тому, что приходилось указывать несколько типов обрабатываемых исключений одновременно:
try:
do_something()
except (os.error, IOError) as ex:
pass
Ситуация на самом деле была еще хуже: очень легко забыть указать еще одно нужное исключение, которое может внезапно прилететь. Дело в том что исключения операционной системы часто никак не проявляют себя при разработке. На машине программиста всё работает отлично и он не подозревает о возможных проблемах. Как только программа выходит в production пользователь вдруг ловит что-то неожиданное и программа аварийно завершается. Все опечалены.
Проблема решена в PEP 3151: весь этот зоопарк теперь является псевдонимами для OSError. Т.е. пишите OSError и не ошибетесь (прочие имена оставлены для обратной совместимости и облегчения портирования кода на новую версию).
Давайте рассмотрим ещё один аспект исключений, порожденных операционной системой.
У OSError есть атрибут errno, который содержит код ошибки (список всех возможных символьных констант для ошибок можно посмотреть в модуле errno).
Открываем файл, получаем OSError в ответ. Раньше мы должны были анализировать ex.errno чтобы понять, отчего произошла ошибка: может файла нет на диске, а может нет прав на запись — это разные коды ошибок (ENOENT если файла нет и EACCES или EPERM если нет прав).
Приходилось строить конструкцию вроде следующей:
try:
f = open(filename)
except OSError as ex:
if ex.errno == errno.ENOENT:
handle_file_not_found(filename)
elif ex.errno in (errno.EACCES, errno.EPERM):
handle_no_perm(filename)
else:
raise # обязательно выбрасывать не обработанные коды ошибки
Теперь иерархия расширилась. Привожу полный список наследников OSError:
OSError
+-- BlockingIOError
+-- ChildProcessError
+-- ConnectionError
| +-- BrokenPipeError
| +-- ConnectionAbortedError
| +-- ConnectionRefusedError
| +-- ConnectionResetError
+-- FileExistsError
+-- FileNotFoundError
+-- InterruptedError
+-- IsADirectoryError
+-- NotADirectoryError
+-- PermissionError
+-- ProcessLookupError
+-- TimeoutError
Наш пример можем переписать как:
try:
f = open(filename)
except FileNotFound as ex:
handle_file_not_found(filename)
except PermissionError as ex:
handle_no_perm(filename)
Гораздо проще и понятней, правда? И меньше мест, где программист может ошибиться.
Заключение
Переходите на Python 3.3, если можете. Он хороший и облегчает жизнь.
Новые плюшки в вопросе, касающемся исключений, я показал.
Если использовать новый питон не позволяют обстоятельства — пишите на чём есть, но помните как правильно это делать.
Автор: Andrew Svetlov
PyBooklet: создание PDF с двумя страницами на листе для печати (Перевод)
Автор: Ishayahu Lastov
Документация South — Перевод. Часть 3: Дополнительные команды и миграция данных
Последовательная работа с миграцией
class Group(models.Model):
name = models.TextField(verbose_name="Name")
facebook_page__id = models.CharField(max_length=255)
./manage.py schemamigration southtut --auto
./manage.py migrate southtut
./manage.py schemamigration southtut --auto --update
+ Added model southtut.Group
Migration to be updated, 0026_auto__add_group, is already applied, rolling it back now...
previous_migration: 0025_auto__foo (applied: 2012-05-25 21:20:47)
Running migrations for southtut:
- Migrating backwards to just after 0025_auto__foo.
< partner:0026_auto__add_group
Updated 0026_auto__add_group.py. You can now apply this migration with: ./manage.py migrate southtut
./manage.py migrate southtut
Просмотр текущих миграций
$ ./manage.py migrate --list
southtut
(*) 0001_initial
(*) 0002_auto__add_field_knight_dances_whenever_able
(*) 0003_auto__add_field_knight_shrubberies
(*) 0004_auto__add_unique_knight_name
Перенос данных
- Создаём две новые колонки: password_salt и password_hash (миграция схемы)
- Используя содержимое колонки password вычисляем соль и хеш для каждого пользователя (миграция данных)
- Удаляем старую колонку password (миграция схемы)
from django.db import models
class User(models.Model):
username = models.CharField(max_length=255)
password = models.CharField(max_length=60)
name = models.TextField()
Документация South — Перевод. Часть 2. Более сложные изменения схемы
Теперь, когда мы знаем как обслуживать простые изменения модели, давайте посмотрим на более сложные случаи, которые Вы так же можете обработать при помощи South.
Значение по умолчанию
from django.db import models
class Knight(models.Model):
name = models.CharField(max_length=100)
of_the_round_table = models.BooleanField()
dances_whenever_able = models.BooleanField()
shrubberies = models.IntegerField(null=False)
./manage.py schemamigration southtut --auto
? The field 'Knight.shrubberies' does not have a default specified, yet is NOT NULL.
? Since you are adding or removing this field, you MUST specify a default
? value to use for existing rows. Would you like to:
? 1. Quit now, and add a default to the field in models.py
? 2. Specify a one-off value to use for existing columns now
? Please select a choice:
? Please select a choice: 2
? Please enter Python code for your one-off default value.
? The datetime module is available, so you can do e.g. datetime.date.today()
>>> 0
+ Added field shrubberies on southtut.Knight
Created 0003_auto__add_field_knight_shrubberies.py. You can now apply this migration with: ./manage.py migrate southtut
$ ./manage.py migrate southtut
Running migrations for southtut:
- Migrating forwards to 0003_auto__add_field_knight_shrubberies.
> southtut:0003_auto__add_field_knight_shrubberies
- Loading initial data for southtut.
Уникальные значения
from django.db import models
class Knight(models.Model):
name = models.CharField(max_length=100, unique=True)
of_the_round_table = models.BooleanField()
dances_whenever_able = models.BooleanField()
shrubberies = models.IntegerField(null=False)
$ ./manage.py schemamigration --auto southtut
+ Added unique constraint for ['name'] on southtut.Knight
Created 0004_auto__add_unique_knight_name.py. You can now apply this migration with: ./manage.py migrate southtut
$ ./manage.py migrate southtut
Running migrations for southtut:
- Migrating forwards to 0004_auto__add_unique_knight_name.
> southtut:0004_auto__add_unique_knight_name
- Loading initial data for southtut.
Поля ManyToMany
Пользовательские поля
Ещё?
Автор: Ishayahu Lastov



