파이썬에서 유니코드 형태의 한글을 정상적으로 표시되도록 하는 방법에 대한 것이다.
아래 변수 a에 다음과 같은 형태의 유니코드 값이 있을 때 이것을 정상적으로 한글로 표시되게 할려면 encode() 함수를 이용해서 byte형으로 변환 후 decode() 함수를 이용해서 unicode_escape 처리를 해 주면 된다.
아래에서 #-*- coding:utf-8 -*-는 파이썬에서 한글을 사용하고자 할때 필요한 코드이다.
비록 주석이라 할지라도 이 코드로 지정하지 않으면 한글이 들어간 코드는 컴파일 단계에서 에러를 내뿜는다.

#-*- coding:utf-8 -*-
a = "\uc790\uc720 \ub300\ud55c\ubbfc\uad6d \ub9cc\uc138"

#유니코드 문자열을 한글로 변환하기 위해서는 먼저 문자열을 encode 함수를 통해
# bytes 형으로 변환하고, decode 함수를 사용하여 'unicode_escape' 처리를 해 주면
# 한글이 제대로 출력된다.

a = a.encode('utf-8')
a = a.decode('unicode_escape')
print a

위 코드의 실행 결과는 아래와 같이 표현된다.

자유 대한민국 만세

python에서 리눅스 shell command를 이용하여 특정 프로그램의 실행 여부 파악하기


리눅스에서 특정 프로그램의 실행 여부를 파악할 때 터미널 창에서 ps 명령어를 이용해서 대개는 확인하게 된다. SocServer라는 이름을 가진 프로그램이 현재 실행중인지 파악할려면 아래와 같이 한다.


ps -elf | grep SocServer*


이렇게 되면 다음과 같은 정보가 보인다면 해당 프로그램은 실행 중이라는 뜻이다.


0 S root     14851  1812  0  80   0 -  8931 inet_c 16:53 pts/18   00:00:00 python /home/joe/PyCharmProj/SocServer.py


여기서 4번째 column의 값 14851이 SocServer.py의 실행 process id이다. 즉 특정 프로그램의 process가 존재하는지를 통해서 해당 프로그램의 실행 여부를 파악할수 있는 것이다.


이 프로그램의 실행을 강제 종료 시킬려면 다음과 같이 하게 될 것이다.


kill -9 14851


위의 기능과 같은 기능을 하되 보다 단순한 정보를 보여주는 shell command가 있는데 pgrep이다.


pgrep -lf SocServer*


를 하면 다음과 같은 보다 간단한 정보를 나타내준다. 


14851 python


이렇게 간단한 정보를 활용하면 파이썬 상에서 코딩하기가 보다 수월해 진다.

아래는 파이썬에서의 특정 프로그램 실행 여부를 확인하는 코드이다.

이를 위해서는 os와 subprocess 파이썬 모듈을 import 시켜야 한다.



import os

import subprocess


# 이미 실행 중인 process가 있으면 다시 실행되지 않도록 하기 위해

# 실행되는 process가 없으면 0을 반환, 있으면 1을 반환

socSrvProc = subprocess.check_output("pgrep -lf SocServer* | wc -l", shell=True)

print 'socSrvProc : ', socSrvProc


if int(socSrvProc) == 0 :  # SocServer.py가 기존 돌아가고 있는 것이 없으면

    # PC의 C# .DLL library와 소켓 통신을 위한 별도의 프로그램.

    # SocServer.py가 서버 역할 C# .DLL library가 client 역할

    os.system("python /home/joe/PyCharmProj/SocServer.py &")


    print 'SocServer.py를 실행했습니다. 서버 소켓 준비됨'

else :

    print 'SocServer는 이미 실행 중입니다.'



python에서 리눅스 쉘 명령어(shell command)를 이용하여 자기 IP 알아내기


소켓 통신 등의 경우 특히 서버 역할을 하는 경우 자기 IP를 스스로 알아내는 코드기 필요하다.

검색해 보면 파이썬에서 이를 알아내는 코드가 소개되어 있는데 다음과 같이


socket.gethostbyname(socket.gethostname())

socket.gethostbyaddr(socket.gethostname())

