Archive for the ‘программирование’ Category

py2exe: win32con missing module

Tuesday, February 16th, 2010

Hi everybody,

Yesterday I tried to turn my python code into win32 executable with py2exe utility and faced with problem: py2exe was unable to find win32con module with error:

The following modules appear to be missing ['win32con']

In a couple of hours I googled next solution: one should simply install Python for Windows extensions. This fixed everything.

QtWebKit – turn javascript and images off

Thursday, June 18th, 2009

How to turn javascript and images off in QWebView?

QWebView *myWebView = new QWebView(this);
....
myWebView->settings()->setAttribute(QWebSettings::JavascriptEnabled, false);
myWebView->settings()->setAttribute(QWebSettings::AutoLoadImages, false);

That’s all :)

QWhatsThis – fighting with wordwrap

Saturday, June 13th, 2009

Another problem with QWhatsThis::showText() method: word wrapping. If you want to turn it off, you’d probably face with some problems: it’s not as easy as it supposed to be.

I’ve found the solution that satisfied me: instead of calling

QWhatsThis::showText(myPoint, "blah blah...", ...);

I frame “blah blah …” text into table with <nobr> tags and use this call:

QWhatsThis::showText(myPoint, "<table><tr><td><nobr>blah blah...</nobr></td></tr></table>", ...);

This solved my problems.

Qt: QWhatsThis – handling clicks

Saturday, June 13th, 2009

QWhatsThis::showText() shows beautiful hint where one can place HTML formatted text. The question is: how we can handle link clicks, like <a href=”more”>more…</a>?

The answer is located in this thread: one should re-implement QWidget::event() method for “parent” widget (3rd param in QWhatsThis::showText() method), like:

void MyWidget::event(QEvent *_event)
{
    if (_event->type() == QEvent::WhatsThisClicked){
        if (QWhatsThisClickedEvent* clicked = dynamic_cast(_event)){
            QString href = clicked->url(); // here is the "href" value of the link user clicked
            ...........
        }
    }
    return QWidget::event(_event);
}

So, when user clicks on the <a href> link, MyWidget::event() fires, extracts the “url” and handles it.

If you don’t want to re-implement the QWidget::event() method, you could use installEventFilter().

Сапер: какова вероятность того, что поле откроется?

Sunday, December 14th, 2008

Кто играл в “сапера”, тот знает, что бывают ситуации, когда логически вычислить положение следующей мины/пустой клетки невозможно. Я задался вопросом: “А какова вообще вероятность того, что поле откроется, если открывать его настолько аккуратно, насколько это возможно (исключить человеческий фактор)?” Очевидно, что такая цифра существует. Собственно эта статья посвящается тому, как мне удалось приблизиться к этим числам. Сначала сразу приведу результаты, чтобы не напрягать лишний раз людей, которые зашли сюда только за ними :)

Результаты:

Сразу скажу, что эти числа получены в ходе экспериментов и научную ценность могут представлять только после теоретического основания, если кто-нибудь когда-нибудь решит его сделать; кроме того возможны погрешности. Это всего лишь попытка найти их. Тем не менее:

Новичок (поле 8×8, 10 мин), первый клик в углу: вероятность 75%;

Новичок, первый клик в центре: вероятность 67.7%.

Любитель (поле 16×16, 40 мин), первый клик в углу: вероятность 66.1%

Любитель, первый клик в центре: вероятность 62.9%

Профессионал (30×16, 99 мин), первый клик в углу: 23.1%

Профессионал, первый клик в центре: 22.9%

Как результаты были получены:

Как я уже писал, результаты экспериментальные. Поскольку постановка звучала так: “Какова вероятность того, что самый аккуратный/лучший/идеальный игрок в мире откроет поле размером NxM с K минами, если на первом клике подорваться невозможно”, то для начала был сделан алгоритм, который достаточно хорошо (близок к идеальному) открывает данное ему поле. После этого, для каждого вида поля я создавал 10000 досок и давал ему их решать. Число успешно открытых им досок деленное на 100 и бралось в качестве претендента для искомой вероятности.

Описание “близкого к идеальному” алгоритму, который использовался в эксперименте:

Алгоритм итерационный:

