1. 개요

 다대다 TCP/IP 채팅 프로그램이다. select문을 이용해 전송할 사람을 선택하는 방식이며, 여러 클라이언트가 접속해 있으면 함께 채팅이 가능해진다.


환경 : 리눅스

언어 : C


2. 서버

2-1. 개요

TCP/IP를 이용한 일대다 채팅 서버. 서버에서는 공지 역할을 하며 서버에서 메시지를 치면 모든 클라이언트에게 정보를 전송한다. 그리고 클라이언트가 메시지를 쳤다면 다른 클라이언트에게 다시 뿌려주는 역할을 해준다. 종료는 ^E(Ctrl+E)로 가능하다. 서버를 종료하면 다른 클라이언트도 함께 종료시킨다. 클라이언트에게서 /h, /i등의 명령어를 받으면 클라이언트의 정보를 해당 클라이언트에게만 전송해 준다.

 

2-2. 소스코드

#include "smart.h"

void SendStringToClient( int _socket,  const char *_ccp );
const char *itoa( unsigned int _uiData );


int main()
{
  MESSAGE m_message;
  
  fd_set status;
  
  int servSock;  // 서버 소켓번호는 정수형으로 이루어져 있다.
  int clntSock[MAXUSER];  // 클라이언트 소켓
  int tempSock;

  int iMaxSock;  // 
  
  unsigned int uiUser;

  int iCount;
  int i;
  
  struct sockaddr_in echoServAddr;  //  서버 소켓 구조체(신버전)
  struct sockaddr_in echoClntAddr;  //  클라이언트 주소...
  
  unsigned short echoServPort;  // 서버 포트.... 9999...
  unsigned int clntLen;  // 

  time_t stTempTime;
  
  int iRet;  // 잡동사니를 넣어두는 변수.

  unsigned char ucBuffer[500];  // 클라이이언트로부터 메시지를 저장하는 버퍼.
  

  echoServPort = 9999;  // 사용할 포트 번호.

  servSock = socket( PF_INET, SOCK_STREAM, IPPROTO_TCP );  // Socket을 생성한다.
  // Protocol Family

  if( servSock < 0 )  // 위의 구문에서 에러가 발생했다면
  {
    printf( "Socket Function Error!\n" );
    return0 );
  }

  memset( &echoServAddr, 0sizeof( echoServAddr ) );  //해당 메모리를 전부 0으로 초기화.
  echoServAddr.sin_family = PF_INET;  // Internet Address Family
  echoServAddr.sin_addr.s_addr = htonl( INADDR_ANY );  // INADDR_ANY는 자동으로 주소를 입력시켜 준다. 직접 입력해도 되나, 메모리에 저장되는 방식에 유의해야 한다.(Little->Big(표준))
  // 서버 주소는 반드시 고정되어야 하지만 클라이언트에서는 클라이언트의 주소를 자동할당하는 것이 좋다. 컴퓨터가 바뀌어도 자동 할당됨.
  echoServAddr.sin_port = htons( echoServPort );  // 포트 번호도 Big-Endian으로 바꿔주어야 한다. short형이므로 htons
  
  iRet = bind( servSock, ( struct sockaddr * )&echoServAddr, sizeof( echoServAddr ) );
  // 열려 있는 소켓에 포트 번호, IP등을 부여한다. 바인딩
  // bind함수에 쓰이는 인자는 구형 구조체이므로 캐스팅한다.
  // 블로킹 함수가 아니다.
  
  if( iRet < 0 )  // 위의 구문에서 에러가 발생했다면
  {
    close( servSock );  // 소켓을 연 이후 에러가 났으므로 닫아주도록 한다.
    printf( "Bind Failed Error!\n" );
    return0 );
  }

  iRet = listen( servSock, MAXPENDING ); // MAX PENDING : 5

  if( iRet < 0 )  // 위의 구문에서 에러가 발생했다면...
  {  
    close( servSock );  // 소켓을 연 이후 에러가 났으므로 닫아주도록 한다.
    printf( "Listen  Failed Error!\n" );
    return0 );
  }


  clntLen = sizeof( echoClntAddr );
    
  iMaxSock = servSock+1;  //셀렉트 문의 첫번째 인자는 무조건 제일 큰 소켓.
  
  uiUser = 0;
  
  while1 )
  {
    FD_ZERO( &status );  // 변수 초기화(0으로)
    FD_SET( 0&status );  // 0번째 값을 1로 바꾼다(stdin) -> 키보드 감지.
    FD_SET( servSock, &status );  // 랑데부 소켓과 키보드만 감지한다.

    for( i=0; i<uiUser; i++ )
    {
      FD_SET( clntSock[i], &status );
      
      if( iMaxSock <= clntSock[i] )
      {
        iMaxSock = clntSock[i]+1;
      }
      
    }
    
    iRet = select( iMaxSock, &status, 000 );
    if( iRet < 0 )
    {
      printf( "Select Function Error!\n" );
      
      strcpy( m_message.m_buffer, "서버오류로 인해 접속을 종료합니다." );
      
      for( iCount=0; iCount<uiUser; iCount++ )  // 접속한 클라이언트들에게 전부 전송한다.
      {
        write( clntSock[iCount], &m_message, sizeof( m_message ) );
      }
      
      strcpy( m_message.m_buffer, "/q" );
      
      for( iCount=0; iCount<uiUser; iCount++ )  // 접속한 클라이언트들에게 전부 전송한다.
      {
        write( clntSock[iCount], &m_message, sizeof( m_message ) );
      }
      
      break;
    }
    
    if1 == FD_ISSET( servSock, &status ) )  // 랑데부. 새로운 클라이언트가 접속했을 때.
    {
      tempSock = accept( servSock, ( struct sockaddr * )&echoClntAddr, &clntLen );
      // 들어오는 사람의 IP 및 정보가 요구되므로 2번째 인자가 클라이언트 정보.
      // accept는 구 자료형 형식의 함수이기 때문에 강제 캐스팅
      // clntLen : 크기.
      // accept에서는 접속이 될 때 까지 그대로 대기상태가 된다. 블로킹 함수.
      // servSock : 랑데부 소켓(외부에서 받아들이기만 한다)
      // clntSock : 커뮤니케이션 소켓. 내용을 처리할 때 쓰인다.
  
      printf( "Socket Number : %d\n", tempSock );
      
      if( tempSock < 0 )
      {
        printf( "Accept Function Error!\n" );
        continue;
      }
    
      printf( "클라이언트 접속 IP : %s\n", inet_ntoa( echoClntAddr.sin_addr ) );
      // Network to ASCII Code(수치로만 되어있는 IP주소를 사용자가 알아볼 수 있게 문자열로 바꾸어준다.)
  
      printf( "클라이언트 접속 PORT : %d\n", ntohs( echoClntAddr.sin_port ) );
      // Big-Endian으로 저장된 클라이언트의 포트 정보를 서버에 맞게 Little-Endian으로 변환하여 출력한다.

      if( MAXUSER <= uiUser )  // 사람이 다 찼는데 또 들어올 경우.
      {
        close( tempSock );
        continue;  // 와일 문 정상화.
      }

      clntSock[uiUser] = tempSock;  // tempSock으로 받은 커뮤니케이션 소켓을 넣어준다.
      uiUser = uiUser + 1;  // 접속 인원 + 1

      printf( "현재 접속자 수는 총 %d명 입니다.\n", uiUser );
      
    }
    
    else if1 == FD_ISSET( 0&status ) )
    {
      iRet = read( 0, m_message.m_buffer, sizeof( m_message.m_buffer ) );
      m_message.m_buffer[iRet-1= 0;
      strcpy( m_message.m_userName, "Server" );

      time( &stTempTime );
      strftime( m_message.m_time, 26"%Y-%m-%d %H:%M:%S", localtime( &stTempTime ) );
      
      if( m_message.m_buffer[0== CLTEND )
      {
        strcpy( m_message.m_buffer, ENDMSG );
        
        for( iCount=0; iCount<uiUser; iCount++ )  // 접속한 클라이언트들에게 전부 전송한다.
        {
          write( clntSock[iCount], &m_message, sizeof( m_message ) );
        }
        break;
      }
      
      for( iCount=0; iCount<uiUser; iCount++ )  // 접속한 클라이언트들에게 전부 전송한다.
      {
        write( clntSock[iCount], &m_message, sizeof( m_message ) );
      }
      
    }

    else
    {
      for( iCount=0; iCount<uiUser; iCount++ )
      {
        if1 == FD_ISSET( clntSock[iCount], &status ) )  // 커뮤니케이션.
        {
          iRet = read( clntSock[iCount], &m_message, sizeof( ucBuffer )-1 );
          // 커뮤니케이션 소켓으로부터 값을 읽어온다.
          // read함수는 글자 수를 반환하므로 iRet에 글자수를 임시로 저장한다.
      
          //ucBuffer[ iRet ] = 0;  // 마지막 글자는 \n(개행문자)이므로 NULL로 처리한다.


          //m_message.m_userNumber = iCount+1;

          if( iRet != 0 )
          {
            if0 == strcmp( LOGOUT, m_message.m_buffer ) )
            {
              printf( "2\n" );
              
              m_message.m_buffer[0= CLTEND;
              write( clntSock[iCount], &m_message, sizeof( m_message ) );  // 버퍼에 있는 내용을 클라이언트에게 전송한다..
              
              clntSock[iCount] = clntSock[uiUser-1];
              close( clntSock[iCount] );
              
              strcpy( m_message.m_buffer, m_message.m_userName );
              printf( "%s\n", m_message.m_buffer );
              strcat( m_message.m_buffer, ENDMSG );
              printf( "%s\n", m_message.m_buffer );
              
              for( i=0; i<uiUser; i++ )  // 접속한 클라이언트들에게 전부 전송한다.
              {
                write( clntSock[i], &m_message, sizeof( m_message ) );  // 버퍼에 있는 내용을 클라이언트에게 전송한다..
              }
              uiUser = uiUser - 1;

              printf( "{" OUTMSG "}\n" );
              continue;
            }
            else if'/' == m_message.m_buffer[0] )  // 첫 글자가 '/'이면 매크로 명령어를 실행한다.
            {
              switch( m_message.m_buffer[1] )
              {  
                case 'h':
                  strcpy( m_message.m_userName, "Server" );
                  strcpy( m_message.m_buffer, "도움말 메뉴를 엽니다...\n/p : 포트 정보\n/i : IP 정보\n");
                  write( clntSock[iCount], &m_message, sizeof( m_message ) );
                  break;
              
                case 'p':
                  strcpy( m_message.m_userName, "Server" );
                  strcpy( m_message.m_buffer, itoa( ntohs(echoClntAddr.sin_port ) ) );
                  write( clntSock[iCount], &m_message, sizeof( m_message ) );
                  break;
                  
                case 'i':
                  strcpy( m_message.m_userName, "Server" );
                  strcpy( m_message.m_buffer, inet_ntoa( echoClntAddr.sin_addr ) );
                  write( clntSock[iCount], &m_message, sizeof( m_message ) );
                  break;
                
                default:
                  strcpy( m_message.m_userName, "Server" );
                  strcpy( m_message.m_buffer, "등록되지 않은 명령어입니다!");
                  write( clntSock[iCount], &m_message, sizeof( m_message ) );
                  break;
              }
            }
            else
            {
              printf( "[(%s)%s 님의 말 : ", m_message.m_userName, inet_ntoa( echoClntAddr.sin_addr ) );
              fflush( stdout );
              printf( "%s", m_message.m_buffer );  // 버퍼에 있는 내용을 stdout(화면)에 출력한다.
              printf( "(%s)]\n", m_message.m_time );
              //SendStringToClient( clntSock, inet_ntoa( echoClntAddr.sin_addr) );
              //SendStringToClient( clntSock, "님의 말 : " );
              
              for( i=0; i<uiUser; i++ )  // 접속한 클라이언트들에게 전부 전송한다.
              {
                write( clntSock[i], &m_message, sizeof( m_message ) );  // 버퍼에 있는 내용을 클라이언트에게 전송한다..
              }

            }
          }
        }
      }
  
    }
  }// End of while(1)

  printf( "서버를 종료합니다...\n" );
  
  close( servSock );  // 서버 소켓을 종료한다.

  for( i=0; i<uiUser; i++ )  // 접속한 클라이언트들을 전부 닫는다.
  {
    close( clntSock[i] );  // 클라이언트 소켓을 종료한다.
  }
  
  return0 );  // 빠빠이
}

