Погодный плагин для «Sublime Text»

Окно редактора (86.25КиБ)
Окно редактора «Саблайм Текст» с погодой и пробками в строке состояния

Написал свой первый плагин для «Саблайма» — для отображения в строке состояния погоды и пробок «Яндекса». Заодно немного повспоминал «Пайтон», давно на нём ничего не писал.

Удивительно, но факт — другого работоспособного плагина на эту тему не обнаружилось. Единственный конкурент использует старое АПИ «Яху», которое уже не работает, потому не работает и плагин.

Отдельное спасибо «Яндексу» за то, что их АПИ умеет определять текущее местоположение — ничего задавать в конфиге не надо, удобно.

Поскольку в строке состояния можно выводить только буквы, воспользовался Юникодом для вывода погодных значков, для уровня пробок не нашлось ничего лучше фруктов, так что индикатором загруженности дорог у меня служат зелёное яблоко, жёлтый лимон и красный помидор. Не знаю будут ли видны эти символы пользователям Линукса или Виндоуза, мне негде посмотреть.

Пришлось повозиться с АПИ самого «Саблайма». Во-первых, отдельного события на старт редактора нет, пришлось активировать плагин на получение фокуса окном. Во-вторых, некоторые авторы плохо читают документацию — у многих их плагины полностью переинициализируются на событие, это ни к чему, я, например, дёргаю «Яндекс» как можно реже, по таймеру в отдельном потоке, а в строке показываю кешированное.

В настройках есть шаблон вывода, можно убрать пробки или погоду, если они не нужны.

Подал пул реквест на включение в саблаймовский пакетный менеджер, но особо упорствовать не буду, если откажут.

«Кремниевая долина»

Пока отхожу от операции, смотрю сериал «Кремниевая долина» и дабы не заржавел мозг, сделал расшифровку надписи на футболке Эрлиха — одного из героев сериала, на трёх языках: Перле, Пайтоне и Тикле. Листинг один для трёх языков:
#\
"@{[$\=qq{\n},\
print map {chr $_} (0b1000010, 0b1101001, 0b1110100, 0b1100011, 0b1101111, 0b1101001, 0b1101110)]}";#\
__END__ = 1;\
print(''.join(chr(b) for b in (0b1000010, 0b1101001, 0b1110100, 0b1100011, 0b1101111, 0b1101001, 0b1101110))); #\
"""
foreach name {01000010 01101001 01110100 01100011 01101111 01101001 01101110} {
    puts -nonewline [binary format B* $name]
}
puts {} ;#"""
Дольше всего с Перлом возился, пока не вспомнил, что у него есть вычисление выражений внутри строк через конструкцию @{[…]}. Нужно рассказать как всё работает?
Комментировать
10 июля 2015 18:29

Массив с конца: ~n

Чудесное нашлось в комментариях на «Хабре»: логичный, на мой взгляд, способ получать значения с конца массива. Способ, вообще говоря, для «Пайтона», но и для «ПХП» можно применить.

В «Пайтоне» есть такая проблема, даже не то чтобы проблема, скорее мелкое неудобство, что списки, если их считать с начала, нумеруются с нуля, а если с конца — с минус единицы:
>>> print([1,2,3][0], [3,2,1][-1])
(1, 1)
Лично я к этому так привык, что в нелогичность такого поведения сразу и не вник. Неудобство становится более очевидным, если от фиксированных значений перейти к переменным:
>>> i=2
>>> print([1,2,3,4][i], [4,3,2,1][-i-1])
(3, 3)
Чтобы брать значения с конца, везде, как приклееную, приходится таскать эту дурацкую минус единицу. Но есть способ лучше, более того, я им иногда пользовался, но почему-то не возвёл в систему — можно использовать тильду. Всё сразу становится более логичным:
>>> i=2
>>> print([1,2,3,4][i], [4,3,2,1][~i])
(3, 3)
>>> print([1,2,3][0], [3,2,1][~0])
(1, 1)
Для тех, кто не понимает смысла операции «тильда» можно объяснить, что без тильды значения берутся с начала, с тильдой — с конца. Здо́рово!

Обычно в таких случаях возражают, что новички будут путаться. Это какой-то странный аргумент, честное слово, в «Пайтоне» для новичков очень много новых «значков» — те же декораторы, а «тильда» есть в большинстве распространённых языков, пусть новички учатся, к тому же забыть вычесть единицу — одна из частых ошибок, встречается не только у новичков, но и у матёрых программистов.
18 комментариев
4 января 2014 20:27

«Словохват»

