반응형

장비제어에서 카메라에 모터를 장착하여 Auto Fucusing 을 사용하는 경우가 많이 있습니다. 

전문적인 비젼 전문가는 아니라서 최적의 Auto Fucusing 알고리즘을 코딩하지는 못하지만 그나마 간단하게 실무에서 사용이 가능한 Auto Fucusing 알고리즘 코드를 올려 놓습니다. 

 

이전 카메라 제어 소스(OpenCVSharp 카메라 제어 #4)의 CameraThread 클래스에 아래 함수를 추가 하시면 됩니다. 

public double calculate_focus_score()
{
    double score = 0;
    Mat median = new Mat();
    Mat laplacian = new Mat();

    try 
    { 
        Cv2.MedianBlur(sourceFrame, median, 3) ;
        Cv2.Laplacian(median, laplacian, median.Type().Depth, 3, 1, 0);

        Cv2.MeanStdDev(laplacian, out var mean, out var stddev);

        score = stddev.Val0 * stddev.Val0; 
    }
    finally
    { 
        median.Dispose();
        laplacian.Dispose();
    }

    return score ;
}

사용방법 / 순서 

 

1. 카메라 모터를 초기위치(최상단위치)로 옮겨 놓는다. 

2. 모터를 일정높이 만큼 낮춘다. 

3. calculate_focus_score() 함수를 실행하여 리턴되는 값과 현재 모터 위치값을 매핑한다. 

4. 2~3을 일정 높이에 다다를 때까지 반복한다. 

5. calculate_focus_score() 함수 실행 결과값이 가장 큰값이 포커싱이 가장 잘된 위치이므로

    값이 가장큰 모터 위치로 카메라를 이동한다. 

 

반응형
반응형

일반적으로 계측장비나 온도컨트롤러의 경우 계측값을 HEX로 표현하여 통신하는 경우가 많습니다.

수신된 데이타의 값이 Hex 문자열로 들어올 때 간단하게 정수값으로 변경하는 방법입니다. 

 

const char *hexstring = "abcdef0";

int number = (int)strtol(hexstring, NULL, 16);   // 16의 의미는 hexstring 이 16진수라는 의미입니다.

 

String 문자열에 0x 의 16진수 기호가 붙어 있을 경우는 아래와 같이 사용합니다. 

 

const char *hexstring = "0xabcdef0";

int number = (int)strtol(hexstring, NULL, 0);

반응형
반응형

void __fastcall Windows_ShutDown(void)
{
    HANDLE hToken;
    TOKEN_PRIVILEGES tp;
    LUID luid;
    OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY, &hToken);
    LookupPrivilegeValue(NULL, "SeShutdownPrivilege", &luid);
    tp.PrivilegeCount = 1 ;
    tp.Privileges[0].Luid = luid ;
    tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED ;
    AdjustTokenPrivileges(hToken, false, &tp, 0, NULL, NULL) ;
    ExitWindowsEx(EWX_SHUTDOWN, 0);
}

반응형
반응형

C++ 빌더 툴을 사용하다가 C#으로 전환하면서 하나 불편한 부분이 바로 RadioButton 이었습니다. 

빌더 툴에서 RadioGroup 라는 컴포넌트가 있어서 RadioGroup 의 Index 속성을 이용하여 코딩을 줄일 수가 있었는데 C#에는 그와 같은 컴포넌트가 없어서 너무 불편했습니다. 

 

아래 그림처럼 RadioButton 을 만들었을 경우 

일반적으로는 아래의 코드처럼 각각의 메뉴에 이벤트를 만들어 사용하게 됩니다. 

public partial class Form1 : Form
{
   enum Meat{등심,안심,갈비}

   private Meat _selectedMeat;

   public Form1()
   {
      InitializeComponent();
   }
   private void radioButton1_Click(object sender, EventArgs e)
   {
      this._selectedMeat = Meat.등심;
      DisplayMenu();
   }
   private void radioButton2_Click(object sender, EventArgs e)
   {
      this._selectedMeat = Meat.안심;
      DisplayMenu();
   }
   private void radioButton3_Click(object sender, EventArgs e)
   {
      this._selectedMeat = Meat.갈비;
      DisplayMenu();
   }
   private void DisplayMenu()
   {
      lblMsg.Text = string.Format("{0} 메뉴를 선택하셨습니다.", this._selectedMeat.ToString());
   }
}

 