void SendStringToClient( int _socket,  const char *_ccp )
{
  write( _socket, _ccp, strlen( _ccp ) );
  return;
}

const char *itoa( unsigned int _uiData )
{
  char cBuffer[5= "0000";
  int i;
  int j;
  
  for( i=10000, j=0; i>=0; i/=10, j++ )
  {
    cBuffer[j] = _uiData/i;
    _uiData = i * (_uiData/i);
  }
  return cBuffer;
}


2-3. 실행 사진


3. 클라이언트

3-1. 개요

TCP/IP를 이용한 일대다 채팅 클라이언트이다. 정해진 구조체를 서버에 전송시키며, 첫 접속 시 유저의 이름을 입력하여 채팅 시 구분을 할 수 있도록 하였고 메시지를 입력했을 때 strftime함수를 이용하여 시간을 문자열로 저장해 전송하여 언제 메시지를 입력하였는지 알 수 있게 하였다. 또한 특정 문자를 치면 서버에 특수명령어를 보내는 것이 가능하다.


 

3-2. 소스코드

#include "smart.h"
#include "function.h"

int main(int argc, char *argv[])
{
  fd_set status;
  MESSAGE m_message;
  char m_userName[20];
  
  int sock;
  struct sockaddr_in echoServAddr;  //서버 구조체
  unsigned short echoServPort;  // 서버 포트
  char *servIP;  //서버 주소
  char *echoString;
  char buffer[500];  // 버퍼

  time_t stTempTime;
  int iRet;

  servIP = argv[1];  // 서버 주소
  
  sock = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );  // 서버에 연결할 소켓 생성 및 열기
  if( sock < 0 )  // 실패했을 시...
  {
    printf( "Socket Function Failed!\n" );
    return 0;
  }

  memset( &echoServAddr, 0sizeof( echoServAddr ) );  // 구조체의 메모리 클리어
  echoServAddr.sin_family = AF_INET;  // 서버와 통신할 규약 설정(AF_INET/TCP)
  echoServAddr.sin_addr.s_addr = inet_addr( servIP );  // 서버 주소 설정
  echoServAddr.sin_port = htons( 9999 );  // 서버 포트 설정

  iRet = connect( sock, ( struct sockaddr* )&echoServAddr, sizeof( echoServAddr ) );
  // 클라이언트가 서버에 접속을 요청한다.
  
  if( iRet < 0 )  // 접속 요청 실패
  {
    printf( "Connect Function Failed!\n" );
    close( sock );  // 소켓이 열려 있으므로 닫아준다.
    return 0;
  }

  printf( "접속하였습니다. 이름을 입력해 주시기 바랍니다. : " );
  fflush( stdout );
  iRet = read( 0, m_userName, sizeof( m_userName ) );
  m_userName[iRet-1= 0;

  while1 )  // 무한루쁘
  {
    FD_ZERO( &status );
    FD_SET( 0&status );
    FD_SET( sock, &status );
    iRet = select( sock+1&status, 000 );

    if( iRet < 0 )  // 셀렉트 문 오류 처리
    {
      printf( "Select Function Error!\n" );
      write( sock, LOGOUT, sizeof(LOGOUT) );
      break;
    }
    // select의 인자 : 감시하고자 하는 요소, 읽기, 쓰기, 에러, 블로킹(얼마나 정지시킬지/0이면 무한대기->입력이 없으면 밑으로 안내린다.) )
    
    if1 == FD_ISSET( 0&status ) )
    {
      iRet = read( 0, m_message.m_buffer, sizeof( m_message.m_buffer ) );  // stdin으로부터 받는 글자를 버퍼에 저장한다.

      time( &stTempTime );
      strftime( m_message.m_time, 26"%Y-%m-%d %H:%M:%S", localtime( &stTempTime ) );
      
      m_message.m_buffer[iRet-1= 0;
      
      strcpy( m_message.m_userName, m_userName );

      if( CLTEND == m_message.m_buffer[0] )  // 프로그램 종료 루틴
      {
        printf( "클라이언트를 종료합니다...\n" );
        strcpy( m_message.m_buffer, LOGOUT );
        write( sock, &m_message, iRet );
      }
      
      write( sock, &m_message, sizeof( m_message ) );  // 소켓을 통해 서버에 해당 메시지를 전송한다.  

    }
    else if1 == FD_ISSET( sock, &status ) )
    {
      read( sock, &m_message, sizeof( m_message ) );
      
      if0 == strcmp( ENDMSG, m_message.m_buffer ) )  // 프로그램 종료 루틴
      {
        printf( "서버가 종료되었으므로 프로그램을 끝냅니다.\n" );
        break;
      }

      printf( "[ %s : ", m_message.m_userName );
      fflush( stdout );  // 개행 문자가 없으므로 출력되지 않는다.
      
      printf( "%s", m_message.m_buffer );

      printf( "(%s)]\n", m_message.m_time );  // 이 때 출력된다.
    }
  }

  close( sock );  //소켓을 닫아준다.
  return 0;

}
/*
void MyMemorySet( void *vp, unsigned char ucPad, unsigned int uiSize )
{
  while( uiSize != 0 )
  {
    *( ( unsigned char * )vp + uiSize ) = ucPad;
    --uiSize;
    vp = ( unsigned char * )vp + 1;  // Void Pointer는 값은 자유로 가르킬 수 있지만, 연산이 불가능하므로 Casting.
  }
  return;
}

void MyBZero( void *vp, unsigned int uiSize )
{
  MyMemorySet( vp, 0x00, uiSize );
  return;
}*/


