LoginSignup
2
0

More than 3 years have passed since last update.

Socketを使ったHTTPサーバーとHTTPクライアント(+Webブラウザー) - Python3

Last updated at Posted at 2020-11-11

Untitled.jpg

Socketモジュールを使ったHTTP通信

python3のsocketモジュールを使った、HTTP通信の簡単な例

HTTPは簡単に言うと、HTMLファイルをTCP通信(TCPとは限らない)で送ったりするために、本来はどんなデータでも良いTCPのデータの部分(TCPペイロードとか言う)に対してルール(データはこのフォーマットに従ってくださいという)を作ったわけだから、recv,sendでそのフォーマットに従ったデータを送れば良い。

ここでは、RFC7230,RFC7230日本語訳を読みながら進める。

TCPandHTTP.jpg

http通信.jpg

※HTTP通信とは「HTTP」と呼ばれるお約束事に従って行われる通信のこと。だそうです。

スタートライン(start-line)とヘッダ節(header-section)

スタートラインについて start-line

HTTPメッセージはクライアントからサーバーへのリクエストとサーバーからクライアントへのレスポンスのいずれかになる。このスタートラインでこの2つを判別することができる。スタートラインは前者の場合リクエストライン、後者の場合ステータスラインというフォーマットを取る。

ヘッダ節について header-section

今回は次のヘッダフィールドを使う。

Serverサイド

  1. Server
  2. Connection
  3. Content-Type
  4. Content-Length

Clientサイド

  1. Accept
  2. Host
  3. Connection
  4. User-Agent

実際のところ今回はどのヘッダフィールドの値も使いませんでした。

ここまでの実装

ここではコードやヘッダ節の値を固定しているが、実際には知っての通り動的に決められます。

クライアント側のリクエストライン+ヘッダ節

RETURN_PHRASE = '\r\n'
VERSION = 'HTTP/1.1'


def make_request_line(mtd, request_target):
    '''Return http_request-line'''

    request_line = \
        mtd + ' ' + request_target + ' ' + VERSION + RETURN_PHRASE
    return request_line


def make_header_section():
    '''Return http_header-section'''

    field_values = {
        'Host': '192.168.1.200',
        'Accept': 'html/plain',
        'Connection': 'close',
        'User-Agent': 'Suzukaze Browser 1.0'
    }
    header_sc = 'Host:' + field_values['Host'] + RETURN_PHRASE
    header_sc += 'Accept:' + field_values['Accept'] + RETURN_PHRASE
    header_sc += 'Connection:' + field_values['Connection'] + RETURN_PHRASE
    header_sc += 'User-Agent:' + field_values['User-Agent'] + RETURN_PHRASE
    header_sc += RETURN_PHRASE
    return header_sc


mtd = 'GET'
request_target = '/'
request_line = make_request_line(mtd, request_target)

header_section = make_header_section()

print(header_section)

関数make_request_line(メソッド, リクエストターゲット) -> リクエストラインは次を返す。

GET / HTTP/1.1

関数make_header_section() -> ヘッダ節は次を返す。

Host:192.168.1.200
Accept:html/plain
Connection:close
User-Agent:Suzukaze Browser 1.0
空行

サーバー側のステータスライン+ヘッダ節

コードは200固定。だからreason_phrasesは余計だが雰囲気のために追加。

RETURN_PHRASE = '\r\n'
VERSION = 'HTTP/1.1'


def make_status_line():
    '''Return http_staus_line'''

    reason_phrases = {
        '200': 'OK',
        '403': 'Forbidden',
        '404': 'Not Found',
        '405': 'Method Not Allowed'
    }
    status_line = \
        VERSION + ' ' + '200' + ' ' + reason_phrases['200'] + RETURN_PHRASE
    return status_line


