JavaScript実行の基本とQtプログラム内のサイトとの相互作用

はじめに


おそらく、多くの人がクロスプラットフォームのQtライブラリについて聞いたことがあるでしょう。 WebKit Webページレンダリングエンジンはさらに優れています。 少し前までは、最初のものは2番目のラッパーを含むようになりました。50行でブラウザーを作成する例は見つけるのが難しくありません。 それでも、QtコードからWebページの個々の要素にアクセスする方法についてはあまり書かれていません。

この説明では、人々はPyQtの基本的な知識(サマーフィールドを教えた)とJavaScriptの漠然とした考えを持っていると仮定しています。 私は自分のレベルをそのように特徴づけているので、特にJavaスクリプトの記述で、エラーについて事前に謝罪します。 Pythonが言語として使用されているという事実にもかかわらず、C ++ / Qtプログラマーにも質問はありません。
テストケースは、PyQt-4.7.3、バージョンPython-2.6.6-r1、GNU / Linuxで実行されました。 プログラムからは、JS(Chromeなど)とPyQt IDEのデバッグ機能を備えたブラウザが必要になりますが、私はEric4を使用します。

例1.モックするブラウザー



# -*- coding: utf-8 -*-
from PyQt4.QtCore import *
from PyQt4.QtNetwork import *
from PyQt4.QtGui import *
from PyQt4.QtWebKit import *

class BaseBrowser(QWidget):
def __init__(self, parent = None):
super(BaseBrowser, self).__init__(parent)
self.__progress = 0
QNetworkProxyFactory.setUseSystemConfiguration(True)
self.webView = QWebView()
self.webView.load(QUrl( "http://www.yandex.ru" ))
self.connect(self.webView, SIGNAL( "loadFinished(bool)" ), self.adjustLocation)
self.connect(self.webView, SIGNAL( "titleChanged(QString)" ), self.adjustTitle)
self.connect(self.webView, SIGNAL( "loadProgress(int)" ), self.setProgress)
self.connect(self.webView, SIGNAL( "loadFinished(bool)" ), self.finishLoading)
self.locationEdit = QLineEdit()
self.locationEdit.setSizePolicy(QSizePolicy.Expanding, self.locationEdit.sizePolicy().verticalPolicy())
self.connect(self.locationEdit, SIGNAL( "returnPressed()" ), self.changeLocation)
self.goButton = QPushButton( "Go" )
self.connect(self.goButton, SIGNAL( "clicked()" ), self.changeLocation)
self.layout = QGridLayout(self)
self.layout.addWidget(self.locationEdit, 0, 0)
self.layout.addWidget(self.goButton, 0, 1)
self.layout.addWidget(self.webView, 1, 0, 1, 2)
self.setLayout(self.layout)

def adjustLocation(self):
self.locationEdit.setText(self.webView.url().toString())

def changeLocation(self):
url = self.locationEdit.text()
if url[0:7] != 'http://' :
url = 'http://' + url
self.webView.load(QUrl(url))
self.webView.setFocus()

def adjustTitle(self):
if self.__progress <= 0 or self.__progress >= 100:
self.setWindowTitle(self.webView.title())
else :
self.setWindowTitle(QString( "%1 (%2%)" ).arg(self.webView.title()).arg(self.__progress))

def setProgress(self, p):
self.__progress = p
self.adjustTitle()

def finishLoading(self):
self.__progress = 100
self.adjustTitle()

if __name__ == "__main__" :
import sys
app = QApplication(sys.argv)
prog = BaseBrowser()
prog.show()
sys.exit(app.exec_())


* This source code was highlighted with Source Code Highlighter .


サンプルコード1: pastebin.com/GVQ4dw1M

ブラウザは、C ++ / QtおよびPyQtのトレーニング例からのブラウザのテーマに関するいくつかのバリエーションを示します。次の2つの例では、これを継承します。 この方法でプログラムは小さなものであっても書けず、プログラムは1つのクラスであってはならないことを理解していますが、コードの数、その可視性、正しいアーキテクチャのバランスをできる限り守っています。
そのため、ブラウザはあまり知りませんが、入力されたページをロードして表示できます。これには、QWebViewウィジェットを使用し、このウィジェットによって生成された標準信号をブラウザのスロットに接続しました。これにより、プログラムは現在のSIGNAL Webページのタイトル(QString) ")、SIGNALのロードの進行状況(" loadProgress(int) ")およびロードの終了-SIGNAL(" loadFinished(bool) ")。 さらに、「Enter」を押すかボタンをクリックして、ページアドレスとこのWebページに移動するボタンを入力するQlineEditフィールドが作成されます。
ブラウザを起動し、作業中です。「裸の」WebKitの作業速度からすごいです。 これまでのところ、特別なことは何も書いていません。 ブラウザはすべてのリンクをたどっていません。