3-3. 실행 사진

Win32 API세븐 세그먼트(7-Segment) 타이머


개요

 Win32API와 GDI+를 이용한 세븐 세그먼트 타이머. 실제 세븐 세그먼트를 흉내낸 것으로, 한 칸의 세그먼트는 GDI+의 다각형 기능을 이용한 것이다. 에디트 박스에 숫자를 초 단위로 입력하고 START!버튼을 클릭하면 타이머가 시작된다. 컵라면에 응용하기 위하여 에디트 박스의 기본 값을 180(3분)으로 하였다.

 초의 변화를 감지하기 때문에 아주 정밀하지는 않으며, 불안정한 시작을 막기 위해 첫 시작시 최대 약 1초 동안 대기할 수 있다는 단점이 있다. 타이머가 완료되면 비프 음과 함께 메시지 박스를 출력하도록 하였다.

 잦은 갱신으로 인한 반짝거림을 막기 위해 더블 버퍼링을 사용하였다. 단, 우측의 '타이머' 문자열과 에디트박스, 버튼은 깜빡거릴 수 있다.


실행 화면

<첫 시작 화면>

(가운데 ':'는 1초마다 켜지고 꺼지고 하도록 되어 있다.)


<타이머 설정 화면>


<타이머 완료시 알림>