def make_header_section(content_length):
    '''Return http_header_section'''

    field_values = {
        'Server': 'Suzukaze Server 1.0',
        'Date': datetime.now(timezone.utc).strftime(r'%a, %d %b %Y %X %Z'),
        'Connection': 'close',
        'Content-Type': 'html/plain',
        'Content-Length': content_length
    }
    header_sc = 'Date:' + field_values['Date'] + RETURN_PHRASE
    header_sc += 'Server:' + field_values['Server'] + RETURN_PHRASE
    header_sc += 'Connection' + field_values['Connection'] + RETURN_PHRASE
    header_sc += 'Content-Type' + field_values['Content-Type'] + RETURN_PHRASE
    header_sc += 'Content-Length' + field_values['Cotent-Length'] + RETURN_PHRASE
    header_sc += RETURN_PHRASE
    return header_sc


def read_index_html():
    '''Return index.html and that length'''

    with open('index.html', 'rb') as f:
        index_html = f.read()
        index_len = len(index_html)
        return index_html, index_len


status_line = make_status_line()

index_html, index_len = read_index_html()
content_length = index_len

header_section = make_header_section(content_length)
関数make_status_line() -> ステータスラインは次を返す。
HTTP/1.1 200 OK
関数make_header_section(Content-Lengthの値) -> ヘッダ節は次を返す。
Date:Mon, 00 Nov 2020 00:00:00 UTC
Server:Suzukaze Server 1.0
Connection:close
Content-Type:html/plain
Content-Length:1224
空行
関数read_index_html() -> index.htmlの中身, index.htmlの長さは次を返す。
b"<!DOCTYPE html>\n<head>\n    <meta charset='utf-8'>\n</head>\n<style>\n* 
(略)
ab\xe3\x81\xa4\xe3\x81\x84\xe3\x81\xa6</li>\n                <li>\xe5\x95\x8f\xe3\x81\x84\xe5\x90\x88\xe3\x82\x8f\xe3\x81\x9b</li>\n            </ul>\n        </footer>\n    </div>\n</body>\n"
1224

サーバー側がリクエストから取り出す必要がある情報

今回のコードではリクエストされるのが'/'であることがわかっているので、request-targetは必要ないし、ConnectionもCloseするから結局のところサーバーが必要な情報はありません。だからリクエストラインとヘッダ節はスルーします。

メッセージボディ(message-body)

サーバー側のメッセージボディを実装すると言ってもすでにindex.htmlをバイト列として読み込んだのでサーバー側のヘッダ節の後ろにくっつけるだけである。クライアント側のメッセージボディは無い。

ここで注意しないといけないのがスタートラインとヘッダ節がUnicode文字列(str型)であることである。ソケットで送信するにはバイト列(byte-like-object)にする必要があるので変換する。

クライアント側

mtd = 'GET'
request_target = '/'
request_line = make_request_line(mtd, request_target)

header_section = make_header_section()

http_msg = \
    bytes(request_line, 'utf-8') + bytes(request_line, 'utf-8')

サーバー側

status_line = make_status_line()

index_html, index_len = read_index_html()
msg_body = index_html
content_length = str(index_len)

header_section = make_header_section(content_length)

http_msg = \
    bytes(status_line, 'utf-8') + bytes(header_section, 'utf-8') + msg_body

ソケット通信の実装

後はこのHTTPメッセージを送り合うだけです。

ここのコードは、Socketモジュールを使ったTCP通信 - Python3をベースに作ります。

サーバー側

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(SERVER_ADDRESS)
sock.listen(0)

try:
    conn_sock, client_address = sock.accept()
    print_msg('ac', 'The connection accepted.')
    print_msg('i', '{}:{} --------> {}:{}'
              .format(client_address[0], client_address[1],
                      SERVER_ADDRESS[0], SERVER_ADDRESS[1]))

    # Receiving the request
    recd_data = b''
    while True:
        data = conn_sock.recv(32)
        if not data:
            break
        recd_data += data
    print_msg('i', 'Total received: {}'.format(recd_data))

    # Sending the response
    http_msg = make_http_msg()
    remaining = len(http_msg)
    res = 0
    while remaining > 0:
        res = conn_sock.send(http_msg[res:])
        remaining -= res
