본문 바로가기
Programming/Golang

Go언어 GUI 시스템 트레이에 아날로그 시계 만들기 with go-sciter

by Wilkyway 2020. 9. 25.
반응형

지금까지 만든 시계앱을 시스템 트레이로 만들어보도록 하겠습니다. 만들고 나면 이런 모양의 시스템 트레이가 하나 생성되게 됩니다. (Windows 기준). 모든 소스는 아래 공개해 놓았으며, 이전포스트는 굳이 참고하지 않아도 문제없습니다.

 

 

이미지는 다른 시계 예제에서 다운받아 온 것인데, 나름 귀엽네요. 그럼 이제 본격적으로 들어가보도록 하겠습니다.

 

1. 파일 정리

우선 지금까지는 main.go와 main.html에서 각각 기능과 뷰를 담당했는데, 앞으로는 시스템 트레이 프로그램이 메인이 되고, 거기서 clock 프로그램을 불러오는 방식으로 수정하도록 하겠습니다.


       main.go     : 시스템 트레이 실행 (시계 프로그램 불러오기)

           ㄴ clock.go   : 시계 프로그램 실행 (윈도우 생성 / 뷰 불러오기, 기존 main.go)

                  ㄴ clock.html  : 시계 화면구성 및 동작 (기존 main.html)

        assets - clock.ico  : 시스템 트레이용 시계 이미지


구조가 단순하므로 패키지 구성 없이 그냥 같은 폴더 내 main 패키지로 진행하겠습니다. 이미지는 별도로하구요. 트레이 아이콘으로 쓸 이미지를 공유드리니, 프로젝트 폴더 내 assets라는 폴더를 하나 더 만들어 그 안에 넣어주세요. (아래 코드에 따라...)

 

clock.ico
0.19MB

 

 

2. 라이브러리 설치

시스템 트레이 제작을 위해서는 시스템 트레이를 위한 라이브러리를 설치해야 하는데, 아주 이지한 Golang Library가 존재합니다. getlantern/systray라는 라이브러리입니다. 아래 명령으로 설치해줍니다.

go get github.com/getlantern/systray

 

3. main.go , 시스템 트레이 실행파일

시스템 트레이 프로그램을 아래와 같이 작성합니다. 라이브러리를 임포트 하고, main함수에서 onReady와 onExit함수를 불러옵니다. onReady를 불러와서 기본적인 세팅을 하고, 고루틴으로 clock() 실행, quit메뉴 선택시 종료하는 함수를 실행합니다. 종료시 실행해야 할 일은 없으므로 onExit는 비워둡니다. 

package main

import (
	"fmt"
	"io/ioutil"
	"github.com/getlantern/systray" // window tray 관련 라이브러리
)

// Preaprare Program for execution ///
func main() {

	systray.Run(onReady, onExit)

}

func onReady() {
	systray.SetIcon(getIcon("assets/clock.ico"))			// Icon 불러오기
	systray.SetTitle("I'm alive!")
	systray.SetTooltip("Look at me, I'm a tooltip!")		// 마우스 오버 시 툴팁 표시

	mQuit := systray.AddMenuItem("Quit", "Quitsthis app")	// 종료 메뉴 추가

	go func() {
		for {
			select {
			case <-mQuit.ClickedCh:
				systray.Quit()
				return
			}
		}
	}()

	go func() {
		clock()
	}()

}

func onExit() {
	// Cleaning stuff here.
}

func getIcon(s string) []byte {
	b, err := ioutil.ReadFile(s)
	if err != nil {
		fmt.Print(err)
	}
	return b
}

4. clock.go 프로그램 작성

지난 post에서 작성한 main.go를 수정하여 작성하도록 하겠습니다. 자세한 변경은 아래 코드를 보시고, 중요 변경사항에 대해서만 말씀드리자면...

 

  • clock함수 작성: 기존에 main으로 되어있는 부분을 clock으로 수정합니다.
  • 작업표시줄에서 제거: window생성 부분에서(line30~35) sciter.SW_TOOL 을 추가합니다. 이 부분은 시스템 트레이로 실행되는 프로그램이 작업표시줄에도 표시되는 것을 막아줍니다.
  • 앱 위치 수정: 저는 시계가 화면 우상단에 위치시키겠습니다. 이를 위해 화면 사이즈를 시스템으로부터 얻어와야 하는데, 이것이 win.GetSystemMetrics(win.SM_CXSCREEN), win.GetSystemMetrics(win.SM_CySCREEN)입니다. 이를 잘 활용하여 앱의 크기와 위치를 재설정해줍니다. 
package main

import (
	"fmt"

	"github.com/lxn/win"

	sciter "github.com/sciter-sdk/go-sciter"
	"github.com/sciter-sdk/go-sciter/window"
)

// Specifying  havily used
// Singltons to make them
// package wide available
var root *sciter.Element
var rootSelectorErr error
var w *window.Window
var windowErr error

// Preapare Scitre For Execution ///
func init() {

	// initlzigin window for downloaer
	// app with appropriate properties
	cw := 200                                        //clock width
	ch := int(win.GetSystemMetrics(win.SM_CYSCREEN)) // clock height
	x_pos := int(win.GetSystemMetrics(win.SM_CXSCREEN)) - cw
	y_pos := 10
	rect := sciter.NewRect(y_pos, x_pos, cw, ch)
	w, windowErr = window.New(sciter.SW_TITLEBAR|
		sciter.SW_CONTROLS|
		sciter.SW_MAIN|
		sciter.SW_TOOL| //tool모드로, 작업표시줄에 아이콘 표시 안함.
		sciter.SW_GLASSY,
		rect)

	if windowErr != nil {
		fmt.Println("Can not create new window")
		return
	}
	// Loading main html file for app
	htloadErr := w.LoadFile("./clock.html")
	if htloadErr != nil {
		fmt.Println("Can not load html in the screen", htloadErr.Error())
		return
	}

	// Initializng  Selector at global level as we  are going to need
	// it mostly and as it is
	root, rootSelectorErr = w.GetRootElement()
	if rootSelectorErr != nil {
		fmt.Println("Can not select root element")
		return
	}

	// Set title of the appliaction window
	w.SetTitle("Simple")

}