실행 파일


SevenSegment.exe

 메인 화면

검색 화면

 조종 화면

세팅 화면

 

이전 버전 테스트 영상(ATmega2560)


이 영상은 내 폰으로 테스트한 것. UI를 바꾸기 전임



최근(130822) 버전 테스트 영상(ATmega2560)


다른 분 휴대폰으로 테스트하는걸 찍음. 가로에서 조종화면이 뜨도록 변경함

영상이 쓸데없이 고화질 ^^;;;



발표용 PPT(내용은 이하에 쓰여진 것과 거의 같음)

드라군 통신 파트.ppsx


제작 소요 기간 : 2013 7월 22일 월요일 ~ 쉬는시간 틈마다 제작(약 일주일+알파)

제작 인원 : 나

개발 환경 : Android(2.3.4 진저브레드 / 4.0.4 아이스크림 샌드위치), Windows XP/7 + ADT Bundle

소스 목록(src 9개, layout 6개)


개요

1. FB155BC 모듈이 HID프로파일을 지원하는 버전이 아니었으므로 SPP(Serial Port Profile)통신을 이용하였음

-> HID프로파일이 없다고 휴대폰 블루투스와 통신이 안되는건 아님

- HID : Human Interface Device의 줄임말이며, 키보드나 마우스 등의 주변장치를 의미함, 즉 블루투스 무선 키보드 등을 만들 때 활용되는 것

