国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

python實現(xiàn)Android實時投屏操控

這篇具有很好參考價值的文章主要介紹了python實現(xiàn)Android實時投屏操控。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

scrcpy-client

? ? ? ? python中有一個scrcpy-client庫,可以實現(xiàn)Android設備的實時投屏和操控。它和scrcpy實現(xiàn)Android投屏是一樣的,都是把一個scrcpy-server.jar文件通過adb推送到Android設備,并利用adb指令執(zhí)行scrcpy-server.jar開啟投屏和操控服務端,電腦端通過python創(chuàng)建客戶端來接收視頻流數(shù)據(jù)和發(fā)送控制流數(shù)據(jù)。視頻流數(shù)據(jù)中就是Android實時屏幕數(shù)據(jù),控制流數(shù)據(jù)就是我們在電腦端對Android設備做的操控動作。在scrcpy-client庫中作者提供了一個使用PySide6搭建的投屏控制UI界面,可以完成單臺Android設備的投屏控制,我們可以自行制作投屏控制界面,完成多臺Android設備的投屏控制。

安裝指令:pip3 install?scrcpy-client

直接使用

? ? ? ? 安裝好scrcpy-client庫后,我們可以通過直接使用作者提供的ui界面來投屏Android設備。

import scrcpy_ui


scrcpy_ui.main()

確保我們的電腦上通過USB連接了一臺Android設備,并且Android設備打開了USB調試功能,已允許電腦調式。這時我們就可以通過執(zhí)行上面的代碼,得到Android設備的投屏UI界面,如下圖所示:

scrcpy投屏python,Android自動化測試,android,python,測試工具

在這個界面中我們可以使用鼠標在投屏界面點擊、滑動,控制Android設備的屏幕??梢酝ㄟ^設備序列號下拉框切換設備,F(xiàn)lip勾選后可以得到鏡像屏幕。下方的HOME按鈕點擊后回到主屏幕,相當于按設備的home鍵。BACK按鈕點擊后會返回上一個界面,相當于按設備上的back鍵。還支持鍵盤輸入,我們可以通過電腦的鍵盤讓Android產(chǎn)生按鍵事件。

自定義使用

? ? ? ? 如果你覺得作者提供的UI界面不能滿足你的需求,我們還可以自定義UI界面來實現(xiàn)更多的操作方式。前提是你要會使用PySide6這種UI框架,你必須知道如何在UI界面中使用動態(tài)元素,如何實現(xiàn)鼠標點擊、移動事件,鼠標滾輪滾動事件,鍵盤輸入事件。如果你還不會使用UI界面相關的框架,可以先去學習一下。如果你會UI相關的框架,就接著往下看。

創(chuàng)建投屏服務

? ? ? ? 使用scrcpy中的Client類建立投屏控制服務,Client類中的實例化方法如下:

class Client:
    def __init__(
        self,
        device: Optional[Union[AdbDevice, str, any]] = None,
        max_width: int = 0,
        bitrate: int = 8000000,
        max_fps: int = 0,
        flip: bool = False,
        block_frame: bool = False,
        stay_awake: bool = False,
        lock_screen_orientation: int = LOCK_SCREEN_ORIENTATION_UNLOCKED,
        connection_timeout: int = 3000,
        encoder_name: Optional[str] = None,
    ):

device:Android設備的設備序列號(使用adb devices指令可以查看到)。

max_width:圖像幀的最大寬度,默認使用Android廣播信息中的幀寬度。

bitrate:比特率,默認8000000比特。

max_fps:最大幀數(shù),默認不限制幀數(shù)。

flip:翻轉圖像(鏡像圖像),默認不鏡像。

block_frame:返回非空幀,默認不返回,返回非空幀可能會阻塞openCv2的渲染線程。

stay_awake:連接USB時Android設備屏幕保持常亮,默認不保持常亮。

lock_screen_orientation:鎖定屏幕方向(禁止自動旋轉屏幕),默認不鎖定。

connection_timeout:連接投屏控制服務(socket服務)超時時間,設定時間內未能成功連接則初始化失敗,默認3000毫秒。

encoder_name:編碼器名稱,可選OMX.google.h264.encoder、OMX.qcom.video.encoder.avc、 c2.qti.avc.encoder、c2.android.avc.encoder,默認自動選擇。

????????我們通過實例化Client類來得到一個Android設備的投屏控制服務對象,假如Android設備的序列號為123456789,我們可以通過如下代碼創(chuàng)建投屏控制服務實例對象:

import scrcpy


server = scrcpy.Client(device='123456789', bitrate=100000000)

start方法

? ? ? ? start是Client的實例方法用于啟動投屏控制服務,start可以接收兩個參數(shù),一個是threaded,默認為False,為True時表示在子線程中開啟投屏控制服務;另一個是daemon_threaded,默認為False,為True時表示給子線程開啟進程守護。threaded和daemon_threaded有一個為True時,都會在子線程中開啟投屏控制服務,都為False時表示在主線程中開啟投屏控制服務。要同時開啟多個投屏控制服務時,就需要在子線程中開啟投屏控制服務,自己創(chuàng)建子線程也是可以的。

server.start()

add_listener方法

????????add_listener是Client的實例方法用于設置監(jiān)聽字典listeners,listeners字典中有兩個元素,第一個元素的鍵為frame表示圖像幀元素,第二個元素為init表示初始化元素。兩個元素的值都是一個空列表,用來存放函數(shù)的。如果想把Android設備的屏幕圖像放到UI界面的某個元素上,就需要在UI框架中寫一個能接收圖像、顯示圖像的方法,再把這個方法添加到listeners字典的第一個元素列表中。如果想在建立投屏控制服務時做一些操作,就在UI框架中寫一個操作相關的方法,在把這個方法放到listeners字典的第二個元素列表中。

    def on_frame(self, frame):  # 在使用PySide6的UI框架中定義了一個用于顯示圖像的方法
        app.processEvents()
        if frame is not None:
            ratio = self.max_width / max(self.client.resolution)
            image = QImage(
                frame,
                frame.shape[1],
                frame.shape[0],
                frame.shape[1] * 3,
                QImage.Format_BGR888,
            )
            pix = QPixmap(image)  # 處理圖像
            pix.setDevicePixelRatio(1 / ratio)  # 設置圖像大小
            self.ui.label.setPixmap(pix)  # 在UI界面顯示圖像
            self.resize(1, 1)

在初始化UI界面時把顯示圖像的方法加入到listener字典的第一個元素中(key='frame')。

    def __init__(self):
        super().__init()
        self.server = scrcpy.Client(device='123456789', bitrate=100000000)
        self.server.add_listener(scrcpy.EVENT_FRAME, self.on_frame)

這里只是舉例,實際上的方法需要根據(jù)你的需求自己寫。如果想要把Android設備的圖像展示在UI界面的某個元素上,就必須寫一個展示圖像的方法,再把這個方法添加到listener字典的第一個元素中(key='frame')。

remove_listener方法

????????remove_listener方法是Client的實例方法用于移除監(jiān)聽字典listeners中的某個方法,如果我們想不再顯示圖像時,可以把顯示圖像的方法從listeners中移除掉。

    def no_display(self):
        self.server.remove_listener(scrcpy.EVENT_FRAME, self.on_frame)

stop方法

????????stop方法是Client的實例方法用于結束投屏控制服務,在我們關閉投屏UI界面時需要結束掉投屏控制服務,及時釋放內存資源。

    def closeEvent(self, _):
        self.server.stop()

控制方法

? ? ? ? 我們已經(jīng)把Android設備的屏幕投射到了電腦上,現(xiàn)在就需要通過一些控制Android設備的方法來操作Android設備。Client的實例屬性中有一個control屬性,是通過實例化ControlSender類來得到的,ControlSender類就是專門用來控制Android設備的操作類。

    self.control = ControlSender(self)

所以我們想要控制Android就需要通過投屏控制對象的control屬性。

keycode方法

    @inject(const.TYPE_INJECT_KEYCODE)
    def keycode(
        self, keycode: int, action: int = const.ACTION_DOWN, repeat: int = 0
    ) -> bytes:

????????keycode方法是ControlSender類的實例方法用于向Android設備發(fā)送按鍵事件。keycode方法可以接收3個參數(shù),第一個參數(shù)keycode表示鍵值(你需要了解adb鍵值);第二個參數(shù)action表示按下還是抬起,默認是按下;第三個參數(shù)repeat表示重復操作次數(shù),想重復按幾次。

    def click_home(self):
        self.server.control.keycode(scrcpy.KEYCODE_HOME, scrcpy.ACTION_DOWN)
        self.server.control.keycode(scrcpy.KEYCODE_HOME, scrcpy.ACTION_UP)

點擊home鍵,先按下再抬起,完成一次按鍵。

