WPF2009/01/28 09:32

안녕하세요. 멤버십에서 새벽까지 당직을 스고 집으로 돌아와 고양이랑 조금 놀다가 잠좀 잘까 했는데 고양이녀석 아프다고 낑낑대서 아침에 병원을 대려가기 위해 잠을 안자고 버티고 있는 1人입니다.. ㅠ_ㅠ

이번시간에는 예전부터 포스팅하려다가 미루고 미루고 미루다가 이제야 포스팅하게된.. -_-;; WPF 3D를 이용한 영화 검색엔진.... (뭔가 거창하네요)이라고 하기엔 너무나 허접한 내용을 소개하고자 합니다. 간단히 Naver Open API를 사용하여 입력한 키워드에 맞는 영화 포스터 이미지를 가져와서 3D화면에 출력하는 내용입니다. 아래는 구현 완료 동영상입니다.


동영상 캡쳐좀 잘해보려고 무압축으로 설정했더니.. 대략 2분짜리 동영상인데 900메가 가까이 커져버리더라구요.. 하는수 없이 다시 인코딩해서 용량 줄인다음 다시올렸는데.. 열악한 제 컴퓨터가 무압축 방식을 견디지 못했는지 중간에  뚝~~ 하고 끊기는 장면이 한번 보이네요-_-;; 실제로 돌려보시면 검색결과 가져오는 부분 이외에는 끊기지는 않습니다..^^;; (이부분은 너무 졸려서 해결하지 않은 채로 그냥 둿어요.. ㅈㅅ)

저번시간에는 구글에서 이미지 주워와서 하더니 이번에는 네이버에서 뒤적거려서 구한 이미지 요리조리 편집해서 사용했습니다. 혹시나 있을 수 있는 문제를 대비해 일단은 원래 제작자분이름을 적어놨는데... (사실은 허락을 받고싶지만.. 외국분이라... ㅠㅠ)

이번에도 역시 소스코드가 상당히 길어서 파일로 첨부했습니다.. (절대 설명하기가 귀찮아서 그러는게 아니랍니다!!!! )

위 내용을 구현하기 위해서는 아래와 같은 준비물이 필요합니다.
1. Naver Open API Key : Naver Developer Center에 가시면 Open API Key를 발급하는 코너가 있습니다.
2. 적절한 UI 이미지 : 저처럼 인터넷에서 주워서 사용하셔두 되고..
3. 3D Panel Mesh : 간단하게 이미지만 출력해주면 되기때문에 아래와 같이 간단한 사각형 Mesh를 만들어 사용했습니다.

4. 머리가 아파오는 수학 : 초등학교 수준같은데 어렵네요..................................................... ㅠ

자 그럼 준비가 되셨나요? 그럼 아래 Class Diagram을 보면서 구조에 대해서 살펴보도록 하겠습니다. 이미지가 줄어들어서 잘안보이시는분은 클릭하시면 원본사지으로 확인 하실 수 있습니다.


이번 예제는 위 그림과 같이 총 4부분으로 나뉘어져있습니다. 가장먼저 오른쪽 중앙의 DVDCase를 보겠습니다. 간단하게 포스터 위에 반투명한 DVD Case모양 Image를 덮여씌워서 출력되는 객체입니다. 그다음은 Panel3D인데요, 좀전에 말한 DVDCase객체가 Panel 3D에 붙게됩니다.

Panel3D는 위에서 보여드릴 간단한 2차원 사격형 Mesh로 구성되어 있으며, 마우스 이벤트등을 활용하기 위해 UIElement3D객체를 상속받아 구현했습니다. 그리고 생성자로 DVDCase객체를(꼭 DVD Case가 아니더라도 상관은 없습니다.) 넣으면 DVDCase를 Visual로 사용하는 VisualBrush로 생성한뒤 DiffuseMaterial를 생성하여 Material로 사용합니다. 코드에서 보시면 아시겠지만, 그냥 Material과 BackMaterial에 모두 VisualBrush를 적용한 이유는, 객체가 원을 그리며 회전을 하게 되는데 객체가 뒤로 갔을경우에도 확인 가능하도록 설정한것입니다.

계속해서 PanelList입니다. PanelList는 ModelVisual3D을 상속받아 구현되었으며, base.Children 속성에 여러개의 Pannel이 추가 되게 됩니다. PanelList에서는 PanelList에서 새로 정의된 Children Property를 통해 UIElement를 입력받는데, Collection에 객체가 추가/제거 될때 마다 적절한 크기&위치를 지정하여 객체들이 원을 이루어 분포될 수 있도록 도와주는 역할을 합니다.