例2. DOMツリーとQtからの要素へのアクセス



一般に、HTMLページの構造については個別に読むほうが良いでしょう。2つの文章で説明するのは問題です。 一般に、何らかのWebインターフェイスに対してオフラインシェルを作成する場合、少なくともデータアクセスに関連するJavaスクリプトを学習する必要があります。 そのため、最新のブラウザでは、各ノードが要素、属性、テキスト、グラフィック、またはその他のオブジェクトであるノードツリーの形式でWebページを表示することにより、Webページのコンテンツにアクセスできます。 ノードは親子関係によって相互接続されます(はい、この行はウィキペディアからのものです)。 JavaScriptインタープリターを使用して、このツリーのノードにアクセスできます。 ブラウザーを開いて、同じyandex.ruに移動しましょう(これらがhabraeffectで覆われないことを願っています)。 検索バーの上にいくつのリンクが表示されますか?
リンクのリストをクリックして、開発者のメニューで開きます(Chromeでは、これはコンテキストリストの「チェック項目」です)。 そのため、ツリー内の現在の要素の位置が表示されます。 リストは単純なid = "tabs"を持ち、テーブルです。 JavaScriptコンソールに切り替えて、次の表を選択してください。
document.getElementById("tabs").
その中の行数を見てください。
document.getElementById("tabs").rows.length
そして、何列:
document.getElementById("tabs").rows(0).cells.length.
ブラウザで同じ結果が得られます。

# -*- coding: utf-8 -*-
from basebrowser import *

class SimpleJavaScript(BaseBrowser):
def __init__(self, parent = None):
super(SimpleJavaScript, self).__init__(parent)
self.jsButton = QPushButton( "ExecuteJS" )
self.connect(self.jsButton, SIGNAL( "clicked()" ), self.jsScript)
self.jsStringEdit = QLineEdit()
self.jsStringEdit.setSizePolicy(QSizePolicy.Expanding, self.jsStringEdit.sizePolicy().verticalPolicy())
self.jsStringEdit.setText( "document.getElementById(\"tabs\").rows(0).cells.length" )
self.connect(self.jsStringEdit, SIGNAL( "returnPressed()" ), self.jsScript)
self.jsReturnText = QTextEdit()
self.layout.addWidget(self.jsStringEdit, 2, 0, 1, 1)
self.layout.addWidget(self.jsButton, 2, 1, 1, 1)
self.layout.addWidget(self.jsReturnText, 3, 0, 1, 2)

def jsScript(self):
jsString = self.jsStringEdit.text()
jsReturn = self.webView.page().currentFrame().evaluateJavaScript(jsString)
self.jsReturnText.setPlainText(jsReturn.toString())

if __name__ == "__main__" :
import sys
app = QApplication(sys.argv)
ui = SimpleJavaScript()
ui.show()
sys.exit(app.exec_())


* This source code was highlighted with Source Code Highlighter .


サンプルコード2: pastebin.com/p4P1ZEtS
そのため、JSコードは関数webView.page()。CurrentFrame()。EvaluateJavaScript(jsString)で計算されます
evaluateJavaScript(文字列)関数は、JavaScriptコードを含むQString文字列を唯一の引数として受け取ります。 このコードは現在のページで実行され、結果はQVariant変数として返されます。 同時に、残念ながら、結果としてDOM要素のサブツリーを取得することはできませんが、テキストまたは数値情報を取得してください。

例3.オフラインコントロールの作成



私はATIカードを持っているので、今回はホームページのアドレスをこのように選択しました。私はLinuxを知っているので、彼はこれが大した愛ではないことを理解しています。 実際、Selectのようなページには多くのコントロールがあり、そのうちの1つに対して同等のものを作成します。

# -*- coding: utf-8 -*-
from basebrowser import *
from PyQt4.QtGui import *
from PyQt4.QtCore import *