text方法

    @inject(const.TYPE_INJECT_TEXT)
    def text(self, text: str) -> bytes:

????????text方法是ControlSender類的實例方法用于向Android中輸入文本,前提是Android設備中的某個輸入框被激活了。text方法接收一個參數(shù),就是我們要在Android設備中輸入的文本內容。

    def input_text(self, text):
        self.server.control.text(text)

touch方法

    def touch(
        self, x: int, y: int, action: int = const.ACTION_DOWN, touch_id: int = -1
    ) -> bytes:

????????touch方法是ControlSender類的實例方法用于Android設備屏幕的多點觸控。touch方法可以接收4個參數(shù),前兩個參數(shù)為觸點的x坐標和y坐標;第三個參數(shù)action為按下、移動、抬起;第四個參數(shù)為觸控事件id,默認為-1,你可以設置不同的id來同時執(zhí)行多個觸控事件,達到多點觸控的目的。

    def mouse_move(self, evt: QMouseEvent):
        focused_widget = QApplication.focusWidget()
        if focused_widget is not None:
            focused_widget.clearFocus()
        ratio = self.max_width / max(self.one_client.resolution)
        self.server.control.touch(evt.position().x() / ratio, evt.position().y() / ratio, scrcpy.ACTION_MOVE)

scroll方法

    @inject(const.TYPE_INJECT_SCROLL_EVENT)
    def scroll(self, x: int, y: int, h: int, v: int) -> bytes:

????????scroll方法是ControlSender類的實例方法用于Android設備屏幕的滾動事件。scroll方法可以接收4個參數(shù),前兩個為滾動點的坐標位置;第三個參數(shù)為水平滾動距離;第四個參數(shù)為垂直滾動距離。

    def on_wheel(self):
        """鼠標滾輪滾動事件"""

        def wheel(evt: QWheelEvent):
            ratio = self.max_width / max(self.one_client.resolution)
            position_x = evt.position().x() / ratio
            position_y = evt.position().y() / ratio
            angle_x = evt.angleDelta().x()
            angle_y = evt.angleDelta().y()
            if angle_y > 0:
                angle_y = 1
            else:
                angle_y = -1
            self.server.control.scroll(position_x, position_y, angle_x, angle_y)

        return wheel

寫出這個方法后,我們就可以使用鼠標滾輪來控制Android設備的屏幕上下滾動了。

back_or_turn_screen_on方法

    @inject(const.TYPE_BACK_OR_SCREEN_ON)
    def back_or_turn_screen_on(self, action: int = const.ACTION_DOWN) -> bytes:

????????back_or_turn_screen_on方法是ControlSender類的實例方法用于按返回鍵,并且如果屏幕關閉了還會喚醒屏幕。只接收一個參數(shù)action為按下或抬起。

    def click_back(self):
        self.server.control.back_or_turn_screen_on(scrcpy.ACTION_DOWN)
        self.server.control.back_or_turn_screen_on(scrcpy.ACTION_UP)

expand_notification_panel方法

    @inject(const.TYPE_EXPAND_NOTIFICATION_PANEL)
    def expand_notification_panel(self) -> bytes:

????????expand_notification_panel方法是ControlSender類的實例方法用于打開Android設備的下拉通知欄。

    def open_notification(self):
        self.server.control.expand_notification_panel()

expand_settings_panel方法

    @inject(const.TYPE_EXPAND_SETTINGS_PANEL)
    def expand_settings_panel(self) -> bytes:

????????expand_settings_panel方法是ControlSender類的實例方法用于打開Android設備的下拉菜單欄。

    def open_settings(self):
        self.server.control.expand_settings_panel()

collapse_panels方法

    @inject(const.TYPE_COLLAPSE_PANELS)
    def collapse_panels(self) -> bytes:

????????collapse_panels方法是ControlSender類的實例方法用于收起Android設備的下拉通知欄或菜單欄。

    def close_panel(self):
        self.server.control.collapse_panelsl()

get_clipboard方法

    def get_clipboard(self) -> str:

????????get_clipboard方法是ControlSender類的實例方法用于獲取Android設備粘貼板中的內容。在Android設備上復制的文本,我們可以通過這個方法把文本獲取出來。

    def get_android_clipboard(self):
        return self.server.control.get_clipboard()

set_clipboard方法

    @inject(const.TYPE_SET_CLIPBOARD)
    def set_clipboard(self, text: str, paste: bool = False) -> bytes:

????????set_clipboard方法是ControlSender類的實例方法用于設置Android設備粘貼板中的內容。set_clipboard方法可以接收兩個參數(shù),第一個參數(shù)text為要設置到粘貼板中的文本內容;第二個參數(shù)paste為粘貼狀態(tài),默認為False,當為True時會立即把文本粘貼到輸入框中(Android設備的光標在某個輸入框中時)。

    def set_android_clipboard(self, text: str, paste=False):
        self.server.control.set_clipboard(text, paste)

set_screen_power_mode方法

    @inject(const.TYPE_SET_SCREEN_POWER_MODE)
    def set_screen_power_mode(self, mode: int = scrcpy.POWER_MODE_NORMAL) -> bytes:

????????set_screen_power_mode方法是ControlSender類的實例方法用于Android設備的屏幕電源模式。默認為正常狀態(tài)表示開啟Android設備的屏幕電源,此時Android設備的屏幕為正常狀態(tài)。還可以設置為關閉狀態(tài)(scrcpy.POWER_MODE_OFF),此時Android設備的屏幕為關閉狀態(tài),但并不是滅屏狀態(tài)(屏幕電源關了和滅屏是兩回事),投屏界面還是能看到屏幕。通過這種方式可以在投屏操控Android設備時減少Android設備的電源消耗。

    def set_screen_power_mode(self, mode=2):
        self.server.control.set_screen_power_mode(mode)

totate_device方法

    @inject(const.TYPE_ROTATE_DEVICE)
    def rotate_device(self) -> bytes:

????????totate_device方法是ControlSender類的實例方法用于旋轉Android設備的屏幕。

    def totate_screen(self):
        self.server.control.totate_device()

swipe方法

    def swipe(
        self,
        start_x: int,
        start_y: int,
        end_x: int,
        end_y: int,
        move_step_length: int = 5,
        move_steps_delay: float = 0.005,
    ) -> None:

? ? ? ? swipe方法是ControlSender類的實例方法用于滑動Android設備的屏幕。這個方法是對touch方法的封裝,相當于一點觸控。swipe方法可以接收6個參數(shù),前4個參數(shù)為滑動的起始坐標和終止坐標;第5個參數(shù)為步長(每次滑動的距離),默認為5個坐標單位;第6個參數(shù)為每滑動一步停頓的時間,默認0.005秒。

    def swipe_event(self, start_x: int, start_y: int, end_x: int, end_y: int, step: int, delay: float):
        self.server.control.swipe(start_x, start_y, end_x, end_y, step, delay)

結語

? ? ? ? 我們通過在python的UI框架中使用上面這些方法,就能實現(xiàn)Android設備的投屏控制了,這個投屏控制的應用要做成什么樣子完全由你自己的需求和審美來決定。如果你想同時操作多臺Android可以創(chuàng)建多個投屏控制服務,然后把這些服務放到一個列表或字典中(最好是字典),來實現(xiàn)控制設備的切換,達到單獨控制某臺設備或同時操作多臺設備的目的。

模型(示例)

? ? ? ? 我看評論區(qū)都想要代碼,這里就為大家提供了一個用于參考的模型??梢酝瑫r操控多臺設備,也可以選擇性的操作某臺設備。文章來源地址http://www.zghlxwxcb.cn/news/detail-775898.html

# -*- coding: utf-8 -*-

import sys
import threading
import scrcpy
from PySide6.QtGui import QMouseEvent, QImage, QPixmap, QKeyEvent
from adbutils import adb
from PySide6.QtCore import *
from PySide6.QtWidgets import QApplication, QWidget, QPushButton, QHBoxLayout, QVBoxLayout, \
    QCheckBox, QLabel, QGridLayout, QSpacerItem, QSizePolicy

# 創(chuàng)建QApplication對象
if not QApplication.instance():
    app = QApplication([])
else:
    app = QApplication.instance()


items = [i.serial for i in adb.device_list()]  # 設備列表
client_dict = {}  # 設備scrcpy客服端字典
# 為所有設備建立scrcpy服務
for i in items:
    client_dict[i] = scrcpy.Client(device=i, bitrate=1000000000)


def thread_ui(func, *args):
    """
    開啟一個新線程任務\n
    :param func: 要執(zhí)行的線程函數(shù);
    :param args: 函數(shù)中需要傳入的參數(shù) Any
    :return:
    """
    t = threading.Thread(target=func, args=args)  # 定義新線程
    t.setDaemon(True)  # 開啟線程守護
    t.start()  # 執(zhí)行線程