위의 코드를 Tag 속성을 이용해서 간단하게 줄여 보겠습니다. 

먼저 메뉴 항목의 각 RadioButton Tag 속성을  (등심:0, 안심:1, 갈비:2) 로 설정합니다. 

그리고 3개의 RadioButton Click이벤트를 각각 만들지 말고 하나의 동일한 이벤트 함수를 만들어 공유 합니다. 

코드는 아래와 같습니다. 

 

public partial class Form1 : Form
{
    string[] Meat = { "등심", "안심", "갈비" };

    public Form1()
    {
        InitializeComponent();
    }

    private void RadioButton_Click(object sender, EventArgs e)
    {
        var select = int.Parse( (sender as RadioButton).Tag.ToString() );
        lblMsg.Text = string.Format("{0} 메뉴를 선택하셨습니다.", Meat[select]);
    }
}

4개의 함수가 하나의 함수로 줄어들고 코드도 2줄로 확 줄었습니다. 

이렇게 Tag를 이용하면 차후에 Switch 구문을 사용하여 코딩하기도 굉장히 쉽습이다. 

 

또다른 방법으로 패턴매칭을 이용하여 처리하는 방법 입니다. 위의 코드보다 복잡하지만 유용하게 사용되는 형식읠 때도 있습니다.

public partial class Form1 : Form
{
    string[] Meat = { "등심", "안심", "갈비" };

    public Form1()
    {
        InitializeComponent();
    }

    private void RadioButton_Click(object sender, EventArgs e)
    {
        RadioButton radio = sender as RadioButton ;
        
        _ = radio?.Tag switch
        {
            "0" => lblMsg.Text = string.Format("{0} 메뉴를 선택하셨습니다.", Meat[0]) ,
            "1" => lblMsg.Text = string.Format("{0} 메뉴를 선택하셨습니다.", Meat[1]) ,
            "2" => lblMsg.Text = string.Format("{0} 메뉴를 선택하셨습니다.", Meat[2]) ,
            _ => lblMsg.Text = "주문이 없습니다." 
        }
    }
}
반응형
반응형

74HC573 는 TTL 시리즈 중에서 8bits Latch IC 로 원하는 출력값을 다음 변경전까지 유지시키기 위한 용도로 사용합니다. 

제 경우에는 주로 릴레이 On, Off 에 많이 사용했습니다. 

 

그런데 주로 아두이노나노 보드를 사용하다 보니 출력 핀수가 적어서 시리얼로 제어가 가능한 74hc595 를 많이 사용했습니다. 

아두이노 메가 처럼 IO 핀이 많은 경우나 HC245 와 같은 입출력 포트를 같은 라인으로 사용할 경우 적용하면 좋을듯 하여 

코드를 만들어 보았습니다.

 

class HC573Latch 
{
public :
	
	void HC573Latch(uint8_t le, uint8_t enable,							// OE 단자를 사용할 경우 
		       uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3,
		       uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7);
			   
	void HC573Latch(uint8_t le, 											// OE 단자를 GND로 묶었을 경우 
		       uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3,
		       uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7);			   
			   
	void latch8bits(uint8_t value) ;

private :
	bool _oe_used ;				// OE 단자의 사용 여부 
	
	uint8_t _output_enable ;  // activated by a LOW 
	uint8_t _latch_pin;       // activated by a HIGH pulse.
	uint8_t _data_pins[8];
  
