Create TOC

레이블이 Python인 게시물을 표시합니다. 모든 게시물 표시
레이블이 Python인 게시물을 표시합니다. 모든 게시물 표시

2020년 11월 29일

KDE/일출, 일몰에 따라 자동으로 테마 변경하기

KDE에는 일출, 일몰에 따라 자동으로 테마를 변경하는 설정이 없어서 따로 스크립트를 만들어서 변경한다.

일몰, 일출 시간을 얻기 위해서 python3-astral 패키지를 설치한다.

$ sudo apt install python3-astral

아래 python 스크립트를 ~/.local/bin/lookandfeel_change.py 으로 저장한다.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# vim:set expandtab fenc=utf-8 ff=unix:

import os
import logging
import datetime
import subprocess
import argparse
from astral import Astral  # type:ignore


logging.basicConfig(format='[%(asctime)s]{%(name)s}(%(levelname)s) %(message)s', level=logging.INFO)
logger = logging.getLogger(os.path.basename(__file__))
KST = datetime.timezone(datetime.timedelta(hours=9))

DARK_THEME = 'org.kde.breezedark.desktop'
LIGHT_THEME = 'org.kde.breeze.desktop'


def kreadconfig(group: str, key: str) -> str:
    p = subprocess.run(['/usr/bin/kreadconfig5', '--group', group, '--key', key],
                       capture_output=True)
    return p.stdout.decode('utf-8').strip()


def kwriteconfig(group: str, key: str, value: str):
    p = subprocess.run(['/usr/bin/kwriteconfig5', '--group', group, '--key', key, value],
                       capture_output=True)
    logger.debug(p.stdout.decode('utf-8').strip())
    logger.debug(p.stderr.decode('utf-8').strip())


def apply_theme(theme: str):
    p = subprocess.run(['/usr/bin/lookandfeeltool', '-a', theme], capture_output=True)
    logger.debug(p.stdout.decode('utf-8').strip())
    logger.debug(p.stderr.decode('utf-8').strip())
    kwriteconfig('KDE', 'LookAndFeelPackage', theme)


def main():
    ast = Astral()
    city = ast['Seoul']
    now = datetime.datetime.now(tz=KST)
    today = city.sun(date=now, local=True)
    logger.debug(f'{today["sunrise"]} <= {now} < {today["sunset"]}')

    new_theme = DARK_THEME
    if (today['sunrise'] <= now < today['sunset']):
        new_theme = LIGHT_THEME

    cur_theme = kreadconfig('KDE', 'LookAndFeelPackage')

    if cur_theme != new_theme:
        apply_theme(new_theme)
        logger.debug(f'apply {new_theme}')
    else:
        logger.debug(f'already applied {cur_theme}')


if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='일출/일몰시간을 기준으로 Plasma 테마를 변경')

    parser.add_argument('--debug', '-d', action='store_true')

    options = parser.parse_args()

    if options.debug:
        logger.setLevel(logging.DEBUG)

    main()

저장 후 실행 속성을 준다.

$ chmod +x ~/.local/bin/lookandfeel_change.py

이대로 cron에 등록하면 제대로 동작하지 않는다. cron으로 실행하는 경우 login 했을 때와 환경 변수 구성이 다르기 때문이다. 아래 script를 ~/.local/bin/lookandfeel_change.sh으로 저장한다.

#!/bin/bash

export DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/계정id/bus
export DESKTOP_SESSION=plasma
export DISPLAY=:0
export PATH=/home/계정명/.local/bin:/usr/local/bin/:/usr/local/bin:/usr/bin:/bin:/usr/games
export SHELL=/bin/bash
export XDG_CURRENT_DESKTOP=KDE
export XDG_RUNTIME_DIR=/run/user/계정id
export XDG_DATA_DIRS=/var/lib/flatpak/exports/share:/usr/local/share:/usr/share

/home/계정명/.local/bin/lookandfeel_change.py

계정id는 id 명령으로 확인할 수 있다. Debian 계열인 경우 첫번째로 추가한 사용자 id는 보통 1000 이다.

$ id

저장 후 실행 속성을 준다.

$ chmod +x ~/.local/bin/lookandfeel_change.sh

이제 10분마다 실행할 수 있게 crontab에 등록한다.

*/10 * * * * /home/계정명/.local/bin/lookandfeel_change.sh

2020년 9월 11일

python개발을 위한 code-server 설정