Panel 3D객체들의 회전에 대한 문제는, Panel3D객체가 base.Children속성에 추가되어있기때문에, 간단하게 PanelList에 RotationTransform을 적용하여 간단하게 해결할 수 있었습니다. MainWindow부분에서는 단순한 UI구성과 OpenAPI를 사용한 결과 수집후 Panel List의 Children을 변경하는 기능을담당합니다.

★다운받아 실행해봤는데 에러가 난다고 하시는 분은 Open API키 값을 지정 해주었는 확인 해보시기 바랍니다. 
   (제키값은 지우고 ############와같이 적어놨기때문에 본인의 OpenAPI로 작성하셔야합니다.)

자그럼 오늘은 여기까지로 하구요, 잠이 쓰나미처럼 몰려오고 있는터라 말이 너무 횡설수설한감이 없진않지만.. 질문이나 다른 건의사항이나 기타 등등은 이메일이나 리플로 남겨주세요~~ 그럼 내일 뵙겠습니다!!


아래는 전체 프로젝트 소스코드입니다.




 

크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by 곡스
WPF2008/12/12 02:17


멤버십(수원 삼성소프트웨어멤버십)에서 도서를 관리를 담당하는 일을 맡게 되었는데 기존의 도서 관리가 잘 이루어 지고 있지 않은거 같아 도서 관리 프로그램을 만들어 보았습니다.

WPF 와 C#을 사용해서 개발 했으며 DB는 SQL2003을 사용했습니다. 예전에 한번 올렸었던 도서 관리 프로그램과 기능적인 면에서는 비슷하며 추가된 기능으로는 도서 검색시 온라인서점에서 판매되고 있는 도서까지 함께 보여줘서 도서 신청까지 가능하다는 점이 있습니다.

DB에는 도서에 대한 도서 코드와, ISBN, 대출사용자, 대출날짜, 등록날짜 가 저장되며, 도서 검색시 Naver OpenAPI를 사용하여 관련된 책목록을 가져온뒤  DB에 저장된 도서가 있을 경우 대출가능한 도서로 판단하는 방식입니다.

WPF를 사용하면서 가장 아쉬웠던 점 중에 하나인 Text의 장평을 조절하는 부분을 직접 구현하여 보다 세련되게 Text를 표현하도록 구현 했습니다. 내부 서버에 연결하는 코드가 포함되어 있어 아직 소스코드는 올리지 않았습니다. 따로 요청하시거나 코드가 정리되는데로 업로드 하도록 하겠습니다.


크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by 곡스
TAG Book, c#, openapi, SSM, WPF, XAML
C#2008/09/22 21:00

안녕하세요. 이번시간에는 네이버 OPEN API를 사용하여 블로그에 사람들이 등록한 MP3 또는 WMA형식의 음악파일의 다운로드 주소를 추출하는 기능을 구현 해보겠습니다. 이번에 구현할 Sample Application은 아래와 같습니다. 간단하게 Keyword를 입력받고, Keyword에 해당하는 음악파일을 다운로드 할 수 있는 URL을 출력해 줍니다.


아래는 소스코드입니다.

    public static class Extractor
    {
        const String OPENAPIKEY = "87b0c706f01ebc494df5ea894a6c0aa5";


        public static List Search(string Keyword)
        {

            XmlDocument Doc = new XmlDocument();

            Doc.Load("http://openapi.naver.com/search?key=" 
                      + OPENAPIKEY 
                      + "&display=5&start=1&target=blog&sort=sim&query=" + Keyword);

            List MusicList = new List();
            XmlNodeList LinkList = Doc.GetElementsByTagName("link");

            String TempItem;
            foreach (XmlNode LinkItem in LinkList)
            {
                TempItem = ExtractMusic(LinkItem.InnerText);
                if (String.IsNullOrEmpty(TempItem) == false) 
                    MusicList.Add(TempItem);
            }

            return MusicList;
        }



        private static String ExtractMusic(String URL)
        {

            if (URL.StartsWith("http://blog.naver.com/") == false)
                return null;

            WebClient Downloader = new WebClient();

            String UserID = URL.Substring(22, URL.IndexOf("?") - 22);
            String PostNumber = URL.Substring(URL.LastIndexOf("=") + 1);
            String PritPageURL = "http://blog.naver.com/PostPrint.nhn?blogId="
                                + UserID 
                                + "&logNo=" + PostNumber;
            String Source = Downloader.DownloadString(PritPageURL);

            int Start, End;
            String TempItem;

            End = Source.IndexOf(".wma", StringComparison.CurrentCultureIgnoreCase) + 4;
            if (End < 4)
                End = Source.IndexOf(".mp3", StringComparison.CurrentCultureIgnoreCase) + 4;
            Start = Source.LastIndexOf("http://", End);

            if (End < 4) return null;

            TempItem = Source.Substring(Start, End - Start);
            return TempItem;

        }

    }
기본적인 작동원리는 아래와 같습니다.

  1. Search(String Keyword) 메서드를 이용하여 검색.
  2. OPEN API Key와 Blog검색 API를 사용하여 해당 Keyword를 가지고 있는 Blog URL검색
  3. XMLDocument Class를 사용하여 해당 Link만 추출
  4. ExtractMusic(String URL)를 사용하여 음원 추출
  5. Blog Print Page를 사용하여 게시물 Source코드만 추출
  6. WebClient.DownloadString 메서드를 사용하여 소스코드 다운로드
  7. URL추출
Blog Print Page를 사용하는 이유는 기본적으로 네이버 Open API를 사용하여 Blog URL을 추출하면 게시물과 메뉴등을포함한 URL을 반환 하기 때문에 바로 음원 추출이 불가능 합니다. 때문에 해당 게시물만 확인 할 수 있는 URL이 필요한데 여기서 Print Page를 사용하는 것입니다.

Console.WriteLine("==========================");
Console.WriteLine("  NAVER MUSIC EXTRACTOR");
Console.WriteLine("==========================");

String Keyword = "";
do
{
    Console.Write("Keyword : ");
    Keyword = Console.ReadLine();
    if (String.IsNullOrEmpty(Keyword) == true) continue;

    List Result = Extractor.Search(Keyword);

    Console.WriteLine("==========================");

    foreach (String Item in Result)
    {
        Console.WriteLine(" - " + Item);
        Console.WriteLine("==========================");
    }
    

} while (true);
위는 Extractor Class를 사용한 예 입니다. 
사용자 삽입 이미지
크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by 곡스
TAG c#, Naver, openapi
WPF2008/08/13 19:58

이번 시간에는 Google Open API중 날씨 API를 사용하여 한주간의 날씨를 확일 할 수 있는 Weather Board를 구현해보도록 하겠습니다. 이번 프로젝트 역시 DataBinding 기술을 사용하여 구현되지만 XmlDataProvider를 사용하여 XML Document에서 DataBinding을 적용하는 방법에서 조금 다를 수 있습니다. 구현 결과물은 아래와 같으며 Application이 시작 될 때 Google에서 제공하는 날씨정보를 기반으로 내용이 채워 지게 됩니다.

사용자 삽입 이미지


Google Open API에서 제공하는 날씨 정보를 제공하는 URL은 아래와 같으며 접속해보시면 아시겠지만, 데이터가 XML형태로 제공되는 것을 확인 하실 수 있습니다.

http://www.google.co.kr/ig/api?weather=seoul

실제 우리가 구현할 내용에 현재 온도나 바람 방향 등의 정보는 없지만, 실제로는 최저기온, 최고기온, 바람방향 등의 정보를 제공하며 주소 뒤에 seoul이라고 표시된 부분을 아래와 같이 원하는 지역의 이름으로 입력하시면 해당 지역의 현재 날씨를 가져 올 수 있습니다.

이제 본격적으로 구현에 들어가보도록 하겠습니다. 아래는 소스코드입니다.

XAML

<Window x:Class="Weatherboard.Window1"
xmlns=http://schemas.microsoft.com/winfx/2006/xaml/presentation
xmlns:x=http://schemas.microsoft.com/winfx/2006/xaml
xmlns:w="clr-namespace:Weatherboard"
Title="WPF Weather Board" Height="400" Width="500">
<
Window.Resources>
<
ImageBrush ImageSource="Background.jpg" x:Key="Background"/>
<
XmlDataProvider Source="http://www.google.co.kr/ig/api?weather=seoul"
XPath="xml_api_reply/weather" x:Key="Weather"/>
<
w:GoogleImageConverter x:Key="GoogleImageConverter"/>

<Style TargetType="TextBlock">
<
Setter Property="FontSize" Value="11"/>
<
Setter Property="TextAlignment" Value="Center"/>
<
Setter Property="HorizontalAlignment" Value="Left"/>
<
Setter Property="VerticalAlignment" Value="Bottom"/>
<
Setter Property="Width" Value="40"/>
<
Setter Property="Height" Value="20"/>
</
Style>
<
Style TargetType="Image">
<
Setter Property="HorizontalAlignment" Value="Left"/>
<
Setter Property="VerticalAlignment" Value="Bottom"/>
<
Setter Property="Width" Value="40"/>
<
Setter Property="Height" Value="40"/>
</
Style>
</
Window.Resources>
<
Grid Background="{StaticResource Background}" Width="420" Height="300">

<
Image Source="{Binding Source={StaticResource Weather}, XPath=current_conditions/icon/@data,
Converter={StaticResource GoogleImageConverter}}" Margin="68,0,0,94" />
<
Image Source="{Binding Source={StaticResource Weather}, XPath=forecast_conditions[1]/icon/@data,
Converter={StaticResource GoogleImageConverter}}" Margin="129,0,0,94" />
<
Image Source="{Binding Source={StaticResource Weather}, XPath=forecast_conditions[2]/icon/@data,
Converter={StaticResource GoogleImageConverter}}" Margin="190,0,190,94"/>
<
Image Source="{Binding Source={StaticResource Weather}, XPath=forecast_conditions[3]/icon/@data,
Converter={StaticResource GoogleImageConverter}}" Margin="251,0,0,94" />
<
Image Source="{Binding Source={StaticResource Weather}, XPath=forecast_conditions[4]/icon/@data,
Converter={StaticResource GoogleImageConverter}}" Margin="312,0,0,94" />