    void pulseEnable();
} ;
//-----------------------------------------------------------------------	
void HC573Latch(uint8_t le, uint8_t enable,							// OE 단자를 사용할 경우 
		        uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3,
		        uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7)
{
	_output_enable = enable ;
	_latch_pin = le ;
	_oe_used = true ;
	
	_data_pins[0] = d0 ;
	_data_pins[1] = d1 ;
	_data_pins[2] = d2 ;
	_data_pins[3] = d3 ;
	_data_pins[4] = d4 ;
	_data_pins[5] = d5 ;
	_data_pins[6] = d6 ;
	_data_pins[7] = d7 ;
	
	digitalWrite(_output_enable, HIGH) ;
}			   
//-----------------------------------------------------------------------			
void HC573Latch(uint8_t le, 							// OE 단자를 GND로 묶었을 경우 
		        uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3,
		        uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7)
{
	_latch_pin = le ;
	_oe_used = false ;
	
	_data_pins[0] = d0 ;
	_data_pins[1] = d1 ;
	_data_pins[2] = d2 ;
	_data_pins[3] = d3 ;
	_data_pins[4] = d4 ;
	_data_pins[5] = d5 ;
	_data_pins[6] = d6 ;
	_data_pins[7] = d7 ;
}			   
//-----------------------------------------------------------------------	   
void HC573Latch::latch8bits(uint8_t value) 
{
	if(_oe_used == true) digitalWrite(_output_enable, LOW) ;
	
	for (int i = 0; i < 8; i++) 
	{
		digitalWrite(_data_pins[i], (value >> i) & 0x01);	// 데이타 핀에 데이타를 출력한다. 
	}
  
	pulseEnable();  	// Latch 단자를 on->off 하여 출력을 변경한다.
	
	if(_oe_used == true) digitalWrite(_output_enable, HIGH) ;
}	
//-----------------------------------------------------------------------
void HC573Latch::pulseEnable(void) 
{
	digitalWrite(_latch_pin, HIGH);
	delayMicroseconds(1);   
	digitalWrite(_latch_pin, LOW);	
}
//-----------------------------------------------------------------------
반응형
반응형

Flutter 공부한지 3일 만에 계산기 앱을 만들다 보니 너무 두서 없이 만들어 졌습니다. 앱을 만들어 가는 과정에서 MVVM 패턴이라던가 상태관리라는 것을 적용할 필요가 있다는 것을 알았습니다. 

상태관리 package를 적용하여 프로그램을 수정하면서 이번 포스팅을 계속해야 하나 망설이다가 제어쪽 포스팅을 한참만에 올리게 되었습니다. 어차피 이번 포스팅은 초보자 관점에서 만들어 진것이니 제목 그대로 초보자가 만든 개념으로 포스팅은 마무리 하고 최종 수정된 프로그램은 별도로 공개 하는 방향으로 정하게 되었습니다. 

 

그래서 이번에 최종적으로 계산기 제어 코드를 올리면서 아울러 상태관리와 함께 지난 포스팅에 빠져 있던 몇가지 버튼 기능들도 추가 하여 코드를 수정하고 공개 하게 되었습니다. 

 

수정된 부분

1. MVVM 패턴 적용 : 파일도 패턴에 맞추어 분리 시키고 나름 MVVM 패턴을 적용해 보았습니다. 

2. 상태관리 팩키지 적용 : 상태관리 패키지 2가지를 따로 따로 적용하여 코드를 만들어 보았습니다.

   적용 패키지 (Provider, GetX )

3. 계산기의 몇가지 추가 버튼에 대한 기능을 추가 하였습니다. 

 

코드는 GitHub 에 올려 놓았고 provider 과 GetX 두개로 Branche 시켜 두었습니다. 

Main Branche 가 Provider 를 적용한 것이고 다른 하나는 GetX 입니다. 아래 Git 에서 소스 코드를 확인할 수 있습니다. 

 

https://github.com/Sihwan-Kim/calculator_flutter.git

반응형
반응형

계산기 만들기 마지막 포스팅으로 실제 계산기의 버튼을 누르고 연산을 하는 부분의 코드를 만듭니다. 

일단 컨트롤에 사용될 변수를 전역변수로 선언을 합니다. main 함수 위에 선언해 주시면 됩니다. 

var displayNumber = '0';
var makeNumber = '' ;
var selectedOperator = '−';
var displayFontSize = 80.0 ;
var pointExist = false ;

var firstNumber = 0.0 ;
var secondNumber = 0.0 ;

var _disignPage = new DesignPage() ;

 

컨트롤은 크게 3개의 함수로 구성되어 있습니다. 

 

1. 숫자키 입력 : 숫자키와 소숫점을 눌렀을 때 발생되는 알고리즘에 대한 함수 입니다. 숫자는 처음입력시에는 문자열의 형태로 저장을 하고 이후 연산할 때 double 형의 숫자로 변경하도록 프로그램 했습니다. 