finally:
    conn_sock.shutdown(socket.SHUT_WR)
    conn_sock.close()
    print_msg('cl', 'The connection closed.')

クライアント側

リクエストを送り終えたらshutdownすることでサーバー側にこれ以上こちらはデータを送らないと宣言できる。(ソケットレベルでは空バイトが送られる。だからサーバー側は空バイトの受信でリクエストの終端を検出できる。)

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

try:
    sock.connect(SERVER_ADDRESS)
    print_msg('cn', 'The connection accepted')

    # Sending the request
    http_msg = make_http_msg()
    remaining = len(make_http_msg())
    res = 0
    while remaining > 0:
        res = sock.send(http_msg[res:])
        remaining -= res

    sock.shutdown(socket.SHUT_WR)

    # Receiving the response
    recd_data = b''
    while True:
        data = sock.recv(32)
        print(data)
        if not data:
            break
        recd_data += data
finally:
    sock.close()
    print_msg('cl', 'The connection closed.')

データを取り出す

クライアント側

とりあえずクライアント側は次の2つを取り出す必要がある。

  1. ステータスラインの情報。これは次の正規表現で辞書として取り出す。
   STS_LINE_PTN = b'''
   (?P<version>HTTP/[0-9][.][0-9])[ ]
   (?P<status_code>[0-9][0-9][0-9])[ ]
   (?P<reason>([!\"#$%&\'()*+,-./0-9:;<=>?@A-Z[\\]^_`a-z{|}~ ]|\t)+)
   ([\r][\n])
   '''

※コンパイル時にre.VERBOSEを指定する必要がある。そうすることで文字クラス[]内にない空白は無視される。

次のように取り出される。

   {'version': b'HTTP/1.1', 'status_code': b'200', 'reason': b'OK'}
  1. htmlのコードを含んだメッセージボディ。ヘッダ節の終わりは空行で表すルールなので、b'\r\n\r\n'と一致する部分の最後のインデックスを取得して、そのインデックスを[start:end]のstartにして新しい(メッセージボディだけの)バイト列を生成する。そしてそれをデコードすると元のhtmlのコードが得られます。つまりサーバーサイドの下記文字列(html_source)と等しい。
   with open('sample.html', 'r') as f:
    html_source = f.read()

サーバー側

今の所サーバー側が必要な情報は無い。

得たhtmlをブラウザもどきで表示させる

ここで簡単なhtmlビュワーをPyqt5をつかって作る。UIはデザイナーを使って作り、それをPyQt5 UI code generator 5.15.1でPython3コードに変換してそのコードをいじって使う。本来はUIを書き換えても問題ないようにユーザーが追加するロジックは別に作ったクラスに書いていくが今回はUIを書き換えないので、そのまま設定していく。ブラウザは次のようなコードとなった。このモジュールをソケット通信の行うメインのモジュールでインポートする。

browser_app.py
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWebEngineWidgets import QWebEngineView as QWebView