2. 블루투스를 처음 다루어 보았으므로 헤맨 점이 많았음(UUID라던지 UUID 등)

3. (미완)전송하는 값은 유동적으로 변경될 수 있으므로 Setting에서 전송 값을 바꿀 수 있도록 하는 것이 목표

4, (추가)센서 값을 ADC로 읽어온 후, 모듈을 이용해 기기로 값을 전송한 후 어플에서 확인할 수 있도록 함


앱 흐름

1. 장치가 블루투스를 지원하는가?

-> O : 다음으로 진행

-> X : Toast를 띄우며 프로그램을 종료

2. 장치의 블루투스가 Disable상태인가?

-> O : Enable해준다

-> X : 바로 진행

3. 기기를 검색하여 거리 안에 있는 모든 BluetoothDevice들을 ListView로 띄워준다.

4. 아이템 클릭시 AlertDialog로 접속할지 물어본다.

-> O : BluetoothDevice를 갖는 ArrayList에서 클릭한 값(OnItemClickListener의 인자 postion이용)을 가져온 후,

SPP UUID({00001101-0000-1000-8000-00805F9B34FB})를 이용해 BluetoothSocket을 만들고 접속을 시도(connect())

-> 접속 시도시 오류가 발생할 수 있는데, 주로 검색은 하였으나 통신하기에 거리상 제약이 있을 경우, UUID가 잘못된 경우의 두가지일 확률이 높다!