- Если на i-м шаге можно со 100% уверенностью определить, что в какой-то ячейке есть мина, то алгоритм помечает ее как мину. Пример ситуации: вокруг единички все ячейки открыты, одна закрытая. Значит там мина:)

- Если на i-м шаге можно со 100% уверенностью определить, что какая-то ячейка пустая, то алгоритм “кликает” на нее. Пример: единичка, возле нее установлена “мина”. Значит алгоритм кликает на все оставшиеся соседние с этой единичкой ячейки.

- Если на i-м шаге нельзя со 100% уверенностью определить, где мина есть, а где ее нет, то алгоритм 50 раз случайным образом расставляет оставшиеся мины так, чтобы ситуация на доске была допустимой. То есть он не будет возле открытой единички ставить 3 мины. После этого он анализирует эти 50 возможных расстановок, находит клетки, где мины встречались часто, где редко (среди этих 50-и расстановок), то есть находит клетки, в которых наибольшая вероятность мины (или ее отсутствия), и ставит туда мину (или кликает).

Как видно, алгоритм имитирует человека, который играет “нечестно”: использует программу, которая на каждом шаге вычисляет для него вероятность того, где мины есть, а где их нет. Если он не идеален, то ИМХО весьма хорош:)

Зависимость вероятности успеха (открыть поле) от плотности мин:

Для поля размером 8×8 я посчитал вероятности для любого количества мин (те успех когда на поле 0 мин, 1 мина, 2 мины, … 62 мины). Вот как для этого поля выглядит график зависимости вероятность успеха от плотности мин:

Выводы:

Кроме полученных чисел можно заметить, что вероятность так же зависит от начального клика, лучше начинать открывать с угла:)

Еще есть такой ресурс sweepmines.com, на котором предлагают открыть поле 8×8, 12 мин, в случае успеха возвращают удваивоенную ставку. Так вот, вероятность открыть такое поле — 57% (если с угла). Так что в принципе можете попытаться “обмануть” ресурс и заработать бабла. Но я бы не рискнул :)

Приложения:

численные результаты экспериментов (для всех плотностей мин на новичке, до 44 мин на любителе, немного на профи);

исходники алгоритма (на питоне), который использовался для эксперимента;

программа, которая помогает “открывать” поле простому игроку, показывая вероятности нахождения/отсутствия мины (питон + pyWidgets). Надеюсь Rogen не обидится (очень пригодится тем, кто участвует в рейтинге “по плотности мин” на minesweeper.ru :) )

Ссылающиеся друг на друга объекты в питоне: возможные последствия

Sunday, December 7th, 2008

В питоне объект уничтожается (и вызывается его деструктор) только тогда, когда на него ничего не ссылается. Что делать, когда объект A ссылается на объект B, а объект B в свою очередь ссылается на объект A? В этом случае деструкторы этих объектов никогда не будут вызваны. В частности, этот код

import sys
print(sys.version)

class C(object):
    def __del__(self):
        print("Die C!")

class D(object):
    def __del__(self):
        print("Die D!")

c = C()
d = D()

выведет

2.5.2 (r252:60911, Apr 21 2008, 11:12:42)
[GCC 4.2.3 (Ubuntu 4.2.3-2ubuntu7)]
Die C!
Die D!
,
но если мы вконец допишем

c.d = d
d.c = c

и запустим, то получим


2.5.2 (r252:60911, Apr 21 2008, 11:12:42)
[GCC 4.2.3 (Ubuntu 4.2.3-2ubuntu7)]
.
Все вышенаписанное справедливо как для 2.x ветки, так и для python 3000.

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

Наверое самый известный ОО паттерн: синглтон. Давайте реализуем его на питоне:

import sys

print(sys.version)

class Singleton(object):
	instance = None
	def __new__(type, *args, **kwargs):
		if type.instance == None:
			type.instance = object.__new__(type, *args, **kwargs)
		return type.instance

class A(Singleton):
       def __del__(self):
		print ("A's destructor is called!")

aobj = A()
bobj = A()

print str(aobj)
print str(bobj)

, результат будет:

2.5.2 (r252:60911, Apr 21 2008, 11:12:42)
[GCC 4.2.3 (Ubuntu 4.2.3-2ubuntu7)]
<__main__.A object at 0xb7d8a04c>
<__main__.A object at 0xb7d8a04c>