class Ui_dialog(object):
    def setupUi(self, dialog, html_source):
        dialog.setObjectName('dialog')
        dialog.resize(1000, 650)
        self.stackedWidget = QtWidgets.QStackedWidget(dialog)
        self.stackedWidget.setGeometry(QtCore.QRect(10, 50, 980, 590))
        self.stackedWidget.setObjectName('stackedWidget')
        self.page_1 = QtWidgets.QWidget()
        self.page_1.setObjectName('page_1')
        self.webView = QWebView(self.page_1)
        self.webView.setGeometry(QtCore.QRect(0, 0, 980, 590))
        self.webView.setObjectName('webView')
        self.stackedWidget.addWidget(self.page_1)
        self.page_2 = QtWidgets.QWidget()
        self.page_2.setObjectName('page_2')
        self.page_2_title = QtWidgets.QLabel(self.page_2)
        self.page_2_title.setGeometry(QtCore.QRect(370, 60, 240, 50))
        font = QtGui.QFont()
        font.setPointSize(24)
        self.page_2_title.setFont(font)
        self.page_2_title.setObjectName('page_2_title')
        self.page_2_label1 = QtWidgets.QLabel(self.page_2)
        self.page_2_label1.setGeometry(QtCore.QRect(435, 230, 110, 22))
        font = QtGui.QFont()
        font.setPointSize(16)
        self.page_2_label1.setFont(font)
        self.page_2_label1.setObjectName('page_2_label1')
        self.page_2_ip = QtWidgets.QLineEdit(self.page_2)
        self.page_2_ip.setGeometry(QtCore.QRect(435, 262, 110, 28))
        font = QtGui.QFont()
        font.setPointSize(12)
        self.page_2_ip.setFont(font)
        self.page_2_ip.setObjectName('page_2_ip')
        self.page_2_label2 = QtWidgets.QLabel(self.page_2)
        self.page_2_label2.setGeometry(QtCore.QRect(468, 310, 42, 16))
        font = QtGui.QFont()
        font.setPointSize(16)
        self.page_2_label2.setFont(font)
        self.page_2_label2.setObjectName('page_2_label2')
        self.page_2_url = QtWidgets.QLineEdit(self.page_2)
        self.page_2_url.setGeometry(QtCore.QRect(335, 336, 310, 28))
        font = QtGui.QFont()
        font.setPointSize(12)
        self.page_2_url.setFont(font)
        self.page_2_url.setObjectName('page_2_url')
        self.page_2_save = QtWidgets.QPushButton(self.page_2)
        self.page_2_save.setGeometry(QtCore.QRect(425, 384, 130, 40))
        font = QtGui.QFont()
        font.setPointSize(18)
        self.page_2_save.setFont(font)
        self.page_2_save.setObjectName('page_2_save')
        self.stackedWidget.addWidget(self.page_2)
        self.url_lineEdit = QtWidgets.QLineEdit(dialog)
        self.url_lineEdit.setGeometry(QtCore.QRect(10, 10, 666, 30))
        font = QtGui.QFont()
        font.setPointSize(12)
        self.url_lineEdit.setFont(font)
        self.url_lineEdit.setObjectName('url_lineEdit')
        self.search_btn = QtWidgets.QPushButton(dialog)
        self.search_btn.setGeometry(QtCore.QRect(670, 10, 90, 30))
        font = QtGui.QFont()
        font.setPointSize(12)
        self.search_btn.setFont(font)
        self.search_btn.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
        self.search_btn.setObjectName('search_btn')
        self.config_btn = QtWidgets.QPushButton(dialog)
        self.config_btn.setGeometry(QtCore.QRect(780, 10, 60, 30))
        font = QtGui.QFont()
        font.setPointSize(10)
        self.config_btn.setFont(font)
        self.config_btn.setObjectName('config_btn')

        # ----------------------------------------------

        self.url_lineEdit.setText('準備中')
        self.page_2_title.setText('Setting the DNS')
        self.page_2_label1.setText('IP Address')
        self.page_2_ip.setText('準備中')
        self.page_2_label2.setText('URL')
        self.page_2_url.setText('準備中')

        self.webView.setHtml(html_source)

        # ----------------------------------------------

        QtCore.QMetaObject.connectSlotsByName(dialog)


def mybrowser(html_source):
    app = QtWidgets.QApplication(sys.argv)
    dialog = QtWidgets.QDialog()
    ui = Ui_dialog()
    ui.setupUi(dialog, html_source)
    dialog.show()
    sys.exit(app.exec_())

これを同じディレクトリに置いて、クライアント側でインポートして次のように呼び出す。

status_dict, res_msg = status_line_parser(recd_data)

if status_dict['status_code'].decode() == '200':
    html_lines = get_msg_body(res_msg)
    html_lines = html_lines.decode()
    mybrowser(html_lines)