목차

    2016년 6월 22일

    Raspbian/DHT22 센서 사용하기

    DHT22 센서는 GPIO 4에 연결했다고 가정한다.

    pigpio

    GPIO 에서 값을 읽으려면 root 권한이 펼요하지만 pigpio를 사용하면 사용자 계정에서도 값을 읽을 수 있다.

    설치

    $ sudo apt-get install pigpio python-pigpio
    

    설정

    $ sudo systemctl enable pigpiod.service
    

    시작

    $ sudo systemctl start pigpiod.service
    

    DHT22.py

    설치

    pigpio 샘플에 DHT22 를 다루는 코드가 있다.

    $ wget https://raw.githubusercontent.com/joan2937/pigpio/master/EXAMPLES/Python/DHT22_AM2302_SENSOR/DHT22.py
    

    수정

    DHT22.py에서 atexit 관련 코드를 삭제한다.

    그리고 GPIO 4에서 값을 읽도록 코드를 수정한다.

    @@ -254,7 +254,7 @@ if __name__ == "__main__":
    
        pi = pigpio.pi()
    
    -   s = DHT22.sensor(pi, 22, LED=16, power=8)
    +   s = DHT22.sensor(pi, 4)
    
        r = 0

    테스트

    $ python DHT22.py
    1 54.0 28.5 0.20 0 0 0 0
    2 54.5 28.5 0.20 0 0 0 0
    3 54.6 28.5 0.20 0 0 0 0
    4 54.7 28.5 0.20 0 0 0 0
    

    2016년 5월 22일

    Raspbian/python과 pypy에서 pip 각각 사용하기

    package가 아니고 따로 pip를 설치했다면 제거한다.

    
    $ sudo pip uninstall pip
    

    python을 위해 pip package 설치한다.

    
    $ sudo apt-get install python-pip
    

    pypy를 위해서 pip를 설치한다.

    
    $ wget https://bootstrap.pypa.io/get-pip.py
    $ sudo pypy get_pip.py
    

    python과 pypy를 위한 alias를 ~/.bash_aliases에 만든다.

    
    alias pip = 'sudo -H python -m pip'
    alias pypy_pip = 'sudo -H pypy -m pip'
    

    2014년 1월 6일

    Vim/Win32환경에서 한글이 포함된 경로의 파일에 대해 python mode가 동작하지 않는 문제

    Win32환경에서 한글이 포함된 경로의 python 파일에 대해 python mode 확장이 제대로 동작하지 않는다.

    vundle을 사용할때 기준으로 ~/.vim/bundle/python-mode/pymode/environment.py 파일을 수정해야 한다.

    아래는 patch 내용이다.

    --- pymode/environment.py_org    2014-01-06 08:44:00.971905800 +0900
    +++ pymode/environment.py    2014-01-06 09:21:12.068213500 +0900
    @@ -6,6 +6,7 @@
     import json
     import time
     import os.path
    +import platform
    
     from .utils import PY2
    
    @@ -199,11 +200,19 @@
    
             """
    
    -        if dumps:
    -            value = json.dumps(value)
    +        if platform.system() != 'Windows':
    +            if dumps:
    +                value = json.dumps(value)
    +
    +            if PY2:
    +                value = value.decode('utf-8').encode(self.options.get('encoding'))
    +        else:
    +            # win32 patch
    +            if dumps:
    +                value = json.dumps(value, ensure_ascii=False, encoding='cp949').encode('cp949')
    
    -        if PY2:
    -            value = value.decode('utf-8').encode(self.options.get('encoding'))
    +            if PY2:
    +                value = value.decode('cp949').encode(self.options.get('encoding'))
    
             return value
    
    environmemt-win32.path

    2013년 9월 14일

    Python/pip로 설치한 패키지 업그레이드

    pip를 이용해 설치한 패키지를 한번에 업그레이드 하려면 아래와 같이 python 스크립트를 실행한다.
    python -c "import pip, subprocess; [subprocess.call('pip install -U ' + d.project_name, shell=1) for d in pip.get_installed_distributions() if d.location.find('site-packages') != -1]"
    

    2013년 8월 30일

    Python/Spiral Array

    원문

    문제는 다음과 같습니다:

    6 6
    
      0   1   2   3   4   5
     19  20  21  22  23   6
     18  31  32  33  24   7
     17  30  35  34  25   8
     16  29  28  27  26   9
     15  14  13  12  11  10
    

    위처럼 6 6이라는 입력을 주면 6 X 6 매트릭스에 나선형 회전을 한 값을 출력해야 합니다.

    풀이

    #!/usr/bin/python
    # -*- coding: utf-8 -*-
    
    def fill_round(matrix, num, mx, my, base_x, base_y):
        ix = base_x
        iy = base_y
    
        # LR 진행
        for n in range(0, mx):
            matrix[iy][ix] = num
            num = num + 1
            ix = ix + 1
        ix = ix - 1
        mx = mx - 1
    
        # UD 진행
        iy = iy + 1
        for n in range(1, my):
            matrix[iy][ix] = num
            num = num + 1
            iy = iy + 1
        iy = iy - 1
        my = my - 1
    
        if my < 1:
            return
    
        # RL 진행
        ix = ix - 1
        for n in range(mx - 1, -1, -1):
            matrix[iy][ix] = num
            num = num + 1
            ix = ix - 1
        ix = ix + 1
        mx = mx - 1
    
        if mx < 1:
            return
    
        # DU 진행
        iy = iy - 1
        for n in range(my - 1, 0, -1):
            matrix[iy][ix] = num
            num = num + 1
            iy = iy - 1
        return num
    
    def solution(x, y):
        if x < 0 or y < 0:
            return [[]]
        matrix = [[-1 for i in range(0, x)] for j in range(0, y)]
    
        num = 0
        w = x
        h = y
        i = 0
        while True:
            num = fill_round(matrix, num, w, h, i, i)
            i = i + 1
            w = w - 2
            h = h - 2
            if w <= 0 or h <= 0:
                break
        return matrix
    
    matrix = solution(6, 6)
    print '\n'.join([' '.join(['%3u' % n for n in m]) for m in matrix])
    
    print ''
    
    matrix = solution(9, 7)
    print '\n'.join([' '.join(['%3d' % n for n in m]) for m in matrix])
    

    결과

      0   1   2   3   4   5
     19  20  21  22  23   6
     18  31  32  33  24   7
     17  30  35  34  25   8
     16  29  28  27  26   9
     15  14  13  12  11  10
    
      0   1   2   3   4   5   6   7   8
     27  28  29  30  31  32  33  34   9
     26  47  48  49  50  51  52  35  10
     25  46  59  60  61  62  53  36  11
     24  45  58  57  56  55  54  37  12
     23  44  43  42  41  40  39  38  13
     22  21  20  19  18  17  16  15  14
    

    Python/LCD Display

    원문

    한 친구가 방금 새 컴퓨터를 샀다. 그 친구가 지금까지 샀던 가장 강력한 컴퓨터는 공학용 전자 계산기였다. 그런데 그 친구는 새 컴퓨터의 모니터보다 공학용 계산기에 있는 LCD 디스플레이가 더 좋다며 크게 실망하고 말았다. 그 친구를 만족시킬 수 있도록 숫자를 LCD 디스플레이 방식으로 출력하는 프로그램을 만들어보자.

    입력

    입력 파일은 여러 줄로 구성되며 표시될 각각의 숫자마다 한 줄씩 입력된다. 각 줄에는 s와 n이라는 두개의 정수가 들어있으며 n은 출력될 숫자( 0<= n <= 99,999,999 ), s는 숫자를 표시하는 크기( 1<= s < 10 )를 의미한다. 0 이 두 개 입력된 줄이 있으면 입력이 종료되며 그 줄은 처리되지 않는다.

    출력

    입력 파일에서 지정한 숫자를 수평 방향은 '-' 기호를, 수직 방향은 '|'를 이용해서 LCD 디스플레이 형태로 출력한다. 각 숫자는 정확하게 s+2개의 열, 2s+3개의 행으로 구성된다. 마지막 숫자를 포함한 모든 숫자를 이루는 공백을 스페이스로 채워야 한다. 두 개의 숫자 사이에는 정확하게 한 열의 공백이 있어야 한다.

    풀이

    #!/usr/bin/python
    # -*- coding: utf-8 -*-
    
    q = {
        '1': {
            0: lambda c: [c, 0, 0],
            1: lambda c: [0, (c - 1), 1],
            2: lambda c: [c, 0, 0],
            3: lambda c: [0, (c - 1), 1],
            4: lambda c: [c, 0, 0],
        },
        '2': {
            0: lambda c: [1, (c - 2), 1],
            1: lambda c: [0, (c - 1), 1],
            2: lambda c: [1, (c - 2), 1],
            3: lambda c: [1, (c - 1), 0],
            4: lambda c: [1, (c - 2), 1],
        },
        '3': {
            0: lambda c: [1, (c - 2), 1],
            1: lambda c: [0, (c - 1), 1],
            2: lambda c: [1, (c - 2), 1],
            3: lambda c: [0, (c - 1), 1],
            4: lambda c: [1, (c - 2), 1],
        },
        '4': {
            0: lambda c: [c, 0, 0],
            1: lambda c: [1, (c - 2), 1],
            2: lambda c: [1, (c - 2), 1],
            3: lambda c: [0, (c - 1), 1],
            4: lambda c: [c, 0, 0],
        },
        '5': {
            0: lambda c: [1, (c - 2), 1],
            1: lambda c: [1, (c - 1), 0],
            2: lambda c: [1, (c - 2), 1],
            3: lambda c: [0, (c - 1), 1],
            4: lambda c: [1, (c - 2), 1],
        },
        '6': {
            0: lambda c: [1, (c - 2), 1],
            1: lambda c: [1, (c - 1), 0],
            2: lambda c: [1, (c - 2), 1],
            3: lambda c: [1, (c - 2), 1],
            4: lambda c: [1, (c - 2), 1],
        },
        '7': {
            0: lambda c: [1, (c - 2), 1],
            1: lambda c: [0, (c - 1), 1],
            2: lambda c: [c, 0, 0],
            3: lambda c: [0, (c - 1), 1],
            4: lambda c: [c, 0, 0],
        },
        '8': {
            0: lambda c: [1, (c - 2), 1],
            1: lambda c: [1, (c - 2), 1],
            2: lambda c: [1, (c - 2), 1],
            3: lambda c: [1, (c - 2), 1],
            4: lambda c: [1, (c - 2), 1],
        },
        '9': {
            0: lambda c: [1, (c - 2), 1],
            1: lambda c: [1, (c - 2), 1],
            2: lambda c: [1, (c - 2), 1],
            3: lambda c: [0, (c - 1), 1],
            4: lambda c: [1, (c - 2), 1],
        },
        '0': {
            0: lambda c: [1, (c - 2), 1],
            1: lambda c: [1, (c - 2), 1],
            2: lambda c: [c, 0, 0],
            3: lambda c: [1, (c - 2), 1],
            4: lambda c: [1, (c - 2), 1],
        },
    }
    
    def print_x(s, nums):
        if s <= 0:
            return
        c = s + 2
        r = s * 2 + 3
    
        print ''.join([' ' * q[n][0](c)[0] + '-' * q[n][0](c)[1] + ' ' * q[n][0](c)[2] + ' ' for n in nums])
        print '\n'.join([''.join(['|' * q[n][1](c)[0] + ' ' * q[n][1](c)[1] + '|' * q[n][1](c)[2] + ' ' for n in nums]) for i in xrange(0, r / 2 - 1)])
        print ''.join([' ' * q[n][2](c)[0] + '-' * q[n][2](c)[1] + ' ' * q[n][2](c)[2] + ' ' for n in nums])
        print '\n'.join([''.join(['|' * q[n][3](c)[0] + ' ' * q[n][3](c)[1] + '|' * q[n][3](c)[2] + ' ' for n in nums]) for i in xrange(0, r / 2 - 1)])
        print ''.join([' ' * q[n][4](c)[0] + '-' * q[n][4](c)[1] + ' ' * q[n][4](c)[2] + ' ' for n in nums])
    
    print_x(2, '1234567890')
    print ''
    print_x(4, '1234567890')
    

    실행 결과는 아래와 같다.

          --   --        --   --   --   --   --   --
       |    |    | |  | |    |       | |  | |  | |  |
       |    |    | |  | |    |       | |  | |  | |  |
          --   --   --   --   --        --   --
       | |       |    |    | |  |    | |  |    | |  |
       | |       |    |    | |  |    | |  |    | |  |
          --   --        --   --        --   --   --
    
            ----   ----          ----   ----   ----   ----   ----   ----
         |      |      | |    | |      |           | |    | |    | |    |
         |      |      | |    | |      |           | |    | |    | |    |
         |      |      | |    | |      |           | |    | |    | |    |
         |      |      | |    | |      |           | |    | |    | |    |
            ----   ----   ----   ----   ----          ----   ----
         | |           |      |      | |    |      | |    |      | |    |
         | |           |      |      | |    |      | |    |      | |    |
         | |           |      |      | |    |      | |    |      | |    |
         | |           |      |      | |    |      | |    |      | |    |
            ----   ----          ----   ----          ----   ----   ----
    

    2012년 8월 13일

    Python/ctypes in Win32

    ctypes를 이용해 Win32 환경에서 작업하는 예제 기록.

    자료구조

    ctypes vs Win32

    ctypes.wintypes를 참고한다.

    배열

    WCHAR [1000]의 배열을 선언한다고하면

    FileNameType = c_wchar * 1000
    a = FileNameType()

    익명 구조체/공용체 선언

    SYSTEM_INFO 구조체는 아래와 같이 역명 구조체와 공용체를 가지고 있다.

    typedef struct _SYSTEM_INFO {
    	union {
    		DWORD  dwOemId;
    		struct {
    			WORD wProcessorArchitecture;
    			WORD wReserved;
    		};
    	};
    	DWORD     dwPageSize;
    	LPVOID    lpMinimumApplicationAddress;
    	LPVOID    lpMaximumApplicationAddress;
    	DWORD_PTR dwActiveProcessorMask;
    	DWORD     dwNumberOfProcessors;
    	DWORD     dwProcessorType;
    	DWORD     dwAllocationGranularity;
    	WORD      wProcessorLevel;
    	WORD      wProcessorRevision;
    } SYSTEM_INFO;

    이 구조체를 ctypes로 표시하면 아래와 같다.

    class _Noname1(ctypes.Structure):
        _fields_ = [("wProcessorArchitecture", ctypes.c_ushort),
                    ("wReserved", ctypes.c_short)]
    
    
    class _Noname2(ctypes.Union):
        _anonymous_ = ("s",)
        _fields_ = [('dwOemId', ctypes.c_ulong),
                    ('s', _Noname1)]
    
    
    class SYSTEM_INFO(ctypes.Structure):
        _anonymous_ = ("u",)
        _fields_ = [("u", _Noname2),
                    ("dwPageSize", ctypes.c_ulong),
                    ("lpMinimumApplicationAddress", ctypes.c_void_p),
                    ("lpMaximumApplicationAddress", ctypes.c_void_p),
                    ("dwActiveProcessorMask", ctypes.c_ulong),  # 64 bit에서는 c_longlong이 되어야 한다.
                    ("dwNumberOfProcessors", ctypes.c_ulong),
                    ("dwProcessorType", ctypes.c_ulong),
                    ("dwAllocationGranularity", ctypes.c_ulong),
                    ("wProcessorLevel", ctypes.c_ushort),
                    ("wProcessorRevision", ctypes.c_ushort)]

    pointer type 선언

    위에서 선언한 SYSTEM_INFO에 대한 pointer type으로 LPSYSTEM_INFO을 선언한다고 하면

    LPSYSTEM_INFO = ctypes.POINTER(SYSTEM_INFO)

    ctypes.LP_c_char를 문자열로 변환

    ctypes.LP_c_charctypes.c_char_p로 형변환하면 된다.

    ctypes.cast(ctypes.LP_c_char 객체, ctypes.c_char_p).value

    함수 호출

    Win32 API 호출

    ctypes.windll뒤에 원하는 dll 모듈과 함수를 사용하면 된다. 예를 들어 kernel32GetsystemInfo함수를 호출한다면 아래와 같이 호출할 수 있다.

    ctypes.windll.kernel32.GetsystemInfo( ... )

    DLL 함수 호출

    test1.dllvoid __cdecl testfunction1() 함수를 호출한다고 하면

    test1 = ctypes.CDLL('test1.dll')
    if test1:
    	test1.testfunction1()

    함수 인자로 pointer 전달

    si = SYSTEM_INFO()
    ctypes.windll.kernel32.GetSystemInfo(ctypes.byref(si))

    함수 인자로 string buffer 전달

    buf = ctypes.create_unicode_buffer(4096)
    r = ctypes.windll.kernel32.GetWindowsDirectoryW(buf, 4096)
    if r > 0:
    	print buf.value

    함수 반환값 검사

    함수 객체의 errcheck를 지정하면 함수의 반환값 검사를 모아서 할 수 있다. 예를 들어 test2.dllBOOL __cdecl testfunction2() 함수에 대해서 코드를 작성해보면 아래와 같다.

    >def checkBOOL(result, function, args):
        if result == 0:
    		raise ctypes.WinError()
    	return args
    
    test2 = ctypes.CDLL('test2.dll')
    test2.testfunction2.errcheck = checkBOOL
    
    test2.testfunction2()  # 함수 호출이 끝나면 바로 checkBOOL 함수가 호출되서 반환값 검사를 할 수 있다.

    명시적인 함수 인자 지정

    함수 객체의 argtypes를 이용해서 함수의 인자를 명시적으로 지정할 수 있다. 예를 들어 test3.dllBOOL __cdecl testfunction3(LPCWSTR, LPBOOL)에 대해서 코드를 작성해보면 아래와 같다.

    test3 = ctypes.CDLL('test3.dll')
    test3.testfunction3.argtypes = [ctypes.c_wchar_p, ctypes.POINTER(ctypes.c_long)]
    
    b = ctypes.c_long()
    r = test3.testfunction3(u"hello, world!", ctype.byref(b))

    명시적인 함수 반환형 지정

    함수 객체의 restype을 시용해서 함수의 반환형을 명시적으로 지정할 수 있다(함수 반환형이 void라면 None을 사용한다). 예를 들어 test4.dllHANDLE __cdecl testfunction4()에 대해서 코드를 작성해보면 아래와 같다.

    test4 = ctypes.CDLL('test4.dll')
    test4.testfunction4.restype = ctypes.c_void_p
    
    h = test4.testfunction4()

    callback 함수

    callback 함수 형식에 따라 ctypes.CFUNCTYPE 또는 ctypes.WINFUNCTYPE을 사용해서 callback 함수 형을 만들면 된다.

    CFUNCTYPE

    python 문서에 나온 예제를 Win32에 맞게 변형했다.

    CMPFUNC = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_int))
    
    
    def py_cmp_func(a, b):
        print 'py_cmp_func', a[0], b[0]
        return 0
    
    cmp_func = CMPFUNC(py_cmp_func)
    
    IntArray5 = ctypes.c_int * 5
    ia = IntArray5(5, 1, 7, 33, 99)
    qsort = ctypes.windll.msvcrt.qsort
    qsort.restype = None
    qsort(ia, len(ia), ctypes.sizeof(ctypes.c_int), cmp_func)

    WINFUNCTYPE

    LF_FACESIZE = 32
    LF_FULLFACESIZE = 64
    
    
    class LOGFONT(ctypes.Structure):
        _fields_ = [
            ('lfHeight', ctypes.c_long),
            ('lfWidth', ctypes.c_long),
            ('lfEscapement', ctypes.c_long),
            ('lfOrientation', ctypes.c_long),
            ('lfWeight', ctypes.c_long),
            ('lfItalic', ctypes.c_byte),
            ('lfUnderline', ctypes.c_byte),
            ('lfStrikeOut', ctypes.c_byte),
            ('lfCharSet', ctypes.c_byte),
            ('lfOutPrecision', ctypes.c_byte),
            ('lfClipPrecision', ctypes.c_byte),
            ('lfQuality', ctypes.c_byte),
            ('lfPitchAndFamily', ctypes.c_byte),
            ('lfFaceName', ctypes.c_wchar * LF_FACESIZE)]
    PLOGFONT = ctypes.POINTER(LOGFONT)
    
    
    class ENUMLOGFONT(ctypes.Structure):
        _fields_ = [
            ('elfLogFont', LOGFONT),
            ('elfFullName', ctypes.c_wchar * LF_FULLFACESIZE),
            ('elfStyle', ctypes.c_wchar * LF_FACESIZE)]
    PENUMLOGFONT = ctypes.POINTER(ENUMLOGFONT)
    
    
    if ctypes.sizeof(ctypes.c_long) == ctypes.sizeof(ctypes.c_void_p):
        LPARAM = ctypes.c_long
    elif ctypes.sizeof(ctypes.c_longlong) == ctypes.sizeof(ctypes.c_void_p):
        LPARAM = ctypes.c_longlong
    
    #int CALLBACK EnumFontFamProc(ENUMLOGFONT *lpelf,__in  NEWTEXTMETRIC *lpntm, DWORD FontType, LPARAM lParam;
    EnumFontFamProc = ctypes.WINFUNCTYPE(ctypes.c_int, PENUMLOGFONT, ctypes.c_void_p, ctypes.c_long, LPARAM)
    
    
    def py_enum_font_fam_proc(lpelf, lpntm, FontType, lparam):
        print 'py_enum_font_fam_proc', lpelf.contents.elfFullName
        return 1
    
    enum_font_proc = EnumFontFamProc(py_enum_font_fam_proc)
    
    EnumFontFamilies = ctypes.windll.gdi32.EnumFontFamiliesW
    hdc = ctypes.windll.user32.GetDC(0)
    EnumFontFamilies(hdc, 0, enum_font_proc, 0)
    ctypes.windll.user32.ReleaseDC(hdc)

    2011년 7월 5일

    OS/X - mac port로 pypy 설치하기

    이 문서는 OS/X에서 mac port를 사용해 pypy를 설치하는 방법에 대해서 기술한다.

    python 2.7 설치

    pypy를 사용하기 위해서는 python 2.7을 설치해야 한다. mac port를 이용해서 python 2.7을 설치한다port를 이용해서 python 2.7용 PyQt를 설치하려면 sudo port install py27-pyqt4 하면 된다..

    $ sudo port install python27
    $ sudo port select --set python python27

    python 2.7을 위해서 setuptools도 설치해 둔다.

    $ sudo port install py27-setuptools

    pypy 설치

    아래 명령으로 간단하게 pypy를 설치한다.

    $ sudo port install pypy

    2010년 6월 6일

    OS/X - PyQt 설치

    이 문서는 Snow Leopard에서 PyQt를 설치하는 방법을 기술한다.

    준비

    아래 경로에서 Cocoa 버전의 QT 를 다운 받는다.

    http://qt.nokia.com/downloads/qt-for-open-source-cpp-development-on-mac-os-x

    아래 경로에서 PyQt를 다운 받는다.

    http://www.riverbankcomputing.co.uk/software/pyqt/download

    아래 경로에서 sip를 다운 받는다.

    http://www.riverbankcomputing.co.uk/software/sip/download

    PyQt 빌드 및 설치

    sip

    우선 터미널에서 아래와 같이 sip를 빌드하고 설치한다.

    $ export MACOSX_DEPLOYMENT_TARGET=10.6
    $ python configure.py --universal --arch=i386 --arch=x86_64 -s MacOSX10.6.sdk
    $ make
    $ sudo make install

    PyQt 빌드

    $ export QTDIR=/Developer/Applications/Qt
    $ python configure.py --confirm-license --use-arch=i386 --use-arch=x86_64
    $ make
    $ sudo make install

    테스트

    아래와 같은 python 코드를 저장하고 실행해본다

    import sys
    from PyQt4 import QtCore, QtGui
    
    
    def translate(widgetname, defstr):
        return QtGui.QApplication.translate(widgetname, defstr)
    
    
    class Sample(QtGui.QMainWindow):
        def __init__(self, parent=None):
            super(Sample, self).__init__(parent)
    
            self.setWindowTitle(translate(u'mainwindow', u'Sample'))
            self.resize(250, 150)
            self.setCenter()
    
            self.statusBar().showMessage(translate(u'mainwindow', u'Ready'))
    
            workarea = QtGui.QWidget(self)
    
            quit = QtGui.QPushButton(translate(u'mainwindow', u'Close'), workarea)
            workarea.connect(quit, QtCore.SIGNAL('clicked()'), self, QtCore.SLOT('close()'))
            quit.setToolTip(translate(u'mainwindow', u'This is a close button.'))
            QtGui.QToolTip.setFont(QtGui.QFont(u'Tahoma', 10))
    
            hbox = QtGui.QHBoxLayout()
            hbox.addStretch(1)
            hbox.addWidget(quit)
    
            vbox = QtGui.QVBoxLayout()
            vbox.addStretch(1)
            vbox.addLayout(hbox)
            workarea.setLayout(vbox)
            self.setCentralWidget(workarea)
    
        def closeEvent(self, event):
            reply = QtGui.QMessageBox.question(
                self,
                translate(u'messagebox', u'Question'),
                translate(u'messagebox', u'Are you sure to quit?'),
                QtGui.QMessageBox.Yes,
                QtGui.QMessageBox.No)
    
            if QtGui.QMessageBox.Yes == reply:
                event.accept()
            else:
                event.ignore()
    
        def setCenter(self):
            screen = QtGui.QDesktopWidget().screenGeometry()
            size = self.geometry()
            self.move((screen.width() - size.width()) / 2, (screen.height() - size.height()) / 2)
    
    if __name__ == '__main__':
        app = QtGui.QApplication(sys.argv)
        main = Sample()
        main.show()
        sys.exit(app.exec_())

    실행하면 아래와 같은 화면이 표시된다.

    2010년 3월 1일

    기온 변화 그래프 2

    기상청 홈페이지가 개편되면서 날씨 정보에 대한 xml를 제공하기 시작했습니다. 그러나 기온 변화 그래프에서 필요한 현재 시간 날씨 정보에 대해서는 xml 데이타를 제공하지 않습니다.

    기온 변화 그래프를 계속 그리기 위해서 기상청 웹페이지를 해석해서 그래프로 그리는 간단한 python 스크립트를 작성해봤습니다.

    이 스크립트는 기상청 페이지를 가져와서 xml로 변환하는 buildwexml.py와 그래프를 그리는 gengraph.py 두 부분으로 나뉘어 있습니다.

    이 스크립트는 Debian Linux, python 2.5 환경에서 제작, 테스트 되었습니다.

    코드는 아래와 같습니다

    gengraph.py

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    """gengraph.py
    
    buildwexml.py를 이용해 생성한 xml 파일을 가지고 그래프를 그리는 스크립트
    """
    __author__ = 'Yun-yong Choi'
    __version__ = '0.1'
    
    import time
    import xml.etree.ElementTree
    
    import Gnuplot, Gnuplot.funcutils
    
    import buildwexml
    
    # 데이타를 저장할 경로
    write_root = '/tmp/'
    # 그래프 파일 경로
    graph_filename = '/home/markboy/temperature_graph.png'
    writeinfo = {
          u'서울':[u'seoul.dat', u'Seoul'],
          u'부산':[u'busan.dat', u'Busan'],
          u'제주':[u'jeju.dat', u'Jeju'],
    }
    findcities = [ u'서울', u'부산', u'제주' ]
    
    # web에서 xml 정보를 가져온다.
    buildwexml.build(write_root + 'current.xml')
    
    #xml을 해석한다.
    xmlobj = xml.etree.ElementTree.parse(write_root + 'current.xml')
    timestr = time.strftime('%Y/%m/%d %H', 
                    time.strptime(xmlobj.getiterator('datetime')[0].attrib['data'], '%Y.%m.%d.%H:%S') )
    # city node를 순회한다.
    for city in  xmlobj.getiterator('city'):
        # 찾는 도시가 있으면
        if city.attrib['name'] in findcities :
            # 기온값을 저장한다.
            f = file(write_root + writeinfo[city.attrib['name']][0], 'at')
            f.write("%s %s\n" % (timestr, city.find('temperature').find('now').attrib['data']))
            f.close()
    # 일주일분만 표시하기 위해서 7일전 날짜를 구한다.
    ago = time.time() - 60 * 60 * 24 * 7
    ago_str = time.strftime("%Y/%m/%d", time.localtime(ago) )
    
    # plot 파일 저장
    f = file(write_root + 'temperature_graph.plot', 'wt')
    f.write("""
    set term png small
    set size 1.0, 0.6
    set output '%s'
    set grid
    set key left bottom
    set ylabel \"Temperature(C)\"
    
    set xdata time
    set timefmt \"%%Y/%%m/%%d\"
    set xrange [\"%s\":]
    set timefmt \"%%Y/%%m/%%d %%H\"
    set format x \"%%m/%%d\"
    
    plot """ % (graph_filename, ago_str) )
    
    for key in writeinfo.keys() :
        f.write("'%s' using 1:3 title \"%s\" with line" % (write_root + writeinfo[key][0], writeinfo[key][1]))
        if key <> writeinfo.keys()[-1] :
            f.write(', ')
    f.close()
    # 그래프 그리기
    g = Gnuplot.Gnuplot()
    g.load(write_root + 'temperature_graph.plot')

    buildwexml.py

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    """ buildwexml.py
    
    기상청의 현재 날씨 정보 페이지를 가져와서 xml 파일을 만드는 스크립트
    """
    __author__ = 'Yun-yong Choi'
    __version__ = '0.2'
    
    import os
    import sys
    import codecs
    import urllib2
    
    def getWebpage(url, referer='') :
        """url 파일을 읽어온다"""
        debug = 0
        if debug :
            return file(url.split('/')[-1], 'rt').read()
        else :
            opener = urllib2.build_opener()
            opener.addheaders = [
                ('User-Agent', 'Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US)'),
                ('Referer', referer),
            ]
            return opener.open(url).read()
    
    def getMainPage():
        return getWebpage('http://www.kma.go.kr/weather/observation/currentweather.jsp')
    
    def getDataPage():
        return getWebpage('http://www.kma.go.kr/weather/observation/currentweather_data.jsp', 'http://www.kma.go.kr/weather/observation/currentweather.jsp' )
    
    def normalize(s):
        """<td> tag와  를 제거한 문자열을 돌려준다.
        
            s - 입력 문자열
        """
        return s.replace('<td>', '').replace('</td>', '').replace(' ', '')
    
    def printUsing():
        """사용 방법을 출력한다"""
        print sys.argv[0], '<output file name>'
        
    def getDateTime(buffers):
        """ html 내용을 해석해서 데아타가 생성된 날짜를 얻는다. 날짜 형식은 yyyy.mm.dd.HH:SS 이다.
        
            buffers - html 파일 내용
        """
        return buffers.split('<p class="table_topinfo">')[1].split('</p>')[0].split('/>')[-1]
    
        
    def getDatablocks(buffers):
        """html 내용을 해석해서 도시별로 묶은 list를 돌려준다
        
            buffers - html 파일 내용
        """
        # <table class="table_develop 앞부분을 잘라낸다.
        a = buffers.split('<table class="table_develop"')[1]
        # 맨 처음 만나는 </table>을 기준으로 뒤를 잘라낸다.
        b = a.split('</table>')[0]
        # </thread>를 기준으로 앞을 잘라낸다.
        c = b.split('</thead>')[1].replace('<tr>', '')
        # 빈 줄 제거
        r = ''
        for line in c.split('\n') :
            line = line.decode('cp949').encode('utf-8')
            line = line.strip()
            if len(line) > 0 :
                r = r + line + '\n'
        # </tr> 기준으로 잘라내면 데이타 block이 완성된다.
        return r.split('</tr>\n')[:-1] # 마지막 block은 버린다.
    
    def writeXMLheader(out):
        """XML header를 기록한다."""
        out.write('<?xml version="1.0" encoding="UTF-8"?>\n')
        out.write('<xml>\n')
        out.write('  <current_weather>\n')
        
    def writeXMLfooter(out):
        """XML footer를 기록한다."""
        out.write('  </current_weather>')
        out.write('</xml>')
        
    def writeXMLnode(out, datablock):
        data = datablock.split('\n') # 0번째 <tr>부분은 버린다.
        # 0 - 이름
        city = data[0].split('</a>')[0].split('>')[2]
        out.write("""    <city name="%s">
          <!-- 날씨 -->
          <weather>
            <!-- 현재일기 -->
            <now data="%s" />
            <!-- 시정(km) -->
            <visibility data="%s" />
            <!-- 운량 (1/10) -->
            <clouds data="%s" />
            <!-- 중하운량 -->
            <ml_clouds data="%s" />
          </weather>
          <!-- 기온 -->
          <temperature>
            <!-- 현재 기온 -->
            <now data="%s" />
            <!-- 이슬점온도 -->
            <dew_point_c data="%s" />
            <!-- 체감 온도 -->
            <wind_chill_c data="%s" />
          </temperature>
          <!-- 강수 -->
          <precipitation>
            <!-- 일강수(mm) -->
            <now data="%s" />
            <!-- 적설 (cm) -->
            <snow_cover data="%s" />
            <!-- 습도 (%%) -->
            <humidity data="%s" />
          </precipitation>
          <!-- 바람 -->
          <wind>
            <!-- 풍향 -->
            <direction data="%s" />
            <!-- 풍속(m/sec) -->
            <speed data="%s" />
          </wind>
          <!-- 기압 (hPa) -->
          <atmospheric_pressure>
            <!-- 해면 기압 -->
            <see_level data="%s" />
          </atmospheric_pressure>
        </city>
    """ % (city,
        normalize(data[1]),
        normalize(data[2]),
        normalize(data[3]),
        normalize(data[4]),
        normalize(data[5]),
        normalize(data[6]),
        normalize(data[7]),
        normalize(data[8]),
        normalize(data[9]),
        normalize(data[10]),
        normalize(data[11]),
        normalize(data[12]),
        normalize(data[13]) ) )
    
    def build(outputname) :
        """ 기상청 사이트에서 현재 날씨 정보를 읽어와서 xml로 저장한다.
    
            outputname - 저장할 파일 이름
        """
        out = file(outputname, 'wt')
        writeXMLheader(out)
        out.write('    <datetime data="%s" />\n' % getDateTime(getMainPage()))
    
        for datablock in getDatablocks(getDataPage()) :
            writeXMLnode(out, datablock)
        writeXMLfooter(out)
    
    if __name__ == '__main__' :
        if len(sys.argv) <> 2 :
            printUsing()
            sys.exit(1)
        build(sys.argv[1])

    2010년 1월 12일

    Python/사진 파일의 시간을 사진을 찍은 시간으로 변경하기

    사진 파일(jpg)의 시간을 사진을 찍은 시간으로 변경하는 python script. EXIF.py를 사용한다.

    #!/usr/bin/python
    # -*- coding: utf-8 -*-
    """jpg 파일 변경 시간을 exif 에서 읽어온 시간으로 변경한다."""
    __version__ = '0.1'
    
    import os
    import sys
    import time
    import dircache
    
    import EXIF
    
    # 허가할 확장자 목록
    EXTENSIONS = ('.jpg', '.jpeg')
    
    # 사진을 찍은 시간을 얻을 필드. 처음 발견하는 필드를 사용한다.
    TAGS = ['Image DateTime', 'EXIF DateTimeOriginal', 'DateTime']
    
    
    def printUsing():
        """사용 방법을 출력한다.
        """
        print sys.argv[0], '<file or directory>'
    
    
    def EXIFDateTime2time(exifdatetime):
        """exif의 시간 문자열을 time 값으로 변경한다.
    
            @param[in]  exifdatetime    시간 문자열
    
            @return     변환된 time 값
        """
        return time.mktime(time.strptime(exifdatetime, '%Y:%m:%d %H:%M:%S'))
    
    
    def correctJPGTime(filename):
        """jpg 파일 변경 시간을 수정한다.
    
            @param[in]  filename    수정할 파일 이름
        """
        fnl = filename.lower()
        for ext in EXTENSIONS:
            # 파일 확장자 검사
            if fnl.endswith(ext):
                f = file(filename, 'rb')
                # exif tag를 읽는다.
                tags = EXIF.process_file(f, details=False)
                for key in TAGS:
                    if key in tags:
                        # TAGS 중 처음 발견한 tag의 시간 값을 사용한다.
                        exif_time = EXIFDateTime2time(str(tags[key]))
                        statinfo = os.stat(filename)
                        if exif_time != statinfo.st_mtime:
                            # 시간이 다르면 파일 시간을 변경한다.
                            os.utime(filename, (statinfo.st_atime, exif_time))
                            print 'fix:', filename
                        break
                break
    
    
    def main():
        if os.path.isdir(sys.argv[1]):
            # 주어진 인자가 디렉토리면
            for name in dircache.listdir(sys.argv[1]):
                # 디렉토리의 파일들에 대해서 수정 작업을 한다.
                # sub directory에 대해서는 아무 작업도 하지 않는다.
                fullname = sys.argv[1] + os.path.sep + name
                if os.path.isfile(fullname):
                    correctJPGTime(fullname)
        elif os.path.isfile(sys.argv[1]):
            # 주어진 인자가 파일이면
            correctJPGTime(sys.argv[1])
    
    
    if __name__ == '__main__':
        if len(sys.argv) < 2:
            printUsing()
            sys.exit(1)
        main()

    2009년 12월 1일

    Python/자동 링크 생성

    input에서 http 또는 https 링크를 찾아서 a tag를 붙여주는 python script

    import re
    
    re_href= re.compile('(\b(http|https)://([-A-Za-z0-9+&@#/%?=~_()|!:,.;]*[-A-Za-z0-9+&@#/%=~_()|]))')
    output = re_href.sub(r'<a href="\1" target="_self">\1</a>', input)

    2009년 6월 11일

    Python/피보나치 수열 구하기

    12345678999(123억...)과 99987654321(999억...) 사이의 피보나치 수를 모두 더하면 얼마인가 에 대한 풀이를 해보았다. (원문 )

    수의 범위가 매우 크기 때문에 루프 대신 generator를 사용했다. (루프로 코드를 짜보았지만 Quad Core에서도 오랜 시간 계산이 끝나지 않아서 포기했다)

    import cProfile
    import itertools
    
    
    def Fibonacci_generator():
        """ 피보나치 수를 구하는 generator """
        yield 0
        yield 1
        prev1 = 0  # n-2
        prev2 = 1  # n-1
        result = 0
        while True:
            try:
                result = prev1 + prev2
                yield result
                prev1 = prev2
                prev2 = result
            except OverflowError:
                break
    
    
    def solve(start, end):
        """ star ~ end 사이의 피보나치 수의 합을 구하는 함수."""
        total = 0L
        for fib in Fibonacci_generator():
            if fib >= end:
                break
            if fib > start:
                total += fib
        return total
    
    
    def solve2(start, end):
        """ star ~ end 사이의 피보나치 수의 합을 구하는 함수."""
        return sum(itertools.takewhile(
            lambda fib: fib < end,
            itertools.dropwhile(lambda fib: fib > start,
                                Fibonacci_generator())))
    
    start = 12345678999L
    end = 99987654321L
    cProfile.run('solve(%d, %d)' % (start, end))
    print solve(start, end)
    cProfile.run('solve2(%d, %d)' % (start, end))
    print solve2(start, end)

    결과는 아래와 같다.

    $python fibo.py
             60 function calls in 0.000 CPU seconds
    
       Ordered by: standard name
    
       ncalls  tottime  percall  cumtime  percall filename:lineno(function)
            1    0.000    0.000    0.000    0.000 <string>:1(<module>)
            1    0.000    0.000    0.000    0.000 fibonachi.py:25(solve)
           57    0.000    0.000    0.000    0.000 fibonachi.py:9(Fibonacci_generator)
            1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
    
    
    205486422643
             118 function calls in 0.000 CPU seconds
    
       Ordered by: standard name
    
       ncalls  tottime  percall  cumtime  percall filename:lineno(function)
            1    0.000    0.000    0.000    0.000 <string>:1(<module>)
            1    0.000    0.000    0.000    0.000 fibonachi.py:35(solve2)
            6    0.000    0.000    0.000    0.000 fibonachi.py:37(<lambda>)
           51    0.000    0.000    0.000    0.000 fibonachi.py:38(<lambda>)
           57    0.000    0.000    0.000    0.000 fibonachi.py:9(Fibonacci_generator)
            1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
            1    0.000    0.000    0.000    0.000 {sum}
    
    
    205486422643
    

    2009년 5월 19일

    Python/py2exe를 이용해서 win32 binary를 만들때 manifest 추가

    Windows 환경에서 py2exe 를 이용해서 exe를 만들때 manifest 를 추가할 수 있다.

    setup.py를 아래와 같이 만든다.

    manifest = """
    <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
      <asmv3:trustInfo xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
        <asmv3:security>
          <asmv3:requestedPrivileges>
            <asmv3:requestedExecutionLevel level="asInvoker" uiAccess="false" />
          </asmv3:requestedPrivileges>
        </asmv3:security>
      </asmv3:trustInfo>
    </assembly>
    """
    
    setup(name='MyApp',
        #...
        windows=[ { #...
            'other_resources':[(24, 1, manifest)],
        }]
    )

    2009년 5월 12일

    Python/PyGTK

    PyGTK 예제 소스를 순수 API를 호출하는 방법과 GtkBuilder를 사용한 방법으로 각각 구현해본다.

    Hello, World

    아래와 같은 모습을 가지도록 프로그램을 수정한다.

    API 호출

    코드

    hello.py

    
    #!/usr/bin/python
    # -*- coding: utf-8 -*-
    import gtk
    
    class HelloWorld :
        def hello(self, widget, data=None) :
            print 'Hello, World'
    
        def delete_event(self, widget, event, data=None):
            print 'delete event occurred'
            return False
    
        def destroy(self, widget, data=None) :
            gtk.main_quit()
    
        def __init__(self) :
            self._win = gtk.Window(gtk.WINDOW_TOPLEVEL)
            self._win.connect('delete_event', self.delete_event)
            self._win.connect('destroy', self.destroy)
            self._win.set_border_width(10)
            self._btnhello = gtk.Button('Hello World')
            self._btnhello.connect('clicked', self.hello, None)
            self._btnhello.connect_object('clicked', gtk.Widget.destroy, self._win)
            self._win.add(self._btnhello)
    
        def main(self) :
            self._win.show_all()
            gtk.main()
    
    def main() :
        w = HelloWorld()
        w.main()
    
    if __name__ == '__main__' :
        main()

    GtkBuilder

    GtkBuilder를 사용하면 ui 구성 코드를 xml로 분리할 수 있다.

    코드

    hello.xml

    <?xml version="1.0" encoding="UTF-8" standalone="no"?>
    <interface>
      <object class="GtkWindow" id="HelloWindow">
        <property name="border_width">10</property>
        <signal name="delete_event" handler="delete_event" />
        <signal name="destroy" handler="destroy" />
        <child>
          <object class="GtkButton" id="BtnHello">
            <property name="label">Hello, world</property>
            <signal name="clicked" handler="hello" />
          </object>
        </child>
      </object>
    </interface>

    hello2.py

    >#!/usr/bin/python # -*- coding: utf-8 -*- import gtk class HelloWorld : def hello(self, widget, data=None) : print 'Hello, World' def delete_event(self, widget, event, data=None): print 'delete event occurred' return False def destroy(self, widget, data=None) : gtk.main_quit() def __init__(self) : builder = gtk.Builder() builder.add_from_file('hello2.xml') builder.connect_signals(self) self._win = builder.get_object('HelloWindow') self._btnhello = builder.get_object('BtnHello') self._btnhello.connect_object('clicked', gtk.Widget.destroy, self._win) def main(self) : self._win.show_all() gtk.main() def main() : w = HelloWorld() w.main() if __name__ == '__main__' : main()

    Hello, World 업그레이드

    아래와 같은 모습을 가지도록 프로그램을 수정한다.

    API 호출

    코드

    hello_upgrade.py

    >#!/usr/bin/python # -*- coding: utf-8 -*- import gtk class HelloWorld2 : def callback(self, widget, data) : print 'Hello again - %s was pressed ' % data def delete_event(self, widget, event, data=None): gtk.main_quit() return False def __init__(self) : self._win = gtk.Window(gtk.WINDOW_TOPLEVEL) self._win.set_title('Hello Buttons!') self._win.connect('delete_event', self.delete_event) self._win.set_border_width(10) self._box1 = gtk.HBox(False, 0) self._win.add(self._box1) self._button1 = gtk.Button('Button 1') self._button1.connect('clicked', self.callback, 'button 1') self._box1.pack_start(self._button1, True, True, 0) self._button2 = gtk.Button('Button 2') self._button2.connect('clicked', self.callback, 'button 2') self._box1.pack_start(self._button2, True, True, 0) def main(self) : self._win.show_all() gtk.main() def main() : w = HelloWorld2() w.main() if __name__ == '__main__' : main()

    GtkBuilder

    코드

    hello2_upgrade.xml

    <?xml version="1.0" encoding="UTF-8" standalone="no"?>
    <interface>
      <object class="GtkWindow" id="HelloWindow">
        <property name="title">Hello Buttons!</property>
        <property name="border_width">10</property>
        <signal name="delete_event" handler="delete_event" />
        <child>
          <object class="GtkHBox" id="box1">
            <child>
              <object class="GtkButton" id="button1">
                <property name="label">Button 1</property>
                <signal name="clicked" handler="callback" />
              </object>
            </child>
            <child>
              <object class="GtkButton" id="button2">
                <property name="label">Button 2</property>
                <signal name="clicked" handler="callback" />
              </object>
            </child>
          </object>
        </child>
      </object>
    </interface>

    hello2_upgrade.py

    #!/usr/bin/python
    # -*- coding: utf-8 -*-
    import gtk
    
    class HelloWorld2 :
        def callback(self, widget, data = None) :
            print 'Hello again - %s was pressed ' % widget.get_name()
    
        def delete_event(self, widget, event, data=None):
            gtk.main_quit()
            return False
    
        def __init__(self) :
            builder = gtk.Builder()
            builder.add_from_file('hello2_upgrade.xml')
            builder.connect_signals(self)
            self._win = builder.get_object('HelloWindow')
    
        def main(self) :
            self._win.show_all()
            gtk.main()
    
    def main() :
        w = HelloWorld2()
        w.main()
    
    if __name__ == '__main__' :
        main()

    Reference

    2009년 5월 8일

    Urlencode된 파일 이름을 Decode하기

    Opera에서 파일을 받으면 한글이름일 경우 urlencode 된 이름으로 저장된다. decode 하는 간단한 스크립트를 작성했다

    #!/usr/bin/python
    # -*- coding: utf-8 -*-
    
    import os
    import sys
    import urllib
    
    
    def printUsing():
        """사용 방법을 출력한다."""
        print 'fixname.py  [  ...]'
    
    
    def getUnquoteFileName(name):
        try:
            return unicode(urllib.unquote(name).decode('utf-8'))
        except UnicodeDecodeError:
            return unicode(urllib.unquote(name).decode('cp949'))
    
    
    def main():
        for argv in filter(os.path.isfile, sys.argv[1:]):
            decoded_argv = getUnquoteFileName(argv)
            if argv != decoded_argv:
                print '%s -> %s' % (argv, decoded_argv)
                os.rename(argv, decoded_argv)
    
    if __name__ == '__main__':
        if len(sys.argv) < 2:
            printUsing()
        else:
            main()

    2006년 12월 1일

    Python/socket에 timeout 설정

    출처는 http://www.voidspace.org.uk/python/articles/urllib2.shtml

    import socket
    import urllib2
    
    # timeout in seconds
    timeout = 10
    socket.setdefaulttimeout(timeout)
    
    # this call to urllib2.urlopen now uses the default timeout
    # we have set in the socket module
    req = urllib2.Request('http://www.voidspace.org.uk')
    response = urllib2.urlopen(req)

    Python/map, zip, izip, loop, generator 속도 비교

    코드

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    from itertools import izip, islice
    
    
    # normal loop
    def using_loop(ary):
        result = []
        for i in range(len(ary) - 1):
            result.append((ary[1], ary[i + 1]))
        return result
    
    
    # built-in map()
    def using_map(ary):
        return map(None, ary[:-1], ary[1:])
    
    
    # built-in zip() 을 사용하는 방법
    def using_zip(ary):
        return zip(ary, ary[1:])
    
    
    # itertools를 사용하는 방법
    def using_izip(ary):
        return [(x, y) for x, y in izip(ary, islice(ary, 1, None))]
    
    
    # generator를 사용하는 방법
    def using_gen(ary):
        return [interval for interval in intervals(ary)]
    
    
    def intervals(it):
        it = iter(it)
        st = it.next()
        for en in it:
            yield (st, en)
            st = en
    
    
    def doit():
        SRCLIST = range(1, 1000000)
        using_loop(SRCLIST)
        using_map(SRCLIST)
        using_zip(SRCLIST)
        using_izip(SRCLIST)
        using_gen(SRCLIST)
    
    if __name__ == '__main__':
        import profile
        out = 'tmp.prof'
        profile.run('doit()', out)
        import pstats
        profObj = pstats.Stats(out)
        profObj.sort_stats('cumulative').print_stats()

    결과

    range(1, 100)

    Fri Dec 01 14:00:29 2006    tmp.prof
    
             212 function calls in 0.017 CPU seconds
    
       Ordered by: cumulative time
    
       ncalls  tottime  percall  cumtime  percall filename:lineno(function)
            1    0.001    0.001    0.017    0.017 profile:0(doit())
            1    0.000    0.000    0.015    0.015 <string>:1(?)
            1    0.000    0.000    0.015    0.015 C:\speedtest2.py:35(doit)
            1    0.014    0.014    0.014    0.014 C:\speedtest2.py:23(using_izip)
            1    0.000    0.000    0.001    0.001 C:\speedtest2.py:26(using_gen)
            1    0.000    0.000    0.001    0.001 C:\speedtest2.py:10(using_loop)
            1    0.000    0.000    0.000    0.000 :0(setprofile)
           99    0.000    0.000    0.000    0.000 C:\speedtest2.py:28(intervals)
           98    0.000    0.000    0.000    0.000 :0(append)
            1    0.000    0.000    0.000    0.000 C:\speedtest2.py:17(using_map)
            1    0.000    0.000    0.000    0.000 C:\speedtest2.py:20(using_zip)
            1    0.000    0.000    0.000    0.000 :0(map)
            2    0.000    0.000    0.000    0.000 :0(range)
            1    0.000    0.000    0.000    0.000 :0(zip)
            1    0.000    0.000    0.000    0.000 :0(iter)
            1    0.000    0.000    0.000    0.000 :0(len)
            0    0.000             0.000          profile:0(profiler)
    

    zip() - map() - for loop - generator - izip() 순으로 빠르다.

    range(1, 10000)

    Fri Dec 01 13:57:18 2006    tmp.prof
    
             20012 function calls in 0.157 CPU seconds
    
       Ordered by: cumulative time
    
       ncalls  tottime  percall  cumtime  percall filename:lineno(function)
            1    0.000    0.000    0.157    0.157 profile:0(doit())
            1    0.000    0.000    0.156    0.156 <string>:1(?)
            1    0.003    0.003    0.156    0.156 C:\speedtest2.py:35(doit)
            1    0.047    0.047    0.075    0.075 C:\speedtest2.py:10(using_loop)
            1    0.032    0.032    0.067    0.067 C:\speedtest2.py:26(using_gen)
         9999    0.035    0.000    0.035    0.000 C:\speedtest2.py:28(intervals)
         9998    0.028    0.000    0.028    0.000 :0(append)
            1    0.004    0.004    0.004    0.004 C:\speedtest2.py:23(using_izip)
            1    0.000    0.000    0.003    0.003 C:\speedtest2.py:17(using_map)
            1    0.003    0.003    0.003    0.003 :0(map)
            1    0.000    0.000    0.003    0.003 C:\speedtest2.py:20(using_zip)
            1    0.003    0.003    0.003    0.003 :0(zip)
            1    0.001    0.001    0.001    0.001 :0(setprofile)
            2    0.001    0.000    0.001    0.000 :0(range)
            1    0.000    0.000    0.000    0.000 :0(iter)
            1    0.000    0.000    0.000    0.000 :0(len)
            0    0.000             0.000          profile:0(profiler)
    

    zip() - map() - izip() - generator - for loop 순으로 빠르다.

    range(1, 1000000)

    Fri Dec 01 13:55:11 2006    tmp.prof
    
             2000012 function calls in 22.758 CPU seconds
    
       Ordered by: cumulative time
    
       ncalls  tottime  percall  cumtime  percall filename:lineno(function)
            1    0.000    0.000   22.758   22.758 profile:0(doit())
            1    0.020    0.020   22.757   22.757 <string>:1(?)
            1    0.299    0.299   22.737   22.737 C:\speedtest2.py:35(doit)
            1    5.855    5.855    8.752    8.752 C:\speedtest2.py:10(using_loop)
            1    3.322    3.322    7.997    7.997 C:\speedtest2.py:26(using_gen)
       999999    4.675    0.000    4.675    0.000 C:\speedtest2.py:28(intervals)
       999998    2.862    0.000    2.862    0.000 :0(append)
            1    0.065    0.065    2.213    2.213 C:\speedtest2.py:17(using_map)
            1    2.148    2.148    2.148    2.148 :0(map)
            1    0.033    0.033    1.893    1.893 C:\speedtest2.py:20(using_zip)
            1    1.860    1.860    1.860    1.860 :0(zip)
            1    1.549    1.549    1.549    1.549 C:\speedtest2.py:23(using_izip)
            2    0.070    0.035    0.070    0.035 :0(range)
            1    0.001    0.001    0.001    0.001 :0(setprofile)
            1    0.000    0.000    0.000    0.000 :0(iter)
            1    0.000    0.000    0.000    0.000 :0(len)
            0    0.000             0.000          profile:0(profiler)
    

    izip() - zip() - map() - generator - for loop 순으로 빠르다.