void _numberOnPressed(String st)  		// 숫자키 입력 이벤트 함수 
{
	bool inputAdd = true ; 

	if(makeNumber.length < 9)  // 숫자는 9자리 까지만 
	{
		if(st == '.') // 소숫점이 눌려졌을 경우 
		{
			if(makeNumber.isEmpty == true)
			{ 
				makeNumber += '0.';
				inputAdd = false ;
			}
			else
			{
				if(pointExist == true) inputAdd = false ; // 소숫점이 없을 경우만 추가
			}

			pointExist = true ;
		}
		else if(st == '0' && makeNumber.isEmpty == true)  inputAdd = false;
		
		if(inputAdd == true) makeNumber += st ;

		displayNumber = makeNumber ;

		if(displayNumber.length < 7) displayFontSize = 80.0 ;  // 글자 크기를 선택 
		else displayFontSize = 50 ;

		_disignPage.setState(() { displayNumber ;});  // 화면을 갱신한다. 
	}
}
//-----------------------------------------------------------------------------------------

 

 

2. 연산자 키 입력 : 4개의 연산키를 눌렀을 때 적용되는 함수 입니다.

 

void _operatorOnPressed(String st)
{
	_resultOnPressed('=') ;

	selectedOperator = st ;
	firstNumber = double.parse(displayNumber) ;

	makeNumber = '';
	pointExist = false ;
}
//-----------------------------------------------------------------------------------------

 

3. '=', 'C'  연산결과와 클리어 기능의 함수 입니다. 

void _resultOnPressed(String st)
{
	if(st == 'C')		// clear input
	{
		makeNumber = ''; // clear clicked 
		displayNumber = '0' ;
		selectedOperator = '+' ;
	}
	else
	{
		secondNumber = double.parse(makeNumber) ;
		makeNumber = '';

		var result = 0.0 ;

		switch(selectedOperator)
		{
			case '+' : result = firstNumber + secondNumber ;	break ;
			case '−' : result = firstNumber - secondNumber ;	break ;
			case '×' : result = firstNumber * secondNumber ;	break ;
			case '÷' : result = firstNumber / secondNumber ;	break ;
		}

		displayNumber = result.toString();
	}
	 pointExist = false ;

	_disignPage.setState(() { displayNumber ;});  // 화면을 갱신한다. 
}

 

위의 3개 함수를 기존의 Layout 코드의 마지막 부분에 추가하여 넣습니다. 그리고 지난번에 CalButton 위젯 클래스를 아래와 같이 수정합니다. 

class CalButton extends StatelessWidget
{
	CalButton({super.key, required this.caption, required this.color, required this.buttonKind});
	final String caption ;  // 립력된 버튼의 문자 
	final Color color;      // 버튼의 색
	final int buttonKind;   // 입력된 버튼의 기능(0:숫자, 1:연산, 2:기능)

	@override
	Widget build(BuildContext context)
	{
		return ElevatedButton
		(
			onPressed: () 
			{ 
				switch(buttonKind)
				{
					case 0 : _numberOnPressed(caption); break ;
					case 1 : _operatorOnPressed(caption); break ;
					case 2 : _resultOnPressed(caption); break ; 
				}
			},
			style: ElevatedButton.styleFrom
			(
				backgroundColor: color, 
				fixedSize: Size((MediaQuery.of(context).size.width/4)-30, (MediaQuery.of(context).size.width/4)-20), 
				shape: const CircleBorder(),
			),
			child: Text('$caption', style: TextStyle(fontSize: 40,),),
		);
	}
}
//-----------------------------------------------------------------------------------------

마지막으로 MainPage 클래스도 아래 처럼 수정을 합니다. 

class MainPage extends StatefulWidget 
{
	@override
  	DesignPage createState() => _disignPage ; // DesignPage();
}
//-----------------------------------------------------------------------------------------

 

이것으로 계산기 앱의 기본 연산기능이 마무리 됩니다.

Flutter 시작하고 3일만에 만든 앱이라 두서없이 만들었습니다. 조금씩 공부하다 보니 손봐야 할 코드가 꽤 많네요..