// Preaprare Program for execution ///
func clock() {
	w.Show()
	w.Run()
}

5. clock.html 프로그램 작성

화면을 구성하는 clock.html파일을 아래와 같이 작성해줍니다. 지난 포스트의 main.html로부터 변경된 사항은

  • html태그에 window-resizable="true" 속성을 추가하여 크기조절이 가능하도록 했습니다.(....만 transparent 속성때문에 프레임이 선택되지 않습니다. transparent 이 외에 extended/solid 등 다른 옵션의 경우 가능하니 확인해보시기 바랍니다. sciter.com/html-window/
  • view.windowTopmost=true 속성을 스크립트 부분에 추가하여 시계가 항상 위에 표시되도록 합니다.
  • CSS에서 fixed클래스를 선언해서 해당 앱이 항상 다른 앱보다 뒤에서 표시되도록(바탕화면 바로 다음에..) 하려고 하였으나, 위 속성때문에 제대로 작동하진 않습니다.
  • 색상과 선의 두께 등도 기호에 맞게 조정합니다.
<html window-frame="transparent" window-resizable="true">
  <head>
    <title>Sciter2 clock</title>
    <style>
      html{
        background:transparent;
      }
      div.clock { 
        prototype:Clock; 
        width:*;
        height:*;
        /*border:1px solid black;*/
      }
      h3{
        color: white;
      }
      .fixed { position:fixed; left:0; top:0; right:0; bottom:0; z-index:-1; }
    </style>
    <script type="text/tiscript">

!function(){

  $(span#os).text = System.OS;
  
}();   

view.windowTopmost = true;   //항상 위에 존재

class Clock: Behavior 
{

  function attached() { 
    this.paintContent = this.drawclock; // attaching draw handler to paintContent layer
    this.timer(300,::this.refresh());
    
  }

  function drawclock(gfx)
  {
    
    var (x,y,w,h) = this.box(#rectw);
    var scale = w < h? w / 300.0: h / 300.0;
    var now = new Date();
    gfx.save();
    gfx.translate(w/2.0,h/9.0);
    gfx.scale(scale,scale);    
    gfx.rotate(-Math.PI/2);
    gfx.lineColor(color(0x32,0xA2,0xCF));
    gfx.lineWidth(3);
    gfx.lineCap = Graphics.CAP_ROUND;
       
    // Hour marks
    gfx.save();
    gfx.lineColor(color(0x32,0x5F,0xCF));
    for (var i in 12) {
      gfx.rotate(Math.PI/6);
      gfx.line(100,0,120,0);
    }
    gfx.restore();

    // Minute marks
    gfx.save();
    gfx.lineWidth(5);
    for (var i in 60) {
      if ( i % 5 != 0)
        gfx.line(117,0,120,0);
      gfx.rotate(Math.PI/30);
    }
    gfx.restore();

    var sec = now.second;
    var min = now.minute;
    var hr  = now.hour;
    hr = hr >= 12 ? hr-12 : hr;
  
    // write Hours
    gfx.save();
    gfx.lineColor(color(0x32,0x5F,0xCF));
    gfx.rotate( hr*(Math.PI/6) + (Math.PI/360)*min + (Math.PI/21600)*sec )
    gfx.lineWidth(14);
    gfx.line(-20,0,70,0);
    gfx.restore();

    // write Minutes
    gfx.save();
    gfx.lineColor(color(0x32,0x5F,0xCF));
    gfx.rotate( (Math.PI/30)*min + (Math.PI/1800)*sec )
    gfx.lineWidth(10);
    gfx.line(-28,0,100,0);
    gfx.restore();
      
    // Write seconds
    gfx.save();
    gfx.rotate(sec * Math.PI/30);
    gfx.lineColor(color(0xD4,0x32,0));
    gfx.fillColor(color(0xD4,0x32,0));
    gfx.lineWidth(6);
    gfx.line(-30,0,83,0);
    gfx.ellipse(0,0,10);
    
    gfx.noFill();
    gfx.ellipse(95,0,10);
    gfx.restore();
    
    // outline
    gfx.lineWidth(8);
    gfx.lineColor(color(0x32,0xA2,0xCF));
    gfx.ellipse(0,0,142);
    
    gfx.restore();
    
  }  
    
}
 
    </script>
  </head>
<body>
  <div.fixed>
    <h3>Sciter clock using immediate mode drawing on <span #os></span></h3>
    <div class="clock" >
    </div>
  </div>


</body>
</html>

 

라이브러리 설치에 빠진 것이 없고, 위와 같이 구성하고 실행하면 작성한 프로그램이 항상 위에 작동하고, 시스템 트레이에서 실행되고, 작업표시줄에는 나타나지 않는 것을 보실 수 있을겁니다.

 

 

나름 위젯처럼 되었습니다. ㅎㅎㅎㅎㅎ

 

그럼 모두들 건승하십시오.

오늘은 이만~~~

 

 

반응형

댓글