Вчера подсел на игру «Словохват», вроде игра незамысловатая, но очень интересная. Словохват (85.68КиБ) Смысл — занять одну из клеток противника, помеченного «звёздочкой», в терминах игры это называется «столица». Чтобы занять клетку, нужно придумать слово с буквой, которая написана на этой клетке, причём нужно уложиться в отведённое время — сверху тикает таймер.

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

На медиане поля действуют ещё какие-то законы, пока не разбирался, правила есть на сайте. Длина слов важна — упрощённо, чем длинее слово, тем больше вам даётся очков, за очки можно «покупать» различные штуки — дополнительное время, дополнительную букву, «молнию» (вычищает близкорасположенную клетку от цвета противника).

У автора получилась неплохая такая тактическая игра. Мне играть было скучновато — очень лень выдумывать слова, поэтому я написал небольшую программу на Пайтоне, которая работает со словарём под редакцией профессора Лопатина, ей нужно задать файл словаря и буквы, которые у вас есть, она придумает слово: Словохватохват (18.24КиБ) Работает неспешно, я оптимизировать не стал, времени, даваемого игрой, на поиск вполне хватает (по крайней мере на моём ноуте). Если кому-то захочется оптимизации, то просто посмотрите как преобразуется словарь и закешируйте преобразованный вариант.

У меня на каждом запуске словарь преобразуется заново. Естественно, в словаре, помимо разрешённых частей речи, есть прилагательные, глаголы и так далее — их я не отфильтровываю, в словаре нет для этого информации.
# coding: utf-8
import itertools
import sys
import codecs

# дружим с консолью
sys.stdin = codecs.getreader('utf-8')(sys.stdin)
sys.stdout = codecs.getwriter('utf-8')(sys.stdout)

# строки с пропусками любых букв в любом количестве
def combinations(str):
    for l in xrange(2, len(str)+1):
        yield itertools.combinations(str, l)

# собираем каждую в строку
def iterstr(str):
    for s in itertools.chain(*combinations(str)):
        yield u''.join(s)

# все наши комбинации
def uniqcombinations(str):
    return set(iterstr(sorted(str)))

# проходим по словарю
def iterdict(name, min, max):
    with open(name, 'r') as f:
        for line in f:
            line = line.strip().decode('cp1251')

            # текущее слово должно содержать только буквы и быть не больше искомого слова
            if max >= len(line) >= min and line.isalpha():
                yield u''.join(sorted(line)), line

# ищем наше слово
def find(word, dictname):
    words = uniqcombinations(word)

    for line, original in iterdict(dictname, 3, len(word)):
        if line in words: print(original)

# запуск программы и небольшая помощь
try:
    find(sys.argv[2].decode('utf-8'), sys.argv[1].decode('utf-8'))
except IndexError:
    print(u'Запуск: {0} <словарь> <буквы в наличии>'.format(sys.argv[0]))
Писал именно на Пайтоне из-за прекрасного модуля itertools, на этот раз он пригодился функцией combinations — она выдаёт генератор всех комбинаций букв, заданной длины, без изменения их порядка. Очень хорошо, что не пришлось писать этот скучный кусок кода самостоятельно.

Если вы думаете, что выиграть можно бездумно подставляя слова из моей программы, то сильно ошибаетесь — на сайте попадаются противники, которые вас с таким подходом разнесут в пух и прах.
21 комментарий
4 декабря 2013 08:24

30 банковских дней

Делал жене программу, чтобы отсчитывать от введёной даты 30 банковских дней в текущем году, может пригодится кому (в запускаемый файл преобразовал при помощи PyInstaller, получается один файл, очень удобно). При первом запуске программа скачивает календарь с сайта Calend.Ru и сохраняет рядом, потом берёт из него выходные и праздничные дни.
# coding: utf-8
import re
import httplib
import pickle
import ctypes
from datetime import date, datetime, timedelta

MB_OKCANCEL = 1
MB_SYSTEMMODAL = 4096
IDOK = 1
IDCANCEL = 2

def getCalend():
    h = httplib.HTTPConnection('www.calend.ru')
    h.request('GET', '/work/')
    return h.getresponse().read()

def getHolidays():
    table = re.findall(r'<table\s.+?time_of_death(.+)</table>', getCalend(), re.DOTALL)[0]
    return re.findall(r'<td\s+class="\S+\s+col5"\s+day="(\d+)"\s+month="(\d+)"', table, re.DOTALL)

def readHolidays():
    year = str(date.today().year)
    try:
        with open(year + '.cache', 'r') as f:
            holidays = pickle.load(f)
    except IOError:
        with open(year + '.cache', 'w') as f:
            holidays = tuple(getHolidays())
            pickle.dump(holidays, f)

    return [datetime(day=int(d[0]), month=int(d[1]), year=int(year)) for d in holidays]