제목 그대로 초보자 관점에서 만든것임을 감안해 주세요.. 아래 소스 전체 코드를 파일로 올려 놓습니다. 참고하세요

 

main.dart
0.01MB

반응형
반응형

이번 포스팅은 계산기 Layout 3번째로 키보드를 만들어 봅니다. 기본 화면설계 Layout은 이것으로 마무리가 될듯 합니다. 

한가지 이번 계산기 앱을 플로터 공부하고  2틀 만에 공부해가며 만들고 나서 계속 공부하는 와중에 참 설계가 잘못된 부분이 많구나 하는 것을 알았습니다. 기본적으로 윈도우 프로그램 개발하는 방식과는 차이가 좀 나네요.. 

 

제목에 초보자라는 타이틀이 있으니 초보자 입장에서 설계는 잘못 되었지만 일단은 기존에 설계된 코드로 포스팅을 하고 좀더 공부하면서 설계에 오류나 미진한 부분은 추가로 포스팅 하도록 하겠습니다. 

 

1. 버튼의 모양을 만들기 위한 위젯 Class 구현 

  * 버튼을 하나하나 개별적으로 만들수도 있겠지만 그러면 코드가 길어지기 때문에 클래스로 버튼의 틀을 만들고 속성값만 주어서 버튼이 생성될 수 있도록 합니다. 

   * 버튼 Class

class CalButton extends StatelessWidget
{
	CalButton({super.key, required this.caption, required this.color, required this.buttonKind});
	final String caption ;  // 버튼에 들어갈 문자 
	final Color color;      // 버튼의 색
	final int buttonKind;   // 입력된 버튼의 기능(0:숫자, 1:연산, 2:기능)

	@override
	Widget build(BuildContext context)
	{
		return ElevatedButton
		(
			onPressed: () {},  // 버튼입력 이벤트 처리 부분(나중에 코드 추가)
			style: ElevatedButton.styleFrom
			(
				backgroundColor: color, 
				fixedSize: Size((MediaQuery.of(context).size.width/4)-30, (MediaQuery.of(context).size.width/4)-20), 
				shape: const CircleBorder(),   // 버튼 모양은 원형 
			),
			child: Text('$caption', style: TextStyle(fontSize: 40,),),  // 버튼의 들어가는 글자 모양
		);
	}
}

2. 버튼 그룹 위젯 생성 

  * 위에 만들어진 버튼을 이용하여 4x5 행렬로 키보드 위젯을 만듭니다.

  * 위젯은 Table 라는 layout 위젯을 사용합니다. 

  * Class 구현 

class ButtonGroupWidget extends StatelessWidget 
{
  	const ButtonGroupWidget({super.key});

