| оказался проще, чем мы думали:
import threading
import wx.lib.newevent
MakeEvent01, wx.EVENT01 = wx.lib.newevent.NewEvent()
class widget1(wx.SomeWidget):
def async_report(self, s):
event = MakeEvent01()
event.text = s
wx.PostEvent(self, event)
def __init__(self, parent, **kwargs):
wx.SomeWidget.__init__(self, parent, wx.ID_ANY, **kwargs)
def AssignJob(self,do_stuff):
do_stuff_frozen = functools.partial(do_stuff, async_report)
self.t = threading.Thread(target=do_stuff_frozen)
self.Bind(wx.EVENT01, self.Event01Handler)
self.t.start()
def Event01Handler(self, evt)
self.Display(evt.text)
Этому добру кормим любую функцию вида DoSomeLongAndHeavyStuff(log) и радуемся жизни. Интересно, можно ли ещё проще. | comments: 5 комментариев or Оставить комментарий  |
| Вчера мы с hakubo обсуждали Хаскель. Ну, обсуждали - это сильно сказано, обычно он присылает мне интересные ссылки на про языки программирования или аугментированную реальность, а я что-нибудь не слишком осмысленное пишу в эвей обратно, т.к. наши часы присутствия в онлайне сильно не совпадают.
Вообще я иногда до обидного медленно соображаю. Я бы и рад взяться за изучение чего-нибудь действительно нового и интересного (каждый, кто сидит в питоне дольше двух дней, уже заметил, что, собственно говоря, его целью не является быть интересным — он успехом занял место прагматичного и предельно простого в использовании повседневного инструмента для того, чтобы заставить компьютер делать нужное тебе, а не наоборот - и при этом не дискриминируя людей по платформе, господа юниксшеллофаги), но само осознание того, что на усвоение простейших концепций я потрачу минимум полгода, совершенно не взбадривает.
Но обратно к Хаскелю. Мне все больше начинает казаться, что функциональные языки скрывают кучу стройных, красивых и простых концепций за отвратительнейшим синтаксисом, как будто нарочно пытаясь нас запутать.
Ради интереса полез посмотреть ,как у хаскеля с гуями (т.е. может ли он использоваться для чего-то кроме консольных программ или библиотек). И меня сразу же потрясла одна вещь - wxHaskell уже реализует почти буквально вон тот декларативный гуевый велосипед, что я придумал на досуге. Да простят мне бесстыдный и бесконечный самопиар, я так редко придумываю достойные идеи, что искренне радуюсь, когда их, оказывается, уже сделали.
hello
= do f <- frame [text := "Hello!"]
quit <- button f [text := "Quit", on command := close f]
set f [layout := widget quit]
Вот это я называю «выразительность». Что (не)удивительно, этот код полностью понятен даже без какого бы то ни было знания Хаскеля, функциональных языков или принципов работы гуя на событийной основе.
Я по-прежнему боюсь тыкать палочкой Хаскель. Не могу представить, как лично мне его применить на практике, и хотя в теории это лечится чтением ртфм, на практике фм по нему часто запутан ещё больше, чем сам язык. Из других языков я, возможно, соберусь когда-нибудь потыкать палочкой Руби и Скалу. В Руби привлекает, по-видимому, большая гибкость и расширямость языка, полезная при создании кавайных, претенциозных и совершенно бесполезных маленьких DSL на месте. Чем хороша Скала, всем известно, хотя явовское наследие настораживает. | comments: 22 комментария or Оставить комментарий  |
| Все мы помним, какая хорошая это идея — архитектура Model-View и её вариации, и какое дикое количество геморроя она вызывает на практике. Связано это с тем, что она никак принципиально не решает проблему переплетения кода модели и кода представления, не убирает этот беспорядок, а всего лишь дает возможность в нем ориентироваться. На гордое звание решения могут претендовать лишь инструменты, избавляющие нас от хаоса. Об разделении business-кода и интерфейсов много говорили, и иногда по делу, но в итоге все сводилось к вписыванию кода в обработчики событий и бесчиаленные вызовы TextCtrl1.GetText(). Просто вместо хардкода последовательности инициализации интерфейсов хранились в отдельных файлах и представлялись в виде xml — этакий психологичеcкий трюк, чтоб относиться к ним, как данным. И если для компилируемых языков это было критично, то для, скажем, Питона уже никакой разницы между трансляцией xml-файла в интерфейсный код и собственно подгрузкой и непосредственным исполнением файла с уже имеющимся интерфейсным кодом в принципе не существует.
Эти жалкие полумеры меня не могли радовать. Идеальная система, как я её себе воображал, выглядела примерно так: берется алгоритм, где все инструкции вида print(x) или Label.SetText(x) убираются, а вместо них в начале ставятся, «watch(x) as Label»; а всякие словоблудия типа TextCtrl.GetText(s) заменяются на «reflect(s) as TextCtrl». Странные термины были выбраны специально, чтобы подчеркнуть декларативность стиля, вневременность и внеконтекстность связей: мы, Человек, должны только высказать, что хотим отображать переменную или знать значение текстбокса, а остальное - проблемы роботов.
Оказалось, что похожая штука существует: http://avc.inrim.it/ Именно (почти) об этом я мечтал. Это, товарищи, потрясающе. Эта штука автоматически связывает извлекаемые из ресурсов гуи и переменные в коде. Но мало того, по сути дела это ещё и мета-тулкит. Можно писать код, абсолютно агностичный не только к платформе, не только к конкретному тулкиту, — но и к гую как таковому в принципе.
От моей конепции AVC отличается тем, явно что использует xml и редакторы гуя. Это разумное решение - опираться на уже имеющиеся инструменты. А взамен на это она вообще избавляет нас от необходимости явно декларировать, что мы хотим отображать или знать - достаточно просто дать контролам имена соответсвующих переменных. Теоретически, на базе этого легко можно построить предложенную мной систему, которая будет ещё проще и примитивнее, но зато позволит быстро и в два слова писать истинно декларативный гуй от руки. А для случаев посложнее по-прежнему грузить интерфейсные файлы или читать конфигурацию раскладки шуевых элементов из строки с подобием вики-таблицы. Ну это все теоретически, на практике-то у меня кишка тонка такое кодить.
Картинка.
Рабочий код идентичен для любого из пяти тулкитов. Последовательности инициализации и внешний вид, как и следовало ожидать, немного отличаются - и даже этого бойлерплейта можно было избежать, добавив дополнительные обертки и конфигураторы отдельно для каждого тулкита, а также упростить обработку сложных типов (выводить голые словари и списки без требуемого специального форматирования).
P.S. Ещё один похожий концепт был в экспериментальном интерфейсно-остроумном тулките Shoes для Руби, но для написания стандартного интерфейс он не подходил, да и не ставил перед собой такую задачу. | comments: 3 комментария or Оставить комментарий  |
| В контексте проблемы гуя мне уже несколько раз упоминали про WPF. несмотря на то, там много как интересных решений (доведение до ума слоя абсракций между прикладным кодом и системой), так и серьёзных минусов (огромность и неподъёмность, проприетарная идеология), нас интересует один чисто технологический аспект: а именно, XAML. По заявлениям эта фиговина обеспечивает полностью декларативное написание гуёв и чистое, как никогда раньше, разделение функционального («бизнес», май эсс) кода и gui-кода, и ещё много прочего бла. К сожалению, при ближашем рассмотрении эта хрень оказалась старым добрым XML, т.е. никаких принципиальных изменений в парадигме написания интерфейсов не произошло - просто вызовы api в очередной раз спрятали ещё глубже и укутали слоями абстракции и задолбавшего X*L. Более того, с точки зрения человека XML, будь он хоть сто раз декларативный, в сотню раз хуже императивного гуя на тулкитах - ибо работать с XML можно только с помощью костылей, ведь это фактически антипаттерн человекочитаемости. Я допускаю, что там будет реализован какой-то более чёткий и ясный механизм связывания функциональности гуя и функциональности кода, но, используя презумпцию виновности, получаем вывод: это просто майкрософтовская реализация QtDesigner / Glade для очередных Foundation Classes, обвешанная пышными слоганами.
«Пастернака не читал, но осуждаю»© :D | comments: 1 комментарий or Оставить комментарий  |
| ярлык: C:\GTK\bin\glade-2.exe реальность: C:\GTK\bin\glade-3.exe Парни из команды GTK дают жару! прослоупочить собственный релиз! :) | comments: Оставить комментарий  |
| pygobject-2.14.1-1.win32-py2.5.exe (171 Кб)
>>> import gtk ImportError: No module named cairo
господи, какие же собаки это не собаки! это крысы!!
pycairo-1.4.12-1.win32-py2.5.exe (82 Кб)
>>> import pygtk >>> import gtk >>> import cairo
Ну неужели нельзя было на сорсфорже выложить сразу готовую пачку одним инсталлером, а? Я уже нашёл ссылку на эту пачку, но почему, бл..., её не было в категории win32 binary на сорсфорже?! | comments: 2 комментария or Оставить комментарий  |
| gtk-2.12.9-win32-2.exe gtk-dev-2.12.9-win32-2.exe pygtk-2.12.1-2.win32-py2.5.exe
>>> import pygtk ImportError: No module named pygtk
>>> import gtk ImportError: No module named gobject
надо, наверное, было качать какой-то другой инсталлер. Или исходник. Хотя питоновые инсталлеры всё равно генерятся из исходников. В общем, какой-то другой. repeat until© | comments: Оставить комментарий  |
| В общем, я ляпнул что для излечения от виджетной тулчанки нам нужно использование функциональных парадигм, и пришлось задуматься:
— как применить функциональную парадигму к гую? В предельно общем случае мы получаем следующее:
watch(var) react(button) reflect(var ←→ input)
Получается весьма, симпатично. Заметим, что отказ от императивного подхода в пользу функционального предполагает, что мы понимаем и отдаём в себе отчёт: мы теряем тотальный, параноидальный контроль над тем, как это будет выглядеть, но ведь он для нас априори не важен: имплементация пусть предоставит модуль для настройки результирующего внешнего вида или сверхумное конфигурационное устройство - нас это не волнует.
Хорошо, ну а вот как это теперь написать? Попробуем приблизить это к форме, реализуемой технологически (пусть даже в виде макросов) (OSHI-- я это сказал. проклятие Настоящих Программистов настигнет меня...) Используя менеджер контекста, мы можем спрятать все детали инициализации и даже закрыть глаза на то, когда именно она происходит. Более того, стараемся помнить, что функциональная парадигма в идеале требует описывать только нужные действия, независимо от их порядка, но для простоты предположим, что мы помещаем эти формулы (объявления действий) в начале программы. На границе сознания смутно маячит проблема глобальных переменных, но мы её не замечаем, т.к. мы спасаем мир решаем более фундаментальную проблему, ну и плюс мы смутно помним, что в современных динамических языках не всё так с ними страшно. Кстати, аргументов у всех наших функций тоже должно быть ровно один, иначе наши уравнения теряют смысл (и приобретают Паскаль).
Ну, например, вот так: (псевдокод, но валидный питон... почти. префиксы мы добавлять не будем, хотя wxХрень или QХрень смотрится, конечно, в сто раз круче, чем просто Хрень </ирония>)
with App:
with Window1:
watch(var1) //отображение
react(button1) = proc1 //уравнение :)
reflect(var2 <> input1) //отражение
В принципе, этот код уже должен работать без дополнительной рихтовки. Всё остальное должен либо (автоматически) конфигурировать отдельный девайс настройки, либо и знакомый нам по дельфям метод прямого обращения к свойствам компонента с помощью контекстного менеджера (который уже сам решит, когда выполнять данные ему инструкции настройки) тоже подойдёт. Важно не перегнуть абстракцию: в нашем случае компоненты должны реализовывать высокоабстрагированный интерфейс и сохранять функциональный стиль. Тут можно придумать множество синтаксического сахара - например, каждая из трёх фундаментальных инструкций watch/react/reflect могла бы сама служить контекстным менеджером настройки упомянутого в ней элемента интерфейса, и это было бы хорошо, но в отсутствие рабочей имплементации это — бессмысленное занятие.
Заметим, что я сделал формы нарочно непохожими: оно лучше, когда «разные идиомы выглядят по разному»©. А т.к. их назначения принципиально различны, то имеет смысл сделать их использование тоже разным. Хотя синтаксис можно допилить, и, особенно значки операторов, — их можно использовать любые. Например, вот такая версия с учётом вышесказанного:
reflect(var2 = input1) | proc2 //отражение в виде уравнения и ещё обработчик
style="border:1pt;font-family:verdana" //внезапно набигает цсс!
react(button1) >> proc1 //злой c++ возвращается и мстит! ха-ха-ха!!
caption="push me" //а вот более традиционный подход
appearace=schema1 //а ещё хорошо бы не прописывать все эти visible = true руками,ね
behavior=follow(!proc1) //да и с задолбавшим enabled = false давно пора что-то делать
или даже вот такая спекуляция на идее корутин: reflect(var2 < proc2 > input1) //хотя это выглядит уже чересчур грязно Можно всё. Кроме react(button1, proc1), конечно. За такое убивают. | comments: 16 комментариев or Оставить комментарий  |
| | Почему я принципиальный противник написания GUI-кода руками? Да потому, что это идиотизм: это сущностно противоречит самой идее гуя как такового. Все знают, насколько отвратительной порой бывает такая отвратительная каша GUI-кода и функционального кода, особенно когда возникает необходимость вычленить функциональный код из приложения на какой-нибудь древней платформе. Но! В системах RAD, оборудованных автоматической генерацией гуи-кода типа Дельфи/Билдер этот недостаток хотя бы консистентен (аналоги мне неизвестны, и не уверен, насколько нынче зелена трава на майкрософтовской лужайке). Код гуя генерируют роботы, а мы им просто поклоняемся. То есть знаем типичные недостатки, где их искать и как их лечить. Когда же код гуя пишет вручную человек на том же gtk, результат в каждом конкретном случае непредсказуем. Фактически, ничего не изменилось со времён печально знаменитой закусочной Steve Balmer's MFC's - я не вижу никаких качественных отличий кодинга на тех же ГТК или Qt. Только количественные: на порядок меньше гемора, -//- проще, -//- красивее, -//- функциональнее. | comments: 5 комментариев or Оставить комментарий  |
| |