꿈꾸는 시스템 디자이너

HTML의 javascript단에서 QT 슬롯 함수 호출 방법 본문

Development/Qt

HTML의 javascript단에서 QT 슬롯 함수 호출 방법

독행소년 2015. 12. 2. 18:42

QT 어플리케이션에서 웹페이지를 이용하기 위해서는 웹뷰(QWebView)를 이용한다. 아래의 그림은 하나의 윈도우에 두개의 웹뷰를 통해 구글맵을 표시하고 사용자가 맵을 클릭하면 맵 해당 위치의 위도,경도를 표시하는 예제이다.

또한 그 위도와 경도의 값을 QT의 UI에도 표시해 준다. 다시 말하면, 구글맵을 가지는 HTML파일의 자바스크립트가 QT를 호출하는 예제이다.

 

QtJavascriptTest.zip

 

위 예제에서 두 웹뷰의 차이는 첫 번째 웹뷰는 QT 디바이너를 통해 메인윈도우 폼에 배치한 것이고, 두 번째 웹뷰는 코드상에서 직접 QWebView를 생성하여 사용한 것이다.

 

QT와 HTML의 자바스크립트간의 통신을 위해서는 HTML파일에 오브젝트를 주입하고, HTML의 자바스크립트가 이 오브젝트를 통해 QT의 슬롯을 호출하는 방식을 이용해야 한다.

 

자바스크립트에서 사용할 오브젝트는 다음과 같다.

 

<locationinfo.h>

 #ifndef LOCATIONINFO_H

#define LOCATIONINFO_H

#include <QObject>
#include <QPointF>

class MainWindow;

class LocationInfo : public QObject
{
    Q_OBJECT
public:
    explicit LocationInfo(QObject *parent = 0);
    QPointF m_location1;
    QPointF m_location2;

private:
    MainWindow *m_win;

public slots:
    // html의 javascript로부터 호출될 시그널 함수
    // public영역이 아닌 slots 영역에 정의해야함을 주의!!!
    void sltLocationChanged(float x, float y, int index);
};

#endif // LOCATIONINFO_H

 

<locationinfo.cpp> 

 #include "locationinfo.h"

#include "mainwindow.h"

LocationInfo::LocationInfo(QObject *parent) : QObject(parent)
{
    m_win = (MainWindow *)parent;
}

// html의 javascript로부터 호출될 시그널 함수
// public영역이 아닌 slots 영역에 정의해야함을 주의!!!
void LocationInfo::sltLocationChanged(float x, float y, int index)
{
    switch(index){
    case 1:
        m_location1.setX(x);
        m_location1.setY(y);
        break;
    case 2:
        m_location2.setX(x);
        m_location2.setY(y);
        break;
    }
    m_win->changeLocation(index);
}

 

sltLocationChange()가 자바스크립트단에서 호출할 슬롯함수이다.

 

<test1.html> 

 <!DOCTYPE html>

<html>
  <head>
    <title>Simple Map</title>
    <meta name="viewport" content="initial-scale=1.0">
    <meta charset="utf-8">
    <style>
      html, body {
        height: 100%;
        margin: 0;
        padding: 0;
      }
      #map {
        height: 100%;
      }
    </style>
  </head>
  <body>
    <div id="map"></div>
    <script>

//전역 변수
var map;
var myLatlng;

var marker = null;
var inforwindow = null;

function initMap() {

        var myOptions = {
        center: new google.maps.LatLng(36.36907, 127.37892),
        zoom: 14,
        mapTypeId: google.maps.MapTypeId.ROADMAP,
        panControl: true,   // 위치 이동 가능여부 설정
        zoomControl: true,  //  설정 가능여부 설정
        streetViewControl: false // 스트리트뷰 사용 가능여부 설정
        };

        map = new google.maps.Map(document.getElementById("map"),
        myOptions);

        // 맵을 클릭할  호출되는 리스너, 마커의 위치를 클릭 위치로 옮김
        map.addListener('click', function(e) {
        placeMarkerAndPanTo(e.latLng, map);
        });
}