class JSSelectList(QAbstractListModel):
def __init__ (self, _id, _jsFunc, parent = None):
super(JSSelectList, self).__init__(parent)
self.id = _id
self.jsFunc = _jsFunc

def data(self, index, role=Qt.DisplayRole):
if not index.isValid():
return QVariant()
if role == Qt.DisplayRole:
jsstring = QString( "document.getElementById('%1').options[%2].textContent" ).arg(self.id).arg(index.row())
jsreturn = self.jsFunc(jsstring)
return jsreturn.toString().trimmed()

def rowCount(self, index=QModelIndex()):
jsstring = QString( "document.getElementById('%1').length" ).arg(self.id)
jsreturn = self.jsFunc(jsstring)
ok = False
count, ok = jsreturn.toInt()
return count if ok else 0

def headerData(self, section, orientation, role=Qt.DisplayRole):
if role != Qt.DisplayRole:
return QVariant()
else :
return self.id

class JSComboBoxDemo(BaseBrowser):
def __init__(self, parent = None):
super(JSComboBoxDemo, self).__init__(parent)
self.vendorComboBox = QComboBox()
id = QString( "productLine" )
self.vendorListModel = JSSelectList(id, self.webView.page().currentFrame().evaluateJavaScript)
self.vendorComboBox.setModel(self.vendorListModel)
self.connect(self.vendorComboBox, SIGNAL( "currentIndexChanged(int)" ), self.setSelectOnWebPage);
self.connect(self.webView, SIGNAL( "loadFinished(bool)" ), self.initComboBox)
self.layout.addWidget(self.vendorComboBox, 2, 0, 1, 1)
self.webView.load(QUrl( "http://www.amd.com" ))

def setSelectOnWebPage(self, new_id):
jsstring = QString( "document.getElementById('productLine').selectedIndex=%1" ).arg(new_id)
self.webView.page().currentFrame().evaluateJavaScript(jsstring)

def initComboBox(self):
self.vendorComboBox.setCurrentIndex(0)

if __name__ == "__main__" :
import sys
app = QApplication(sys.argv)
ui = JSComboBoxDemo()
ui.show()
sys.exit(app.exec_())


* This source code was highlighted with Source Code Highlighter .


サンプルコード3: pastebin.com/YzA9hL3H

テーブル、リスト、ドロップダウンなどのGUI要素を作成する場合(正しく翻訳する方法がわかりません)、Qtでは便利なMVCアプローチを使用できます。 データモデルへのアクセスを記述するだけです。組み込みの抽象クラスからデータ表現を継承し、標準コントロールにアタッチするだけです(Summerfieldでは、これは第14章のようです)。 この場合、QAbstractListModelが使用されます。パラメーターからは、JS実行関数とページ上の選択名のみが渡されます。 すべての再定義標準。

例自体では、2つの信号スロット接続を除いて、すべてが非常に明確です。注意してください。

まず、ページがロードされる前にJavaScriptを実行しようとしても意味がありません。したがって、ダウンロードが完了すると、QWebViewウィジェットがSIGNALシグナル(「loadFinished(bool)」)を生成するという事実を利用します。

self.connect(self.webView, SIGNAL("loadFinished(bool)"), self.initComboBox)

それ以外の場合、あなたがラインをプッシュした場合

self.vendorComboBox.setCurrentIndex(0)

__init__では、最初の値で初期化は行われません-ページがまだロードできていないため、evaluateJavaScriptは何も返しません。
次に、両方向の同期が必要です。

self.connect(self.vendorComboBox, SIGNAL("currentIndexChanged(int)"), self.setSelectOnWebPage)

同様に、ページ上のほとんどすべての情報を同期し、ボタンをクリックして、情報をダウンロードできます。

情報が誰かに役立つなら、私はうれしいです。 すべてのメリークリスマスと新年あけましておめでとうございます。

使用された文献:

J.ブランシェット、M。サマーフィールド。 Qt 4:C ++ GUIプログラミング。
マーク・サマーフィールド。 PythonとQtを使用した高速GUIプログラミング。
その他のソース:
JavaScriptおよびPyQt(Aroraインターネットブラウザーのソースコード)を使用するさまざまなインターネットサイト。

Source: https://habr.com/ru/post/J111418/


All Articles