<
TextBlock Text="{Binding Source={StaticResource Weather},
XPath=current_conditions/condition/@data}" Margin="68,0,0,72" />
<
TextBlock Text="{Binding Source={StaticResource Weather},
XPath=forecast_conditions[1]/condition/@data}" Margin="129,0,0,72" />
<
TextBlock Text="{Binding Source={StaticResource Weather},
XPath=forecast_conditions[2]/condition/@data}" Margin="190,0,190,72" />
<
TextBlock Text="{Binding Source={StaticResource Weather},
XPath=forecast_conditions[3]/condition/@data}" Margin="251,0,0,72" />
<
TextBlock Text="{Binding Source={StaticResource Weather},
XPath=forecast_conditions[4]/condition/@data}" Margin="312,0,0,72" />


</
Grid>

</
Window>

C#

[ValueConversion(typeof(String), typeof(String))]
public class GoogleImageConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{

return "http://www.google.co.kr/ig" + (String)value;
}

public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return value;
}
}

먼저 XAML코드부터 보겠습니다. 중요한 부분은 노란색으로 색을 칠해놨으니 그 부분을 중점적으로 설명하도록 하겠습니다.

<XmlDataProvider Source="http://www.google.co.kr/ig/api?weather=seoul" XPath="xml_api_reply/weather" x:Key="Weather"/>