-> X : 아무 일도 일어나지 않고 창을 없앤다

5. OutputStream을 만들어 블루투스 메시지를 보낼 수 있게 설정한다.

6. 사용자가 편하게 조종할 수 있도록 UI를 화면상에 보여준다.(Portrait)

7. (추가)블루투스 수신 스레드를 생성하고, 값이 들어오는지 InputStreamread()를 통해 읽어오고, Handler를 생성해 TextView의 값을 변경할 수 있도록 한다.

- InputStream의 read()함수는 블로킹 함수이므로 값이 들어오지 않으면 무한히 기다릴 것이다. 이는 작동을 방해할 수 있으므로 Thread로 빼두어 서브스레드에서 작동하게 한다.

- 스레드 내부에서 TextView등 UI의 값을 변경하는 것은 불가능하다. 그러므로 Handler를 만들어 Handler가 값을 바꿀 수 있도록 한다.

-> UI자체도 스레드에 관해 관리되며, 외부 스레드에서 접근이 불가능 하다는 모양이다

8. 버튼이 클릭되었거나(기능버튼) 터치되어있는가?(방향버튼)

-> O : OutputStreamwrite()함수를 이용하여 해당 버튼에 대응되는 값을 전송한다. 전송은 문자열도 가능하나, 기본적으로 unsigned char에 대응되게 하였음.

-> X : 입력을 기다림

- 굳이 방향버튼을 터치로 받게 한 이유 : 계속 앞으로 가고싶을 때, 연타해야하므로 불편할 수 있어 누르고 있으면 값이 계속 전송되도록 하였음.....

-> 단, 드라군 모드에서는 연속 입력이 되면 곤란하여 모드를 구분해놓고 터치리스너/클릭리스너를 번갈아 사용하도록 바꾸었음

- 일정 시간 이상 대기시 새로운 값을 전송


AVR ATmega2560 프로그램 흐름

1. USART1을 사용할 수 있게 설정

-> 분주비 9600, 데이터비트 8비트, 정지비트 1, 패리티 없음

-> 수신 인터럽트를 받게 설정

2. 블루투스 모듈(FB155BC)에 AT명령어 중 하나인 "AT+BTSCAN\r\n"을 입력해 스캔 가능하게 설정한다.

-> 이게 되지 않으면 휴대폰에서 검색 자체가 불가능하다.

3. 수신시 vector36함수(USART1용)에서 미리 정해놓은 버튼 값에 따라 switch문으로 처리하도록 설정한다.

4. 송신을 원하는 값을 SendString등의 함수로 제작해 전송한다. 수신과 거의 원리가 비슷하므로 생략

'완성 프로젝트' 카테고리의 다른 글

[TCP/IP] 다대다 채팅  (8) 2013.10.01
[Win32 API] 세븐 세그먼트 타이머  (3) 2013.08.28
[Python] 인터넷 검색 프로그램  (3) 2013.06.15
[Processing] 똥피하기  (4) 2013.02.25
HTML5+자바스크립트 습작  (0) 2013.02.25