class SignThread(QThread):
    """信號線程"""

    def __new__(cls, parent: QWidget, func, *types: type):
        cls.update_date = Signal(*types)  # 定義信號(*types)一個信號中可以有一個或多個類型的數(shù)據(jù)(int,str,list,...)
        return super().__new__(cls)  # 使用父類__new__方法創(chuàng)建SignThread實例對象

    def __init__(self, parent: QWidget, func, *types: type):
        """
        信號線程初始化\n
        :param parent: 界面UI控件
        :param func: 信號要綁定的方法
        :param types: 信號類型,可以是一個或多個(type,...)
        """
        super().__init__(parent)  # 初始化父類
        self.sign = None
        self.update_date.connect(func)  # 綁定信號與方法

    def send_sign(self, *args):
        """
        使用SonThread發(fā)送信號\n
        :param args: 信號的內容
        :return:
        """
        self.sign = args  # 信號元組(type,...)
        self.start()

    def run(self):
        """信號線程執(zhí)行時執(zhí)行此函數(shù)"""
        self.update_date.emit(*self.sign)  # 發(fā)送信號元組(type,...)


class MyWindow(QWidget):
    """UI界面"""

    def __init__(self):
        """UI界面初始化"""
        super().__init__()  # 初始化父級
        self.setWindowTitle('多臺手機投屏控制示例(python & scrcpy)')  # 設置窗口標題
        self.max_width = 600  # 設置手機投屏寬度
        self.setStyleSheet("""QLabel {border-width: 3px;border-style: solid;border-color: black;}""")  # 設置Qlabel標簽樣式
        # 定義元素
        self.check_box = QCheckBox("控制所有設備")  # 定義是否控制所有設備選擇框
        self.back_button = QPushButton("BACK")  # 定義返回鍵
        self.home_button = QPushButton("HOME")  # 定義home鍵
        self.recent_button = QPushButton("RECENT")  # 定義最近任務鍵
        self.video = QLabel("設備屏幕信息加載......")  # 定義手機投屏控制標簽
        self.video_list = []  # 定義手機投屏標簽列表
        for i in items:
            self.video_list.append(QLabel(i))  # 把投屏標簽加入列表
        self.main_layout = QHBoxLayout(self)  # 定義主布局容器
        self.frame_layout = QVBoxLayout()  # 定義投屏操控框容器
        self.button_layout = QHBoxLayout()
        self.device_layout = QVBoxLayout()  # 定義投屏容器
        self.list_layout = QGridLayout()  # 定義投屏列表布局容器
        self.spacer = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding)  # 彈性空間
        self.device_spacer = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding)  # 彈性空間
        self.v_spacer = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)  # 彈性空間
        # 頁面布局
        self.main_layout.addLayout(self.frame_layout)
        self.main_layout.addLayout(self.device_layout)
        self.main_layout.addItem(self.v_spacer)
        self.frame_layout.addWidget(self.video)
        self.frame_layout.addLayout(self.button_layout)
        self.frame_layout.addWidget(self.check_box)
        self.frame_layout.addItem(self.spacer)
        self.button_layout.addWidget(self.back_button)
        self.button_layout.addWidget(self.home_button)
        self.button_layout.addWidget(self.recent_button)
        self.device_layout.addLayout(self.list_layout)
        self.device_layout.addItem(self.device_spacer)
        # 交互事件
        self.back_button.clicked.connect(self.click_key(scrcpy.KEYCODE_BACK))
        self.home_button.clicked.connect(self.click_key(scrcpy.KEYCODE_HOME))
        self.recent_button.clicked.connect(self.click_key(scrcpy.KEYCODE_APP_SWITCH))
        self.video.mousePressEvent = self.mouse_event(scrcpy.ACTION_DOWN)
        self.video.mouseMoveEvent = self.mouse_event(scrcpy.ACTION_MOVE)
        self.video.mouseReleaseEvent = self.mouse_event(scrcpy.ACTION_UP)
        self.keyPressEvent = self.on_key_event(scrcpy.ACTION_DOWN)
        self.keyReleaseEvent = self.on_key_event(scrcpy.ACTION_UP)
        # 所有設備屏幕有序排布,最多15臺設備,可按需修改
        if len(items) > 0:
            self.now_device = items[0]
            self.now_client = client_dict[items[0]]
            self.now_client.add_listener(scrcpy.EVENT_FRAME, self.main_frame)
            for num in range(len(items)):
                self.video_list[num].mousePressEvent = self.switch_video(items[num])
                client = client_dict[items[num]]
                client.add_listener(scrcpy.EVENT_FRAME, self.on_frame(num, client))
                if num < 5:
                    self.list_layout.addWidget(self.video_list[num], 0, num, 1, 1)
                elif num < 10:
                    self.list_layout.addWidget(self.video_list[num], 1, num - 5, 1, 1)
                elif num < 15:
                    self.list_layout.addWidget(self.video_list[num], 2, num - 10, 1, 1)

        self.mouse_thread = SignThread(self, self.mouse_exe, int, int, int)

    def click_key(self, key_value: int):
        """
        按鍵事件\n
        :param key_value: 鍵值
        :return:
        """

        def key_event():
            if self.check_box.isChecked():
                for i in client_dict:
                    client_dict[i].control.keycode(key_value, scrcpy.ACTION_DOWN)
                    client_dict[i].control.keycode(key_value, scrcpy.ACTION_UP)
            else:
                self.now_client.control.keycode(key_value, scrcpy.ACTION_DOWN)
                self.now_client.control.keycode(key_value, scrcpy.ACTION_UP)

        return key_event

    def switch_video(self, device):
        """
        切換設備屏幕為主控屏幕\n
        :param device: 設備序列號
        :return:
        """

        def now_video(evt: QMouseEvent):
            app.processEvents()
            self.now_client.remove_listener(scrcpy.EVENT_FRAME, self.main_frame)
            self.now_client = client_dict[device]
            self.now_client.add_listener(scrcpy.EVENT_FRAME, self.main_frame)
            self.now_client.control.keycode(224, scrcpy.ACTION_DOWN)
            self.now_client.control.keycode(224, scrcpy.ACTION_UP)
            bound = self.now_client.resolution
            self.now_client.control.swipe(bound[0] / 2, bound[1] / 2, bound[0] / 2, bound[1] / 2 - 20)
            self.now_client.control.swipe(bound[0] / 2, bound[1] / 2 - 20, bound[0] / 2, bound[1] / 2)
            self.now_device = device

        return now_video

    def main_frame(self, frame):
        """
        監(jiān)聽設備屏幕數(shù)據(jù),設置控制窗口圖像\n
        :param frame: 屏幕數(shù)據(jù)
        :return:
        """
        app.processEvents()
        if frame is not None:
            ratio = self.max_width / max(self.now_client.resolution)
            image = QImage(
                frame,
                frame.shape[1],
                frame.shape[0],
                frame.shape[1] * 3,
                QImage.Format_BGR888,
            )
            pix = QPixmap(image)
            pix.setDevicePixelRatio(1 / ratio)
            self.video.setPixmap(pix)

    def on_frame(self, num, client):
        """
        監(jiān)聽設備屏幕數(shù)據(jù),設置小窗口圖像\n
        :param num: 設備投屏序號
        :param client: scrcpy服務
        :return:
        """

        def client_frame(frame):
            app.processEvents()
            if frame is not None:
                ratio = 300 / max(client.resolution)
                image = QImage(
                    frame,
                    frame.shape[1],
                    frame.shape[0],
                    frame.shape[1] * 3,
                    QImage.Format_BGR888,
                )
                pix = QPixmap(image)
                pix.setDevicePixelRatio(1 / ratio)
                self.video_list[num].setPixmap(pix)

        return client_frame

    def mouse_event(self, action=scrcpy.ACTION_DOWN):
        """
        鼠標事件\n
        :param action: 事件類型
        :return: 對應的事件函數(shù)
        """

        def event(evt: QMouseEvent):
            focused_widget = QApplication.focusWidget()
            if focused_widget is not None:
                focused_widget.clearFocus()
            ratio = self.max_width / max(self.now_client.resolution)
            self.mouse_thread.send_sign(evt.position().x() / ratio, evt.position().y() / ratio, action)

        return event

    def mouse_exe(self, x, y, action):
        """
        執(zhí)行鼠標事件\n
        :param x: x坐標
        :param y: y坐標
        :param action: 事件類型
        :return:
        """
        if self.check_box.isChecked():
            for i in client_dict:
                client_dict[i].control.touch(x, y, action)
        else:
            self.now_client.control.touch(x, y, action)

    def on_key_event(self, action=scrcpy.ACTION_DOWN):
        """
        鍵盤按鍵事件\n
        :param action: 事件類型
        :return: 對應的事件函數(shù)
        """

        def handler(evt: QKeyEvent):
            code = self.key_code(evt.key())
            if code != -1:
                if self.check_box.isChecked():
                    for i in client_dict:
                        client_dict[i].control.keycode(code, action)
                else:
                    self.now_client.control.keycode(code, action)

        return handler

    @staticmethod
    def key_code(code):
        """
        Map qt keycode ti android keycode

        Args:
            code: qt keycode
            android keycode, -1 if not founded
        """
        if code == -1:
            return -1
        if code == 35:
            return 18
        if code == 42:
            return 17
        if 48 <= code <= 57:
            return code - 48 + 7
        if 65 <= code <= 90:
            return code - 65 + 29
        if 97 <= code <= 122:
            return code - 97 + 29

        hard_code = {
            32: scrcpy.KEYCODE_SPACE,
            16777219: scrcpy.KEYCODE_DEL,
            16777248: scrcpy.KEYCODE_SHIFT_LEFT,
            16777220: scrcpy.KEYCODE_ENTER,
            16777217: scrcpy.KEYCODE_TAB,
            16777249: scrcpy.KEYCODE_CTRL_LEFT,
            16777235: scrcpy.KEYCODE_DPAD_UP,
            16777237: scrcpy.KEYCODE_DPAD_DOWN,
            16777234: scrcpy.KEYCODE_DPAD_LEFT,
            16777236: scrcpy.KEYCODE_DPAD_RIGHT,
        }
        if code in hard_code:
            return hard_code[code]

        print(f"Unknown keycode: {code}")
        return -1

    def closeEvent(self, _):
        """窗口關閉事件"""
        for i in client_dict:
            client_dict[i].stop()  # 關閉scrcpy服務