  	@override
  	Widget build(BuildContext context) 
	{
    	return Table
		(
			border: TableBorder.all(),
      		columnWidths: const <int, TableColumnWidth>
			{
        		0: FlexColumnWidth(),
        		1: FlexColumnWidth(),
        		2: FlexColumnWidth(),
				3: FlexColumnWidth(),  
      		},
      		defaultVerticalAlignment: TableCellVerticalAlignment.middle,	
      		children: <TableRow>
			[
        		TableRow
				(
					decoration: const BoxDecoration(color: Colors.black,),
          			children: <Widget>
					[
						Padding(padding: EdgeInsets.all(5), child: CalButton(caption: 'C', color: Colors.grey, buttonKind: 2,),),
						Padding(padding: EdgeInsets.all(5), child: CalButton(caption: '+/-', color: Colors.grey, buttonKind: 1,),),
						Padding(padding: EdgeInsets.all(5), child: CalButton(caption: '%', color: Colors.grey, buttonKind: 1,),),
						Padding(padding: EdgeInsets.all(5), child: CalButton(caption: '÷', color: Colors.orange, buttonKind: 1,),),
          			],
        		),
        		TableRow
				(
					decoration: const BoxDecoration(color: Colors.black,),  
          			children: <Widget>
					[
						Padding(padding: EdgeInsets.all(5), child: CalButton(caption: '7', color: Color.fromARGB(255, 61, 61, 61), buttonKind: 0,),),
						Padding(padding: EdgeInsets.all(5), child: CalButton(caption: '8', color: Color.fromARGB(255, 61, 61, 61), buttonKind: 0,),),
						Padding(padding: EdgeInsets.all(5), child: CalButton(caption: '9', color: Color.fromARGB(255, 61, 61, 61), buttonKind: 0,),),
						Padding(padding: EdgeInsets.all(5), child: CalButton(caption: '×', color: Colors.orange, buttonKind: 1,),),
          			],
        		),
				TableRow
				(
					decoration: const BoxDecoration(color: Colors.black,),
          			children: <Widget>
					[
						Padding(padding: EdgeInsets.all(5), child: CalButton(caption: '4', color: Color.fromARGB(255, 61, 61, 61), buttonKind: 0,),),
						Padding(padding: EdgeInsets.all(5), child: CalButton(caption: '5', color: Color.fromARGB(255, 61, 61, 61), buttonKind: 0,),),
						Padding(padding: EdgeInsets.all(5), child: CalButton(caption: '6', color: Color.fromARGB(255, 61, 61, 61), buttonKind: 0,),),
						Padding(padding: EdgeInsets.all(5), child: CalButton(caption: '−', color: Colors.orange,buttonKind: 1,),),
          			],
        		),
				TableRow
				(
					decoration: const BoxDecoration(color: Colors.black,),
          			children: <Widget>
					[
						Padding(padding: EdgeInsets.all(5), child: CalButton(caption: '1', color: Color.fromARGB(255, 61, 61, 61), buttonKind: 0,),),
						Padding(padding: EdgeInsets.all(5), child: CalButton(caption: '2', color: Color.fromARGB(255, 61, 61, 61), buttonKind: 0,),),
						Padding(padding: EdgeInsets.all(5), child: CalButton(caption: '3', color: Color.fromARGB(255, 61, 61, 61), buttonKind: 0,),),
						Padding(padding: EdgeInsets.all(5), child: CalButton(caption: '+', color: Colors.orange, buttonKind: 1,),),
          			],
        		),			
				TableRow
				(
					decoration: const BoxDecoration(color: Colors.black,),
          			children: <Widget>
					[
						Padding(padding: EdgeInsets.all(5), child: CalButton(caption: '0', color: Color.fromARGB(255, 61, 61, 61), buttonKind: 0,),),
						Padding(padding: EdgeInsets.all(5), child: CalButton(caption: '2', color: Color.fromARGB(255, 61, 61, 61), buttonKind: 1,),),
						Padding(padding: EdgeInsets.all(5), child: CalButton(caption: '.', color: Color.fromARGB(255, 61, 61, 61), buttonKind: 0,),),
						Padding(padding: EdgeInsets.all(5), child: CalButton(caption: '=', color: Colors.orange, buttonKind: 2,),),
          			],
        		),		
      		],
    	);
  	}
}

3. 이제 만들어진 버튼 그룹위젯을 화면에 추가 합니다. 

  * 지난 포스팅에 만든 DesignPage 클래스에 그룹 위젯을 생성 하여 넣습니다. 

  * 아래 코드에서 추가 되는 부분을 넣으시면 됩니다. 

class DesignPage extends State<MainPage>  
{
  	@override
	Widget build(BuildContext context)
	{
		return Scaffold
		(
			appBar: AppBar(title: Text('Calculator Program'),),
			body: Column
			(
				crossAxisAlignment: CrossAxisAlignment.stretch,
				children: <Widget>
				[
					Container
					(
						padding: EdgeInsets.all(30),
						alignment: Alignment(1.0, 1.0),   
						color: Colors.black,    					
						height: (MediaQuery.of(context).size.height - MediaQuery.of(context).padding.top) * 0.30,   // 화면의 30%를 차지하도록 설정
						child: displayText(caption: '$displayNumber', fontsize: displayFontSize,),
					),
          			ButtonGroupWidget(),  // 추가코드(버튼그룹위젯 생성)
				],					
			),
			backgroundColor: Colors.black,   
		);
	}
}

이것으로 화면 디자인에 대한 layout은 마무리 되었습니다. 

지금까지 만들어진 코드를 실행해 보면 처음에 보여 드린 계산기 모양이 나타나게 될 겁니다. 

반응형

+ Recent posts