そうすると、

Untitled.jpg

このように表示される。

ソースコード

client.py

client.py
import socket
import re
from colored_print import print_msg
from browser_app import mybrowser


# Request


SERVER_ADDRESS = ('192.168.1.201', 8000)
RETURN_PHRASE = '\r\n'
VERSION = 'HTTP/1.1'


def make_request_line(mtd, request_target):
    '''Return http_request-line'''

    request_line = \
        mtd + ' ' + request_target + ' ' + VERSION + RETURN_PHRASE
    return request_line


def make_header_section():
    '''Return http_header-section'''

    field_values = {
        'Host': '192.168.1.200',
        'Accept': 'html/plain',
        'Connection': 'close',
        'User-Agent': 'Suzukaze-Browser-1.0'
    }
    header_sc = 'Host:' + field_values['Host'] + RETURN_PHRASE
    header_sc += 'Accept:' + field_values['Accept'] + RETURN_PHRASE
    header_sc += 'Connection:' + field_values['Connection'] + RETURN_PHRASE
    header_sc += 'User-Agent:' + field_values['User-Agent'] + RETURN_PHRASE
    header_sc += RETURN_PHRASE
    return header_sc


def make_http_msg():
    '''Return http-message'''

    mtd = 'GET'
    request_target = '/'
    request_line = make_request_line(mtd, request_target)

    header_section = make_header_section()

    http_msg = \
        bytes(request_line, 'utf-8') + bytes(header_section, 'utf-8')
    return http_msg


# Response


def status_line_parser(msg_lines):
    '''Return dict of status line and http_msg except status line'''

    STS_LINE_PTN = b'''
    (?P<version>HTTP/[0-9][.][0-9])[ ]
    (?P<status_code>[0-9][0-9][0-9])[ ]
    (?P<reason>([!\"#$%&\'()*+,-./0-9:;<=>?@A-Z[\\]^_`a-z{|}~ ]|\t)+)
    ([\r][\n])
    '''
    p_sts_line = re.compile(STS_LINE_PTN, re.VERBOSE)

    m_sts_line = p_sts_line.match(msg_lines)
    if m_sts_line is not None:
        sts_line_dict = m_sts_line.groupdict()
        return sts_line_dict, msg_lines[m_sts_line.end():]


def get_msg_body(msg_lines):
    '''Return a message body'''

    end = msg_lines.rfind(b'\r\n\r\n')
    print(end)
    if end is not -1:
        return msg_lines[end:]
    else:
        raise ValueError('msg_lines is a invalid format')


########################################################################


sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

try:
    sock.connect(SERVER_ADDRESS)
    print_msg('cn', 'The connection accepted')

    # Sending the request
    http_msg = make_http_msg()
    remaining = len(make_http_msg())
    res = 0
    while remaining > 0:
        res = sock.send(http_msg[res:])
        remaining -= res

    sock.shutdown(socket.SHUT_WR)

    # Receiving the response
    recd_data = b''
    while True:
        data = sock.recv(32)
        print(data)
        if not data:
            break
        recd_data += data
finally:
    sock.close()
    print_msg('cl', 'The connection closed.')


status_dict, res_msg = status_line_parser(recd_data)

if status_dict['status_code'].decode() == '200':
    html_lines = get_msg_body(res_msg)
    html_lines = html_lines.decode()
    mybrowser(html_lines)

server.py

server.py
import socket
from datetime import datetime, timezone
from colored_print import print_msg


# Response


SERVER_ADDRESS = ('192.168.1.12', 8000)
RETURN_PHRASE = '\r\n'
VERSION = 'HTTP/1.1'


def make_status_line():
    '''Return http_staus-line'''

    reason_phrases = {
        '200': 'OK',
        '403': 'Forbidden',
        '404': 'Not Found',
        '405': 'Method Not Allowed'
    }
    status_line = \
        VERSION + ' ' + '200' + ' ' + reason_phrases['200'] + RETURN_PHRASE
    return status_line