socket.gethostbyname_ex(socket.gethostname())


127.0.0.1인 loop back IP 값만 반환해 준다.

리눅스의 경우는 파이썬에서 shell command를 적절히 사용하면 여러모로 간한하게 문제를 해결할수 있다.


우선 socket과 subprocess 모듈을 import해야 한다.


import socket

import subprocess


그리고 리눅스 shell command중에서 IP를 확인하는 명령어들이 몇가지 있으나 단지 IP 정보만이 아닌 여러가지 부가적인 정보를 같이 보여주기 때문에 사용하기 용이하지 않다.

대표적으로 ifconfig같은 경우 아래와 같은 정보를 보여준다.


enp0s3    Link encap:Ethernet  HWaddr 08:00:27:ff:a4:58  

          inet addr:192.168.0.150  Bcast:192.168.0.255  Mask:255.255.255.0

          inet6 addr: fe80::c33f:ad86:8225:3597/64 Scope:Link

          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1

          RX packets:62078 errors:0 dropped:0 overruns:0 frame:0

          TX packets:56852 errors:0 dropped:0 overruns:0 carrier:0

          collisions:0 txqueuelen:1000 

          RX bytes:17715562 (17.7 MB)  TX bytes:44145423 (44.1 MB)


lo        Link encap:Local Loopback  

          inet addr:127.0.0.1  Mask:255.0.0.0

          inet6 addr: ::1/128 Scope:Host

          UP LOOPBACK RUNNING  MTU:65536  Metric:1

          RX packets:108 errors:0 dropped:0 overruns:0 frame:0

          TX packets:108 errors:0 dropped:0 overruns:0 carrier:0

          collisions:0 txqueuelen:1000 

          RX bytes:14178 (14.1 KB)  TX bytes:14178 (14.1 KB)


우리가 필요로하는 것은 단지 자신의 IP만 알면 되는데 이 경우 사용할수 있는 명령어가 hostname이라는 shell command이다.


hostname -I를 실행하면 192.168.0.150과 같은 자신의 IP를 알아낼수 있다.

아래는 코드조각이다.


import socket

import subprocess


myIP = subprocess.check_output("hostname -I", shell=True)

print 'My IP : ', myIP



python Tkinter GUI 프로그램에서 lambda를 이용한 버튼 이벤트 처리


코드에서 막바로 lambda를 이용한 버튼 클릭 이벤트에 대해서 살펴보자.

아래는 코드이다.

프로그램 실행하면 다음과 같은 윈도우 창이 뜨게 된다.




#-*- coding:utf-8 -*-


# 파이썬 GUI용 툴킷 Tkinter를 import

from Tkinter import *


# Tkinter 윈도우 객체 생성

root = Tk()


# (가로 x 세로 + x위치좌표 + y위치좌표)

root.geometry("500x300+300+200")


def callback(number):

    print "Button-", number


print 'b1'

# btn1 버튼 클릭시 실행할 메소드는 callback()이고

# 이 메소드로 넘길 paramter 값은 111이다.

# 따라서 btn1 버튼 클릭시 아래 내용 출력된다.

# Button- 111

btn1 = Button(text="One", command=lambda: callback(111))

btn1.configure(font=("Consolas", 15), width=20)

btn1.pack(pady=7)


print 'b2'

btn2 = Button(text="Two", command=lambda: callback(222))

btn2.configure(font=("Consolas", 15), width=20)

btn2.pack(pady=7)


print 'b3'

btn3 = Button(text="Three", command=lambda: callback(333))

btn3.configure(font=("Consolas", 15), width=20)

btn3.pack(pady=7)


# Tkinter 윈도우 화면에 표시

root.mainloop()



python의 soocket 통신시 send()sendall()의 차이에 대해서


Linux 시스템이 구동되는 임베디드 장비의 python과 Windows PC의 C#간 Socket통신을 하면서 임베디드 장비에서 binary 파일(이미지 파일)을 읽어서 Ethernet을 통해 PC로 전송하는 프로그램을 개발하다 보니 묘하게도 전송된 파일의 사이즈가 원본과 약간의 차이가 나는 것을 발견하게 되었다.