작년 6월?쯤... 꽤 전에, 인터넷 창 열고 클릭하기 귀찮아서 만들었던 것으로 기억됨.

파이썬/PyGTK로 제작하였으므로 두 개가 설치되어 있지 않으면 실행이 안 된다.


이용자의 레지스터를 참조해서 기본 브라우저를 띄우고 해당 사이트 주소+검색어를 자동으로 쳐주는 것.

참 잉여였던 것 같다.



searchgui.py

#!/usr/bin/env python

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



import os

import _winreg

import getpass


import pygtk

pygtk.require("2.0")

import gtk


isChooseBrowser = False

spaceChar = "+"

searchKeyword = ""


siteDic = { "기본 검색":"", "엔하 위키 미러" : "http://mirror.enha.kr/wiki/", "위키피디아" : "ko.wikipedia.org/w/index.php?search=", "구글" : "https://www.google.co.kr/search?q=", "네이버" : "http://search.naver.com/search.naver?where=nexearch&query=" }


class Search:

    

    def keypress( self, widget, event, entry ):

        if(  int( gtk.keysyms.F5 ) >= int( event.keyval ) >= int( gtk.keysyms.F1 ) ):

            if( event.keyval == gtk.keysyms.F1 ):

                shortcut = "기본 검색"

            if( event.keyval == gtk.keysyms.F2 ):

                shortcut = "엔하 위키 미러"

            if( event.keyval == gtk.keysyms.F3 ):

                shortcut = "위키피디아"

            if( event.keyval == gtk.keysyms.F4 ):

                shortcut = "구글"

            if( event.keyval == gtk.keysyms.F5 ):

                shortcut = "네이버"

            self.startsearch( self, shortcut, entry )


    def delete_event(self, widget, event, data =None ):

        print("test")

        return False

    def destroy(self, widget, data=None ):

        gtk.main_quit()

        

    def enter_callback(self, widget, entry):

        print( entry.get_text())


    def startsearch( self, widget, event, entry  ):

        

        if( event == "기본 검색" ):

            spaceChar = " "

            searchSite = siteDic["기본 검색"]

        if( event == "엔하 위키 미러" ):

            spaceChar = "%20"

            searchSite = siteDic["엔하 위키 미러"]

        if( event == "위키피디아" ):

            spaceChar = "+"

            searchSite = siteDic["위키피디아"]

        if( event == "구글" ):

            spaceChar = "+"

            searchSite = siteDic["구글"]

        if( event == "네이버" ):

            spaceChar = "+"

            searchSite = siteDic["네이버"]

            

        searchKeyword = entry.get_text()

        searchSite += searchKeyword.replace(" ",spaceChar)


        basicBrowser = _winreg.QueryValue( _winreg.HKEY_CLASSES_ROOT, "http\\shell\\open\\command" )

        basicBrowser = basicBrowser[ 0 : basicBrowser.find( " -" ) ]

        os.system( "\"" + basicBrowser + " \"" + searchSite.encode("euc-kr") + "\"" +"\"" )


        print(( "검색 사이트 :\t" + event ).encode( "euc-kr" ))

        print(( "공백 문자 :\t" + spaceChar).encode( "euc-kr" ))

        print( "검색어 :\t" + searchKeyword )

        print("\"" + basicBrowser + " \"" + searchSite.encode("euc-kr") + "\"" +"\"")

    

    def __init__(self):

        

        self.window = gtk.Window( gtk.WINDOW_TOPLEVEL )

        self.window.set_size_request(500,40)

        self.window.set_resizable( False )

        self.window.move( 0,0 )

        self.window.connect("delete_event", self.delete_event)

        self.window.connect("destroy", self.destroy)

        self.window.set_border_width(0)

        

        vBox = gtk.VBox( False, 0 )

        hEntryBox = gtk.HBox( False, 0 )

        hBBox = gtk.HButtonBox( )


        self.window.add(vBox)

        vBox.show()

        

        ##self.window.add(hEntryBox)

        hEntryBox.show()


        ##self.window.add(hBBox)

        hBBox.show()

        

        entry = gtk.Entry()

        ##entry.connect("activate", self.enter_callback, entry)


        entry.set_editable( True )

        entry.select_region(0, len(entry.get_text()))

        entry.show()

        


        btn1 = gtk.Button("기본 검색(F1)")

        btn1.connect( "clicked", self.startsearch, "기본 검색", entry)

        btn1.set_size_request(95,20)

        ##self.window.add(btn1)


        btn2 = gtk.Button("엔하 위키(F2)")

        btn2.connect( "clicked", self.startsearch, "엔하 위키 미러", entry)

        btn2.set_size_request(95,20)

        ##self.window.add(btn2)


        btn3 = gtk.Button("위키피디아(F3)")

        btn3.connect( "clicked", self.startsearch, "위키피디아", entry)

        btn3.set_size_request(95,20)

        ##self.window.add(btn3)


        btn4 = gtk.Button("구글(F4)")

        btn4.connect( "clicked", self.startsearch, "구글", entry)

        btn4.set_size_request(95,20)

        ##self.window.add(btn4)


        btn5 = gtk.Button("네이버(F5)")

        btn5.connect( "clicked", self.startsearch, "네이버", entry)

        btn5.set_size_request(95,20)

        ##self.window.add(btn5)


        hBBox.add( btn1 )

        hBBox.add( btn2 )

        hBBox.add( btn3 )

        hBBox.add( btn4 )

        hBBox.add( btn5 )

            

        btn1.show()

        btn2.show()

        btn3.show()

        btn4.show()

        btn5.show()


        hEntryBox.pack_start(entry, True, True, 0)

        ##hEntryBox.pack_end(entry, True, True, 0)


        vBox.pack_start( hEntryBox, True, True, 0)

        vBox.pack_end( hBBox, True, True, 0)


        self.window.connect("key-press-event", self.keypress, entry)

        self.window.show()

            

    def main(self):

        gtk.main()