XMLDataProvider는 간단하게 설명해서 XAML에서 DataBinding을 가능하게 하기 위한 도구로서 (MSDN에는 데이터 바인딩을 위해 XML에 대한 선언적 액세스를 활성화하는 것 이라 설명되있습니다.) Source Property에 XML Document의 URL을 입력합니다. 여기에 앞에서 설명했던 WeatherAPI URL을 입력했습니다. 그리고 XPath에 접근할 XML경로를 입력합니다. 우리가 사용할 WeatherAPI는 아래와 같은 구조로 되어있으며,

사용자 삽입 이미지


실제 데이터가 Weather안에 저장되어 있으므로 xml_api_reply/weather라고 XPath를 지정했습니다. XPath는 단순히 경로뿐 아니라 간단한 쿼리 까지 적용이 가능하여 매우 효과적으로 사용 할 수 있습니다. (XPath에 대한 자세한 내용은 MSDN을 참고하시기 바랍니다.)

그리고 이제 날씨정보를 표시하기 위한 Image 객체를 생성하고 Image객체의 Source를 Weather API에서 제공받는 Icon Image로 Binding 시켜줍니다. (icon의 data Attribute에 저장되어있습니다.)

<Image Source="{Binding Source={StaticResource Weather}, XPath=current_conditions/icon/@data,
Converter={StaticResource GoogleImageConverter}}" Margin="68,0,0,94" />

Binding Source는 위에서 선언한 XMLDataProvider가 됩니다. 그리고 여기서도 XPath를 지정할 수 있는데, XmlDataProvider에서 지정한 Path이후만 작성하면 됩니다. 만약 루트부터 Path를 입력하고자 한다면 아래와 같이 표현 할 수 있습니다.

//xml_api_reply_weather/current_conditions/icon/@data

여기서 한가지 고려 해야 할 사항이 WeatherAPI에서 제공해주는 Icon Image가 아래와 같이 절대 경로가 아닌 상대경로로 입력되어 있다는 점인데,

사용자 삽입 이미지

이 때문에 직접 ValueConverter를 구현하여 앞부분 서버 경로를 입력해주셔야 합니다. ValueConvert를 구현한 부분이 C#으로 된 부분이며 Convert를 Resource로 등록하여 사용하고 있습니다. 이후 내용은 반복적이기 때문에 각각의 아이템에 대한 설명은 생략하도록 하겠습니다.
사용자 삽입 이미지

크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by 곡스