몇 byte에서 몇 십 byte까지...


로그를 출력해보면 데이터를 전송하는 쪽(python)에서 뭔가 모르게 데이터를 다 전송하지 못하는것 같았다. 이게 말이 되는지 모르지만 아무튼 전송된 파일의 파일 사이즈가 약간 적게 전송되는 기현상이 계속되었다.


결국은 python socket의 send()와 sendall()이 차이가 있음을 보게된다. 황당함~

Stackoverflow에 다음과 같은 설명이 있다.


https://stackoverflow.com/questions/34252273/what-is-the-difference-between-socket-send-and-socket-sendall


socket.send is a low-level method and basically just the C/syscall method send(3) / send(2). It can send less bytes than you requested, but returns the number of bytes sent.


socket.sendall is a high-level Python-only method that sends the entire buffer you pass or throws an exception. It does that by calling socket.send until everything has been sent or an error occurs.


If you're using TCP with blocking sockets and don't want to be bothered by internals (this is the case for most simple network applications), use sendall.


send()

 -. low-level에서 작동되는 시스템콜 형태

 -. 요청한 데이터보다 더 적게 전송할수도 있다.

 -. 그러나 요청한 만큼 전송한 걸로 return해 준다.


sendall()

 -. high-level단의 메소드로 python 메소드이다

 -. 요청한 데이터의 모든 버퍼 내용을 모두 전송한다. 그렇게 되지않을시 Exception발생

 -. sendall()도 내부적으로는 send()를 이용하는데 단지 모두 전송할 때까지 send()를 호출한다.


황당하지만 이렇다고 한다.

테스트해보면 5Mb정도의 이미지 파일을 전송해 볼때 sendall()이 send()보다는 확실히 속도가 좀 떨어지는 것 같다.




파이썬(python)에서의 문자열 비교에 대한 간단한 예제 코드이다.


#-*- coding: utf-8 -*-


from operator import eq  # 문자열 비교를 위한 함수 eq 추가


chk = "joe7"


if chk == "joe":

    print "같다."

else:

    print "다르다."


위의 코드는 C style 언어들의 보편적인 비교 법이다. 

그런데 Java에서는 저런식으로 비교가 안된다는 약간이 당황스러움.

말이 나온김에 Java의 문자열 비교를 보자면

if (chk.equals("joe")) { ... }와 같이 해야 한다.


위 코드 실행 결과는 당연히 "다르다"가 출력될 것이다.

이번에는 문자열 비교 함수 eq()를 이용한 방식이다.


아래 코드에서 eq(chk, "joe")는 괄호 안의 2개의 매개인자가 같으면 true, 아니면 false가 된다.

결과는 당연히 "다름"이 출력된다.


if eq(chk, "joe"): 

    print "같음"

else:

    print "다름"


다음으로 문자열 비교에서 매우 유용하게 사용되는 특정 문자를 "포함하고 있는지"에 대한 파이썬에서의 용법이다.

find()라는 메소드를 활용하면 된다. find()메소의 syntax는 다음과 같다.


str.find(s, beg=0, end=len(str))


str이라는 문자열에서 문자열 s가 발견 되어지면 발견되어진 위치 값(index값)을 반환한다.

만일 발견되지 않으면 -1을 반환한다. 이때 문자열의 index는 0부터 시작한다.


beg : 문자열 str에서 s라는 문자열을 찾을 때 시작 Index 값이다. 지정하지 않으면 default로 0가 지정된다.


end : 문자열 str에서 s라는 문자열을 찾을 때 종료될 index 값이다. 지정하지 않으면 문자열 str의 length 값이 지정된다.


예를들어


str = "<EOF>"가 있다면 

index 0 : <

index 1 : E

index 2 : O

index 3 : F

index 4 : >

가 된다.


print str.find("EOF")를 하면 1이 반환된다.

print str.find("<EOF>")를 하면 0이 반환된다.

print str.find("efgh")를 하면 -1이 반환된다.

print str.find("OF")를 하면 2가 반환된다.

print str.find("F")를 하면 3이 반환된다.