if __name__ == "__main__" :

    hWnd = Search()

    hWnd.main()



application.windows.zip


자바+프로세싱으로 만든 똥피하기. 작년 4월?에 만든 거라 많이 오래됨.

게임 자체는 허접하지만ㅋㅋ;; 그나마 이걸 기점으로 내가 프로그래밍에 그나마 없던 열정을 조금이라도 퍼붓게 된 것 같다.

source폴더에 소스 하나 들어가있어요. 띄우는 데에 엄청나게 허벌나게 오래 걸립니다.

자바에 문제가 있어서 실행을 학원와서야 시켜볼 수 있어따...



게임 화면입니다.

그래픽은 직접 그림판과 아이콘 편집 프로그램으로 저렴하게 그렸습니다.

물방울/똥/모래시계가 확률에 따라 후다다다 떨어집니다.

똥에 맞으면 청결도가 25 떨어지고 물방울에 맞으면 청결도가 25 올라갑니다.

청결도가 0이 되면 죽고 점수는 초단위로 계산합니다.



모래시계에 맞으면 이렇게 배경과 오브젝트들이 5초간 어둡게 변하며 천천히 떨어집니다. 매트릭스!!!(5초 맞나?ㅋㅋㅋㅋ;;;)

그리고 서서히 빨라지고 서서히 밝아집니다.



죽으면 요래요래 점수나오고 끝.

사람이 할게 없어지니까 HTML5를 깨작거리게 되더군요



아래 나오는 문구와 배경 이미지는 항목에 마우스를 갖다대거나 클릭하면 바뀌게 만들었습니다.

이미지는 각각 초원 바탕화면, 구글링해서 얻은 새싹 이미지, 고흐의 삽질하는 사람들인가 남자들인가 뭐시기, 키아누 리브스의 슬픈짤

ㅠㅠ

11월 26일에 반 정도 만들고 발표한 건데 아직도 완벽하게 완성안함

...


YoutubeEmbedCopier.exe



(원하는 크기가 없을 경우 width*height 식으로 숫자를 입력하여도 됩니다.)


유투브가 Embed로 된 소스를 지원하지 않게 되었는데 블로그 등지에서는 Embed만 써지므로 만들게 되었습니다.

C#이 어떤건지 연습하는 겸사 만든건데 다소 오래되어 소스도 어디있는지 찾을 수 없...


사용 예시↓


+ Recent posts