def main():
    for i in client_dict:
        thread_ui(client_dict[i].start)  # 給每一臺設備單獨開啟一個scrcpy服務線程
    widget = MyWindow()  # 實例化UI線程
    widget.resize(1200, 800)  # 設置窗口大小
    widget.show()  # 展示窗口
    sys.exit(app.exec())  # 持續(xù)刷新窗口


if __name__ == '__main__':
    main()

到了這里,關于python實現(xiàn)Android實時投屏操控的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。如若轉載,請注明出處: 如若內容造成侵權/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領支付寶紅包贊助服務器費用

相關文章

  • 誰說不可兼得,用scrcpy實現(xiàn)手機免流播放bilibili投屏電腦

    誰說不可兼得,用scrcpy實現(xiàn)手機免流播放bilibili投屏電腦

    目前的手機大額流量卡都是支持設備免流的,但是如何將這個流量用在其他設備,就相當麻煩。于是我查找了些相關Android投屏技術資料,發(fā)現(xiàn)了一個簡單的USB投屏工具——scrcpy。 安裝說明 Office:https://github.com/Genymobile/scrcpy/releases Windows用戶安裝建議在github上下載zip免安裝版

    2024年02月09日
    瀏覽(32)
  • 基于scrcpy的Android群控項目重構,獲取Android屏幕元素信息并編寫自動化事件

    基于scrcpy的Android群控項目重構,獲取Android屏幕元素信息并編寫自動化事件

    基于scrcpy的遠程調試方案 基于scrcpy的Android群控項目重構 基于scrcpy的Android群控項目重構 進階版 基于scrcpy的Android群控項目重構,獲取Android屏幕元素信息并編寫自動化事件(視頻) 基于scrcpy的Android群控項目重構,獲取Android屏幕元素信息并編寫自動化事件(博客) 基于scrcpy的

    2024年02月16日
    瀏覽(37)
  • Scrcpy手機投屏

    Scrcpy手機投屏

    Scrcpy投屏(電腦操作手機)@TOC Android設備至少需要5.0以上版本(即API 21) 確保在電腦設備上啟動了adb調試 在某些設備上,還需要啟動其他選項以使用建買盤和鼠標。鏈接: 其它選項 adb調試的開啟一般是多次點擊手機系統(tǒng)的版本號,比如vivoS15pro:設置-系統(tǒng)管理-關于手機-版本

    2024年02月09日
    瀏覽(34)
  • scrcpy 手機投屏與控制

    scrcpy 手機投屏與控制

    這是一款手機投屏到電腦的軟件, 但是是依靠命令使用的 ( github上的沒有UI屏幕控制 ) 下載連接: https://github.com/Genymobile/scrcpy?tab=readme-ov-file 已 windows 系統(tǒng)為例, 我下載的是 2.3.1 版本的, 選擇 zip 格式即可. 32/64位根據(jù)電腦情況選擇即可. 下載完成后, 自行解壓到自己想放的目錄下

    2024年04月16日
    瀏覽(21)
  • 【投屏】Scrcpy源碼分析一(編譯篇)

    【投屏】Scrcpy源碼分析一(編譯篇)

    Scrcpy源碼分析系列 【投屏】Scrcpy源碼分析一(編譯篇) 【投屏】Scrcpy源碼分析二(Client篇-連接階段) 【投屏】Scrcpy源碼分析三(Client篇-投屏階段) 【投屏】Scrcpy源碼分析四(最終章 - Server篇) Scrcpy是一款小巧的Android設備投屏軟件??梢钥缙脚_,在Windows、Linux、MacOS上對

    2024年02月12日
    瀏覽(18)
  • 基于scrcpy的Android群控項目重構,集成Appium服務執(zhí)行自動化測試用例

    基于scrcpy的Android群控項目重構,集成Appium服務執(zhí)行自動化測試用例

    基于scrcpy的Android群控項目重構 基于scrcpy的Android群控項目重構 進階版 基于scrcpy的Android群控項目重構,獲取Android屏幕元素信息并編寫自動化事件(視頻) 基于scrcpy的Android群控項目重構,獲取Android屏幕元素信息并編寫自動化事件(博客) 基于scrcpy的Android群控項目重構,集成

    2024年02月16日
    瀏覽(41)
  • ADB 連接后,使用scrcpy投屏電腦

    ADB 連接后,使用scrcpy投屏電腦

    將三個ADB文件復制后,放到C:WindowsSystem32下,同時也復制一份放到C:WindowsSysWOW64下 ADB文件: 他這里有提供百度網(wǎng)盤連接下載這幾個文件 【adb安裝】簡單的一批的adb安裝,少走彎路_嗶哩嗶哩_bilibili 然后,cmd命令,?輸入adb,出現(xiàn)版本號,出現(xiàn)一大堆的代碼說明,說明安裝成功

    2024年02月09日
    瀏覽(18)
  • C++版Android實時投屏軟件系統(tǒng)源碼,安卓手機投屏軟件源碼,無需root權限

    C++版Android實時投屏軟件系統(tǒng)源碼,安卓手機投屏軟件源碼,無需root權限

    QtScrcpy 可以通過 USB / 網(wǎng)絡連接Android設備,并進行顯示和控制。無需root權限。 同時支持 GNU/Linux ,Windows 和 MacOS 三大主流桌面平臺。 完整代碼下載地址:C++版Android實時投屏軟件系統(tǒng)源碼 它專注于: 精致 (僅顯示設備屏幕) 性能 (30~60fps) 質量 (1920×1080以上) 低延遲 (35~70ms) 快速啟

    2024年02月05日
    瀏覽(23)
  • 【投屏】Scrcpy源碼分析四(最終章 - Server篇)

    【投屏】Scrcpy源碼分析四(最終章 - Server篇)

    Scrcpy源碼分析系列 【投屏】Scrcpy源碼分析一(編譯篇) 【投屏】Scrcpy源碼分析二(Client篇-連接階段) 【投屏】Scrcpy源碼分析三(Client篇-投屏階段) 【投屏】Scrcpy源碼分析四(最終章 - Server篇) 在前兩篇我們探究了Scrcpy Client的連接和投屏邏輯,本篇我們就要繼續(xù)探究Serv

    2024年02月06日
    瀏覽(20)
  • 分享一個開源的windows安卓投屏工具,scrcpy

    分享一個開源的windows安卓投屏工具,scrcpy

    安裝adb - ADB是一個Android Debug Bridge,用于與Android設備進行通信。如果您已經(jīng)安裝了Android Studio,則可以從其中運行adb。否則,您可以從ADB官方網(wǎng)站下載并手動安裝。 安裝SDL庫 - Scrcpy使用SDL庫來呈現(xiàn)Android設備的屏幕。您可以使用系統(tǒng)包管理器來安裝SDL庫,例如,在Ubuntu上,您可

    2023年04月18日
    瀏覽(54)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領取紅包,優(yōu)惠每天領

二維碼1

領取紅包

二維碼2

領紅包