def make_header_section(content_length):
    '''Return http_header-section'''

    field_values = {
        'Server': 'Suzukaze Server 1.0',
        'Date': datetime.now(timezone.utc).strftime(r'%a, %d %b %Y %X %Z'),
        'Connection': 'close',
        'Content-Type': 'html/plain',
        'Content-Length': content_length
    }
    header_sc = 'Date:' + field_values['Date'] + RETURN_PHRASE
    header_sc += 'Server:' + field_values['Server'] + RETURN_PHRASE
    header_sc += 'Connection:' + field_values['Connection'] + RETURN_PHRASE
    header_sc += 'Content-Type:' + field_values['Content-Type'] + RETURN_PHRASE
    header_sc += 'Content-Length:' + field_values['Content-Length'] + RETURN_PHRASE
    header_sc += RETURN_PHRASE
    return header_sc


def read_index_html():
    '''Return index.html and that length'''

    with open('index.html', 'rb') as f:
        index_html = f.read()
        index_len = len(index_html)
        return index_html, index_len


def make_http_msg():
    status_line = make_status_line()

    index_html, index_len = read_index_html()
    msg_body = index_html
    content_length = str(index_len)

    header_section = make_header_section(content_length)

    http_msg = \
        bytes(status_line, 'utf-8') + bytes(header_section, 'utf-8') + msg_body
    return http_msg


###############################################################


sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(SERVER_ADDRESS)
sock.listen(0)

try:
    conn_sock, client_address = sock.accept()
    print_msg('ac', 'The connection accepted.')
    print_msg('i', '{}:{} --------> {}:{}'
              .format(client_address[0], client_address[1],
                      SERVER_ADDRESS[0], SERVER_ADDRESS[1]))

    # Receiving the request
    recd_data = b''
    while True:
        data = conn_sock.recv(32)
        if not data:
            break
        recd_data += data
    print_msg('i', 'Total received: {}'.format(recd_data))

    # Sending the response
    http_msg = make_http_msg()
    remaining = len(http_msg)
    res = 0
    while remaining > 0:
        res = conn_sock.send(http_msg[res:])
        remaining -= res
finally:
    conn_sock.shutdown(socket.SHUT_WR)
    conn_sock.close()
    print_msg('cl', 'The connection closed.')

colored_print.py

colored_print.py
from colorama import Fore, Style


def print_msg(header, msg):
    '''header are i that is INFO or e that is ERROR'''

    if header == 'i':
        print(Fore.GREEN + '[INFO]',
              Style.RESET_ALL + msg)
    elif header == 'e':
        print(Fore.RED + '[ERROR]',
              Style.RESET_ALL + msg)
    elif header == 'ac':
        print(Fore.BLUE + '[ACCEPT]',
              Style.RESET_ALL + msg)
    elif header == 'cn':
        print(Fore.BLUE + '[CONNECT]',
              Style.RESET_ALL + msg)
    elif header == 'cl':
        print(Fore.BLUE + '[CLOSE]',
              Style.RESET_ALL + msg)
    else:
        print(Fore.RED + 'ERROR: header is an invalid value.'
              + Style.RESET_ALL)

browser_app.py

browser_app.py
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWebEngineWidgets import QWebEngineView as QWebView