print str.find("OF>")를 하면 2가 반환된다. OF>라는 전체 문자열이 str에서 발견되기 때문이다.

print str.find("OF)")를 하면 -1이 반환된다. OF까지는 문자가 포함되어 있지만 OF)라는 문자는 포함되어 있지 않기 때문이다. 


info = "Hello world<EOF>"

print info.find(str)를 하면 11이 반환된다.


print info.find(str, 5)를 하면 str을 찾는 위치를 5위치에서 찾지만 반환은 전체 위치에서의 index값을 반환하므로 11이 반환된다.


print info.find(str, 5, 7)를 하면 str을 info에서 찾을 때 시작 위치를 5에서 시작해서 7위치까지만 찾으므로 str이 발견되지 않는다. 따라서 -1이 반환된다.


아무튼 python에서 java의 contains()와 같은 기능을 find()를 이용해서 할수 있다는 얘기다.

java의 contains()와는 약간 개념이 다르지만 -1이 아니면 해당 문자열을 포함하고 있는 것이 되는 것이다. 예를 들어 data라는 변수에 소켓통신으로부터 데이터를 읽었을 경우 data "EOF"라는 문자열이 포함되어 있다면 소켓통신의 읽기 작업을 종료하는 코드를 작성할려면 다음과 같이 하면된다.


if data.find("EOF") != -1 :

    print "OK, Job completed"

    break;


과 같이하면 될 것이다.




파이썬에서 Linux의 shell command를 사용하는 방법이다.

이를 위해서는 파이썬의 os모듈과 sys모듈을 import해야 한다.

다음은 코드 조각이다.


import os

import sys


os.system('cat qqq.txt > /dev/ttyPS0')


위의 소스코드는 현재 디렉토리에 있는 qqq.txt라는 파일의 내용을 ttyPS0라는 장치(리눅스에서는 장치도 file로 취급)로 전송하는 기능을 수행한다.

여기서 ttyPS0는 현재 디바이스가 가지고 있는 Serial port이다. 만일 현재 디바이스가 임베디드 장비이고 이 장비의 USB UART에 USB 케이블로 PC와 연결되어 있다면 위의 코드를 실행하고 PC쪽에서 Serial 포트의 데이터를 읽는 프로그램을 만들면 임베디드 장비의 qqq.txt의 내용을 PC에서 수신할수 있게 될것이다.


혹은 반대로 하면


os.system('cat /dev/ttyPS0')


이상과 같이 하면 현재의 장비의 Serial port로 들어오는 데이터를 터미널 창에 표시해주는 리눅스의 shell command를 python에서 사용하는 방법이었다. 이 경우는 PC에서 Serial port로 전송하는 데이터를 임베디드 장비에서 수신할수 있는 쉘 명령어를 python에서 사용한 방식이 되겠다.


만일 shell command의 내용을 변수에 저장하고자 한다면 subprocess라는 것을 import해 주어야 한다.


import subprocess


aaa = subprocess.check_output(['cat', 'kkk.txt'])

print aaa


와 같이 하면 kkk.txt의 내용을 변수 aaa에 저장했다가 이것을 출력하는 코드가 되겠다.


혹은 


mylist = subprocess.check_output(['ls', '-l'])

print mylist


이렇게하면 현재 디렉토리에 있는 파일 목록을 변수 mylist에 담고 그것을 출력하는 기능을 수행하는 코드이다.


혹은 보다 간단한 방법은 shell 옵션을 True로 하면 터미널 창에서 입력하는 형태 그대로의 command를 사용할수 있다.


pid = subprocess.check_output('ps -elf | grep CRSViewer.py', shell=True)


이렇게 하면 CRSViewer.py의 실행 process id를 나타내 준다.



python lambda 함수의 간단 사용법

lambda 함수는 익명 함수로 알려져 있는데 기본적으로 사용 포맷은


lambda [인수1, 인수2, ...] : 인수를 이용한 표현식


의 형태이다. 즉 


lambda [parameter_list]: expression


은 아래와 동일하다. 여기서 parameter_list는 생략될수도 있다.


def <lambda>(parameter_list):

      temp = expression

      return temp


간단한 예제를 보면