aobj и bobj указывают на один и тот же объект, но в конце работы программы деструктор для него не вызывается. Возникает естественный вопрос: “Почему”?

В питоне тип (класс) – тоже объект, чем мы и воспользовались для реализации синглтона: когда мы первый раз создали объект класса A, мы присвоили свойству instance класса(-объекта) A ссылку на этот объект. Все логично, но мы забыли об одной вещи: у объекта aobj есть свойство __class__, которое указывает на класс объекта. В нашем случае это класс A.

То есть получается следующее: aobj.__class__ указывает на A, а A.instance указывает на aobj, ссылающиеся друг на друга объекты a и A, их деструкторы никогда не будут вызываны.

Добьем наш пример:

import sys

print(sys.version)

class Singleton(object):
	instance = None
	def __new__(type, *args, **kwargs):
		if type.instance == None:
			type.instance = object.__new__(type, *args, **kwargs)
			Singleton_Destructor.types.append(type)
		return type.instance

class Singleton_Destructor:
	types = []
	def __del__(self):
		for i in self.__class__.types:
			del i.instance

sd = Singleton_Destructor()

class A(Singleton):
	def __del__(self):
		print ("A's destructor is called!")

a = A()
b = A()

print(str(a))
print(str(b))

вернет долгожданный

[GCC 4.2.3 (Ubuntu 4.2.3-2ubuntu7)]
<__main__.A object at 0xb7bb430c>
<__main__.A object at 0xb7bb430c>
A's destructor is called!

C#: декомпозиция (разбиение) сложных форм на части.

Friday, September 12th, 2008

Рассмотрим простую задачу: есть форма, на ней дерево (TreeView) и groupBox, при клике на ноду дерева необходимо отображать какие-то специфичные для этой ноды свойства в groupBox-е – набор контроллов.

Пусть нод много, и их свойства существенно различны: для кого-то это радиокнопки, для кого-то textbox-ы, для кого-то сложные навороты с scrollbar-ами итдитп (те все ноды разбиты на «виды», для каждого вида – свои специфичные свойства).

Способы реализации задачи на C# (WinForms):

1. Нарисовать форму, treeView и groupBox в дизайнере, при клике на ноду динамически генерить контролы для свойств и класть их в groupBox. Недостатки такого подхода очевидны: не используется мощь дизайнера, задача грозит превратиться в огромную рутину и головную боль.

2. Нарисовать groupBox-ы с контролами для всех видов нод в дизайнере, при выборе определенной ноды показывать нужный. Этот подход уже более интересный чем 1-й, но и тут есть некоторые проблемы: а если у этих контролов должны срабатывать какие-то события? А если типов нод больше ста? Получится огромный класс, в котором ковыряться будет не очень приятно. Хотя в принципе “partial class” может облегчить жизнь…

Но мне больше нравится 3-й вариант:
3. Для каждого набора контролов (для каждого типа нод) нарисовать свое окно. При выборе ноды создавать это окно, копировать все контролы из этого окна в groupBox и назначать
делегатов на события, которые требуют реакции главного окна:

nodeSettingsGUI = new nodeType1ControlsForm(this.event1, this.event2…);
groupBox.Controls.Clear();
while (nodeSettingsGUI.Controls.Count > 0)
{
    Control c = nodeSettingsGUI.Controls[0];
    groupBox.Controls.Add(c);
}

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

Установка Apache2.2 + mod_wsgi под Windows: пара подводных камней

Wednesday, September 10th, 2008

Начал изучать питон.  Попробовал поставить Apache 2.2, python 2.5 и  mod_wsgi на windows машину. Первые два встали без проблем, пока их связывал через mod_wsgi, наткнулся на 2 камня.

1 камень: The specified module could not be found

Cкачал mod_wsgi.so, правлю httpd.conf, запускаю сервер, получаю:

httpd.exe: Syntax error on line 120 of C:/Apache2.2/conf/httpd.conf: Cannot load C:/Apache2.2/modules/mod_wsgi.so into server: The specified module could not be found.

Схожую проблему и ее решение нашел тут: нужно в PATH добавить путь до python25.dll

2-й камень: You don’t have permission to access /hello on this server.

После решения первой проблемы вставил