class Ui_dialog(object):
    def setupUi(self, dialog, html_source):
        dialog.setObjectName('dialog')
        dialog.resize(1000, 650)
        self.stackedWidget = QtWidgets.QStackedWidget(dialog)
        self.stackedWidget.setGeometry(QtCore.QRect(10, 50, 980, 590))
        self.stackedWidget.setObjectName('stackedWidget')
        self.page_1 = QtWidgets.QWidget()
        self.page_1.setObjectName('page_1')
        self.webView = QWebView(self.page_1)
        self.webView.setGeometry(QtCore.QRect(0, 0, 980, 590))
        self.webView.setObjectName('webView')
        self.stackedWidget.addWidget(self.page_1)
        self.page_2 = QtWidgets.QWidget()
        self.page_2.setObjectName('page_2')
        self.page_2_title = QtWidgets.QLabel(self.page_2)
        self.page_2_title.setGeometry(QtCore.QRect(370, 60, 240, 50))
        font = QtGui.QFont()
        font.setPointSize(24)
        self.page_2_title.setFont(font)
        self.page_2_title.setObjectName('page_2_title')
        self.page_2_label1 = QtWidgets.QLabel(self.page_2)
        self.page_2_label1.setGeometry(QtCore.QRect(435, 230, 110, 22))
        font = QtGui.QFont()
        font.setPointSize(16)
        self.page_2_label1.setFont(font)
        self.page_2_label1.setObjectName('page_2_label1')
        self.page_2_ip = QtWidgets.QLineEdit(self.page_2)
        self.page_2_ip.setGeometry(QtCore.QRect(435, 262, 110, 28))
        font = QtGui.QFont()
        font.setPointSize(12)
        self.page_2_ip.setFont(font)
        self.page_2_ip.setObjectName('page_2_ip')
        self.page_2_label2 = QtWidgets.QLabel(self.page_2)
        self.page_2_label2.setGeometry(QtCore.QRect(468, 310, 42, 16))
        font = QtGui.QFont()
        font.setPointSize(16)
        self.page_2_label2.setFont(font)
        self.page_2_label2.setObjectName('page_2_label2')
        self.page_2_url = QtWidgets.QLineEdit(self.page_2)
        self.page_2_url.setGeometry(QtCore.QRect(335, 336, 310, 28))
        font = QtGui.QFont()
        font.setPointSize(12)
        self.page_2_url.setFont(font)
        self.page_2_url.setObjectName('page_2_url')
        self.page_2_save = QtWidgets.QPushButton(self.page_2)
        self.page_2_save.setGeometry(QtCore.QRect(425, 384, 130, 40))
        font = QtGui.QFont()
        font.setPointSize(18)
        self.page_2_save.setFont(font)
        self.page_2_save.setObjectName('page_2_save')
        self.stackedWidget.addWidget(self.page_2)
        self.url_lineEdit = QtWidgets.QLineEdit(dialog)
        self.url_lineEdit.setGeometry(QtCore.QRect(10, 10, 666, 30))
        font = QtGui.QFont()
        font.setPointSize(12)
        self.url_lineEdit.setFont(font)
        self.url_lineEdit.setObjectName('url_lineEdit')
        self.search_btn = QtWidgets.QPushButton(dialog)
        self.search_btn.setGeometry(QtCore.QRect(670, 10, 90, 30))
        font = QtGui.QFont()
        font.setPointSize(12)
        self.search_btn.setFont(font)
        self.search_btn.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
        self.search_btn.setObjectName('search_btn')
        self.config_btn = QtWidgets.QPushButton(dialog)
        self.config_btn.setGeometry(QtCore.QRect(780, 10, 60, 30))
        font = QtGui.QFont()
        font.setPointSize(10)
        self.config_btn.setFont(font)
        self.config_btn.setObjectName('config_btn')

        # ----------------------------------------------

        self.url_lineEdit.setText('準備中')
        self.page_2_title.setText('Setting the DNS')
        self.page_2_label1.setText('IP Address')
        self.page_2_ip.setText('準備中')
        self.page_2_label2.setText('URL')
        self.page_2_url.setText('準備中')

        self.webView.setHtml(html_source)

        # ----------------------------------------------

        QtCore.QMetaObject.connectSlotsByName(dialog)


def mybrowser(html_source):
    app = QtWidgets.QApplication(sys.argv)
    dialog = QtWidgets.QDialog()
    ui = Ui_dialog()
    ui.setupUi(dialog, html_source)
    dialog.show()
    sys.exit(app.exec_())
2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0