function placeMarkerAndPanTo(latLng, map) {
        myLatlng = latLng;
        var contentString = '<b>latitude: </b>'+latLng.lat()

+'<br> <b>longitude: </b>'+latLng.lng();

        if(marker == null){
                //마커 생성
                marker = new google.maps.Marker({
                        position: latLng,
                        map: map
                });

                //인포윈도우 생성
                infowindow = new google.maps.InfoWindow({
                        content: contentString
                });

        }
        marker.setPosition(latLng);
        map.panTo(latLng);
        infowindow.setContent(contentString);
        infowindow.open(map,marker);

        // QT단에서 주입한 오브젝트를 통해 시그널 호출
        locInfo.sltLocationChanged(latLng.lat(),latLng.lng(),1);
}

    </script>
    <script src="http://maps.googleapis.com/maps/api/js?key=YOUR_KEY&callback=initMap"
        async defer></script>
  </body>
</html>

 

위의 HTML파일은 웹뷰를 통해 출력한 웹페이지이다. 구글맵을 로딩하고 사용자가 구글맵을 클릭하면 해당 위치에 마커를 표시하고 마커 위해 위도와 경도의 정보를 출력한다. 그리고 마지막으로 빨간색으로 표시된 코드와 같이 sltLocationChanged 슬롯함수를 호출하고있다. 앞에 locInfo는 QT에서 주입한 오브젝트를 의미한다.

 

<mainwindow.cpp>

#include "mainwindow.h"

#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    // HTML의 javascript로부터 데이터를 읽어올 오브젝트
    m_locInfo = new LocationInfo(this);

    /** Case 1,WebVew 생성(From에 WebView를 추가하고 해당 WebView를 받아오는 경우 **/
   m_view1 = ui->webView; // ui로부터 WebView를 받아옴
    m_view1->setUrl(QUrl("qrc:/test1.html")); // WebView에 출력할 html파일 설정
    // 오브젝트를 바로 주입하지 않고, signal을 받은  주입함
    connect(m_view1->page()->mainFrame(),SIGNAL(javaScriptWindowObjectCleared())

,this,SLOT(sltAddObject()));


    /** Case 2,WebVew를 직접 생성하는 경우 **/
   m_view2 = new QWebView(this);   // WebView 생성
    m_view2->setUrl(QUrl("qrc:/test2.html"));   // WebView에 출력할 html파일 설정
    m_view2->setGeometry(430,15,400,300);   // WebView의 위치와 크기 설정

// 오브젝트를 직접 바로 주입

    m_view2->page()->mainFrame()->addToJavaScriptWindowObject("locInfo",m_locInfo); 
    // WebView 출력, MainWindow내에서 생성되었기 때문에 show() 호출 없이도 바로 출력됨
    // 윈도우 없이 콘솔응용에서 바로 WebView를 생성하는 경우에는 show()를 호출해야 출력됨
    //m_view2->show();
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::changeLocation(int index)
{
    switch(index){
    case 1:
        ui->leLat01->setText(QString::number(m_locInfo->m_location1.rx()));
        ui->leLng01->setText(QString::number(m_locInfo->m_location1.ry()));
        break;
    case 2:
        ui->leLat02->setText(QString::number(m_locInfo->m_location2.rx()));
        ui->leLng02->setText(QString::number(m_locInfo->m_location2.ry()));
        break;
    }
}

void MainWindow::sltAddObject()
{
    m_view1->page()->mainFrame()->addToJavaScriptWindowObject("locInfo",m_locInfo);
}

 

실제 웹뷰에 html파일을 설정하고, 오브젝트를 주입하는 코드는 위와 같다.

두번째 하이라이트부터 보면, 웹뷰(m_view2)를 new를 통해 직접 생성하였다. 출력할 웹페이지(test2.html)를 설정한 후,  addToJavaScriptWindowObject()를 통해 바로 오브젝트를 주입힌다.

 

첫번째 하이라이트의 경우에는 ui로부터 웹뷰(m_view1)를 받아오며, 웹페이지(test1.html)를 설정한 후 connect함수를 통해 javaScriptWindowObjectCleared() 시그널을 기다린 후에야 sltAddObject() 슬롯함수를 통해 오브젝트를 주입한다.

첫번째 웹뷰를 두번째 웹뷰처럼 직접 오브젝트를 주입하면, 웹뷰상에 웹페이지는 정상적으로 출력되지만 오브젝트가 주입되지 않아 맵을 클릭해도 데이터를 수신할 수 없다. 그렇기 때문에 connect를 통해 시그널을 기다린 후 오브젝트를 주입해야 함을 주의해야 한다.

 

 

        

 

Comments