WSGIScriptAlias /hello C:/Apache2/htdocs/hello.wsgi

в httpd.conf и создал соответсвующий hello.wsgi . Теперь на http://localhost:8080/hello апач мне выдавал

Forbidden, You don't have permission to access /hello on this server.

Как оказалось, во всем виновата была описка в пути до hello.wsgi (у меня апач стоит в C:\Apache2.2), а поскольку

<Directory />
Options FollowSymLinks
AllowOverride None
Order deny,allow
Deny from all
</Directory>

,т.е. доступ до C:\ закрыт, то при попытки проверить, существует ли папка C:\Apache2, он возвращает Forbidden а не Not Found (что сперва казалось бы более логичным).

Будьте бдительны.

“Транзакционный” характер сессий в PHP

Monday, August 4th, 2008

...Сессия - не "информационное облако", это всего-лишь интерфейс к файлу, ....
сам по себе php не лочится, просто когда 2й процесс (скрипт) делает системный запрос на открытие файла - система его подвешивает и ждет освобождения ресурса. ...
(c)grigori

Эх, век живи – век учись. Еще какой-нибудь месяц назад я находился в блаженном неведении, $_SESSION мне представлялась именно тем “информационным облаком”, о котором писал grigori. Иллюзия разрушена.

Но на месте краха тут же возникли 3 новые истины:

1. session_init() подвешивает скрипт и ждет, когда остальные скрипты с открытыми сессиями и с такими же идентификаторами будут завершены или закроют сессии;

2. всвязи с этим, нужно открывать и закрывать сессии только когда это на самом деле необходимо. Не правильно поступать по принципу “открыть вначале и забыть”. Простейший пример: клиент ajaxом периодически дергает скрипты и пусть они написаны по этому принципу. В этом случае все эти скрипты не смогут работать паралельно, т.к. session_init() будет выступать в роли мьютекса. То есть, мало того, что производительность резко упадет (асинхронность отпадает сразу), есть вероятность “подвисания” всей этой системы, если какой-нибудь скрипт будет выполняться слишком долго.

3. session_set_save_handler – не выход, как это может показаться на самый первый взгляд. Действительно, можно сделать свой обработчик сессий так, чтобы session_init не “весила” все паралельно работающие скрипты. Но давайте вернемся к примеру из п2. Пусть есть один longplay скрипт, и за время его работы паралельно выполнились другие скрипты. В этом случае “callback $close” ? этого скрипта затрет все изменения $_SESSION, которые были сделаны другими скриптами пока он работал.

Таким образом, сессии в php носят “транзакционный” характер: session_init() = BEGIN TRANSACTION, session_write_close() = COMMIT, session_init() – мьютекс для всех скриптов с одним и тем же SESSID.

Excel (XLS) <-> CSV конвертация с помощью javascript-а и php

Saturday, July 26th, 2008

Наверняка любой php программист сталкивался с ситуацией, когда заказчик просит “экспорт товара из экзеля” или чего бы то ни было. И стон идет по всей земле русской (и нерусской)…

Первое, что приходит в голову – это каждый раз запускать Excel и сохранять файл в формате CSV, с которым php уже намного легче работать. Это – замечательное решение, когда число файлов меньше 10 и все они состоят только из одной страницы (поскольку экзель может экспортировать в CSV только страницы, те результат конвертации файла из 10 страниц будет 10 CSV файлов).

Вторая мысль: скачать какую-нибудь софтину, которая бы преобразовывала набор Excel файлов в CSV, включая все страницы. Но, погуглив чуток, сразу видно, что софтины эти все сплошь коммерческие. Можно, конечно, сломать.

Но есть и другой пусть: написать самому. Это очень легко реализуется, если использовать javascript и ActiveX компонент Excel.Application:

xls2csv.js

Этот скрипт необходимо поместить в папку с набором XLS файлов, создать папку output и tmp и набрать в коммандной строке:

cscript xls2csv.js

По завершении работы скрипта в output у нас появятся искомые CSV файлы, папку tmp можно почистить.

Для генерации Excel файлов на сервере я предпочитаю легкий класс ExcelWriter, написанный индусом Harish Chauhan-ом, тяжелому PEAR-овскому пакету Spreadsheet Excel Writer-у.