def getStartDate():
    while 1:
        input = re.split(r'\D', raw_input("Enter start date (dd.mm): "))
        try:
            input = [int(x) for x in input]
            if len(input) == 2:
                break
        except ValueError:
            pass

    return (date.today().year, input[1], input[0])

delta = timedelta(days = 1)

holidays = readHolidays()
msgbox = ctypes.windll.user32.MessageBoxW

while 1:
    try:
        date = datetime(*getStartDate())

        for i in xrange(0, 30):
            while 1:
                date += delta

                if date not in holidays: break

	if msgbox(
            0,
            u'Получилась следующая дата: %02d.%02d.%04d' % (date.day, date.month, date.year),
            u'Ответ',
            MB_OKCANCEL | MB_SYSTEMMODAL
        ) == IDCANCEL:
		raise StopIteration
    except (KeyboardInterrupt, StopIteration):
        break
Программу написал быстро, а сложнее всего было её отправить — ГМэйл упорно не принимает запускаемые файлы, даже в архивах.
15 комментариев
2 апреля 2013 10:51

Уходим с телефоном, «МакБук» выключается

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

Идея выглядела просто: надо посмотреть присутствует ли в эфире мой смартфон, если нету, значит я куда-то ушёл (я без смартфона не хожу). Я сначала это на «Баше» сделал и всё даже работало, но случилось неожиданное: Mac OS время от времени стала падать с серым экраном смерти. Тогда я взял в руки Пайтон. Я даже запрограммировал сканирование эфира, но выяснилась неприятная штука — через какое-то время моя «Нокия» автоматически выключала видимость устройства и никакими уговорами не удалось убедить её этого не делать. Пришлось делать проверку соединением со смартфоном (не знаю как это сажает батарею, не успел ещё проверить).

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

Шаг №1.

Нужно узнать так называемый «адрес» вашего устройства и привязать его к компьютеру. Тут ничего сложного нет, заходим в настройки «Блютуз» на «Маке» (они находятся в «Системных настройках»), привязываем его как обычно и переписываем себе 12 букв, разделённых минусами, это и есть адрес: Настройки Bluetooth (80.68КиБ)

Шаг №2.

С этого шага потребуется уметь пользоваться терминалом. Создаём запускаемый файл /usr/local/bin/bt-lock следующего содержания:
#!/usr/bin/python

import objc
import sys
from subprocess import call

objc.loadBundle('IOBluetooth', globals(), bundle_path=u'/System/Library/Frameworks/IOBluetooth.framework')

def CheckDevice(address):
    dev = IOBluetoothDevice.withAddressString_(address)

    if dev.isConnected():
        return True

    conn = dev.openConnection()

    if conn == -536870185: # Bluetooth is off
        return True

    if conn == 0:
        dev.closeConnection()
        return True

    return False


cmdtorun=(r'/System/Library/CoreServices/Menu Extras/User.menu/Contents/Resources/CGSession', r'-suspend')

try:
    if not CheckDevice(sys.argv[1]):
        call(cmdtorun)
except IndexError:
    print("Usage: bt-lock <device address>")
В скобках замечу, чтобы сделать файл запускаемым нужно в терминале выполнить следующую команду: chmod a+x /usr/local/bin/bt-lock.

Шаг 3.

Создаём файл ~/Library/LaunchAgents/ru.bolknote.Bluetooth-lock.crontab.plist с таким вот содержимым:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>Label</key>
  <string>ru.bolknote.Bluetooth-lock.crontab</string>

  <key>Program</key>
  <string>/usr/local/bin/bt-lock</string>

  <key>ProgramArguments</key>
  <array>
    <string>/usr/local/bin/bt-lock</string>
    <string>а̲д̲р̲е̲с̲ ̲в̲а̲ш̲е̲г̲о̲ ̲у̲с̲т̲р̲о̲й̲с̲т̲в̲а̲</string>
  </array>

  <key>Nice</key>
  <integer>20</integer>

  <key>StartInterval</key>
  <integer>30</integer>

  <key>RunAtLoad</key>
  <true/>
</dict>
</plist>
И выполняем в терминале команду «launchctl load ru.bolknote.Bluetooth-lock.crontab.plist». Это всё.

Чтобы отключить всю эту марахайку, достаточно выполнить в терминале команду «launchctl unload ru.bolknote.Bluetooth-lock.crontab.plist» или выключить Bluetooth на ноуте.

Решение только для «Мака», как работать из Пайтона с Блютузом под другие операционные системы, я не знаю.
20 комментариев
16 марта 2013 20:20