>>> f = lambda x=1, y=2: x+y

>>> f()

3

>>> f(3)

5

>>> f(2)

4

>>> f(0)

2

>>> f(10)

12

>>> f(10, 20)

30

>>> f()

3


x=1, y=2는 lambda의 parameter이고 이것을 이용해서 처리할 expression은 x+y이다.

그리고 x=1, y=2는 x, y 매개인자에 아무런 값도 주어지지 않았을 때의 디폴트 값이다.

따라서 f()의 값은 3이 되는 것이다.

만일 매개인자가 하나만 주어지면(f(3)과 같이) 이때 3은 x에 대입이 되어 처리가 된다.

따라서 f(3)은 x=3, y=2가 대입되어서 5가 되고 f(2)는 4가 되는 것이고 f(10)은 12가 되는 것이다.





python Kivy의 kv language에 대한 개념 소개는 여기를 참조


이번에는 Builder 클래스 중 load_string() 메소드를 이용하는 방법에 대해 소개한다. load_file()은 어렵지 않은 개념이므로 생략


from kivy.app import App

from kivy.lang import Builder


kvImg = """

FloatLayout:

    Image:

        source:'ccc.bmp'

        size_hint: 0.7, 0.7  # None, None

        post_hint: {'center_x': 1, 'center_y': 1}

        canvas.before:

            PushMatrix

            Rotate:

                angle: 45

                origin: self.center

        canvas.after:

            PopMatrix

"""


class RotationApp(App):

    def build(self):

        print 'kvImg : ', kvImg

        # return Builder.load_string(kv)

        return Builder.load_string(kvImg)


RotationApp().run()


이렇게 하면 kv language를 이용해서 45도 회전한 이미지를 화면에 보여줄수 있다.



Kivy를 이용해서 python에서 GUI 프로그래밍을 할 때 Kivy만이 가진 특성 중 하나인 Kv language를 사용하는 방법에 대한 간단한 개념이다.

kv code를 python 코드에 포함(load)시키는 방법에는 2가지 방법이 있다.


1. by name convention(이름으로 지정하는 방법)

python의 클래스 이름과 동일한 kv 파일명을 가진 kv 파일을 만들면 자동으로 이 파일을 load한다.


2. Builder 클래스를 이용하여 직접 python 코드에 load하는 방법

  -. Builder.load_file('some/path/somefile.kv')

  -. Builder.load_string(my_kv_string) 

     이 경우는 python 코드 안에서 string 형태로 kv 코드를 만들고 이를 Build.load_string()을 이용해서 사용하는 방석이다.

     (구체적 예제코드는 여기를 참조)


아래의 코드는 by name convention 방식으로 kv 코드를 python 코드에 load하는 방법이다.


  • exkvtest.kv의 내용이다

<Label>

    text: 'Hello'+' World'

    font_size: 50



  • exkvtest.py의 내용이다.

#-*- coding: utf-8 -*-

from kivy.app import App

from kivy.uix.label import Label


'''

kv의 원리

-. kv 파일명과 python 파일명이 같아야 함. 단 모두 소문자라야 함. ExKvTest.kv, ExKvTest.py식이면 안됨.

-. exkvtest.kv, exkvtest.py식이어야 한다

-. python 소스 코드의 클래스 이름도 kv 파일과 동일해야 한다. 단 소스 코드에서의 class 이름은 소문자로만 하지 

-. 않아도 된다. class ExKvTest(App)과 같이 해도 된다. 또한 소스 코드상에서의 class이름 끝에 App이라는 문자는 상관없다.


There are two ways to load Kv code into your application:

By name convention:

   Kivy looks for a Kv file with the same name as your App class in lowercase, minus “App” if it ends with ‘App’ e.g:

      MyApp -> my.kv

'''


# 안됨

# class MyKvTest(App):

#     def build(self):

#         # return Label(text='Hi~')

#         return Label()


# 잘 됨

class ExKvTestApp(App):

    def build(self):

        # return Label(text='Hi~')

        return Label()



if __name__ == '__main__':

    # MyKvTest().run()  # 안됨

    ExKvTestApp().run()  # 잘됨


+ Recent posts