QQ登录

只需要一步,快速开始

APP扫码登录

只需要一步,快速开始

查看: 6738|回复: 0

[Python] Python手把手实现远程控制桌面(含完整源码)

[复制链接]

等级头衔

积分成就    金币 : 2861
   泡泡 : 1516
   精华 : 6
   在线时间 : 1328 小时
   最后登录 : 2026-7-17

丰功伟绩

优秀达人突出贡献荣誉管理论坛元老活跃会员

联系方式
发表于 2021-3-9 10:12:05 | 显示全部楼层 |阅读模式
一、引言: @. }+ z: d' f* m1 a8 m$ W- b
       本项目旨在让大家理解远控软件的原理,通过远控桌面可以实现远程控制我们的电脑,更好更方便的管理电脑。文末将给出初始版的完整代码,需要使用到的其他工具也会有所说明。最终实现的效果就是只要用户点击了客户端的程序运行,我们就可以在服务端对其进行控制。效果如下:左边是客服端程序运行了,然后我们就可以在左边的另一台电脑上打开服务端程序进行控制,可以看到左边的屏幕图像也已经显示在了右边的电脑上。完整代码见文末!
& U! e3 T% a8 i. h# `二、远控流程
0 y' Y5 H- L* I/ u: n# L9 b, M" F1.1 环境要求/ O" \1 {  V" U3 G) W! G
本次环境使用的是python3.6.5+windows平台
# _; H9 u: c0 g+ P; w9 J主要用的库有:图像处理库opencv,包括用来目标检测和图像处理等操作。
0 j$ q* Z# g8 G5 i: s; OSocket用来远程传输数据达到远程控制的效果;
/ }+ U( M" o* u6 j# T8 F: w" s2 T: UThreading模块用来创建多线程管理;/ c3 U! a. G0 I: r* g
Numpy模块用来辅助opencv对图像进行一些像素值操作;
) V9 l: F! B! p1 {! ZPIL模块用来获取屏幕图像数据;( Q9 q- ?4 r" @+ ~8 Z% p
pynput.mouse用来控制鼠标点击事件。达到远程控制鼠标的作用。8 ?5 `2 u* T1 I! a/ B6 V7 c: U% X
1.2 客户端讲解7 j0 a3 D; r" Z6 D- k
客户端在这里指的是被控制的电脑,就是我们需要受到控制的电脑。8 P% e( j4 Y( \6 A, C% c/ N& R# V
      (1)首先是导入相关模块:: k" }% e* J9 `' f  Q$ j
#客户端代码
import socket
import threading
import cv2
import numpy as np
from PIL import ImageGrab
from pynput.mouse import Button,Controller
      (2)接着创建一个鼠标控制器和用来接收服务端数据的函数。因为需要一直都接收数据,故需要嵌入循环。在这里客户端还需要接收数据的原因是,用来接收服务端传来的鼠标控制信息,要不然怎么实现鼠标控制桌面的效果呢。
; x  Y  M6 L; `) o+ p0 v* |
#接受服务器返回的数据的函数
m = Controller()
def recvlink(client):
    while True:
        msg=client.recv(1024)
        msg=msg.decode('utf-8')
        print(msg)
        key = msg.split(",")
        xp = int(key[0])
        yp = int(key[1])
        m.position = ((xp,yp))
        m.click(Button.left,1)
      (3)创建ipv4的socket对象,使用TCP协议(SOCK_STREAM)。然后设置服务端IP地址,以及端口。这里用来向服务端传输数据,即传输桌面图像数据。注释代码如下:6 _" c; ]3 U* G* U  z9 d3 F, d- }
#创建ipv4的socket对象,使用TCP协议(SOCK_STREAM)
client=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#设置服务器ip地址,注意应该是服务器的公网ip
host='服务器的公网ip'
#设置要发送到的服务器端口,需要在云服务器管理界面打开对应端口的防火墙
port=设置的端口
#建立TCP协议连接,这时候服务器就会监听到到连接请求,并开始等待接受client发送的数据
client.connect((host,port))
#建立连接后,服务器端会返回连接成功消息
start_msg=client.recv(1024)
print(start_msg.decode('utf-8'))
#开启一个线程用来接受服务器发来的消息
t=threading.Thread(target=recvlink,args=(client,))
t.start()
p = ImageGrab.grab()#获得当前屏幕
quality = 25  # 图像的质量
encode_param = [int(cv2.IMWRITE_JPEG_QUALITY), quality]
while True:
    im = ImageGrab.grab()
    imm=cv2.cvtColor(np.array(im), cv2.COLOR_RGB2BGR)#转为opencv的BGR格式
    imm = cv2.resize(imm, (1535, 863))
    img_encode = cv2.imencode(".jpg", imm, encode_param)[1]
    data_encode = np.array(img_encode)
    str_encode = data_encode.tostring()
    #print(len(str_encode))
    #输入要发送的信息
    sendmsg="kehu"
    #向服务器发送消息
    client.send(str_encode)
    if sendmsg=='quit':
        break
#结束时关闭客户端
client.close()
1.3 服务端讲解6 B" A( K7 T* r9 c- K
       服务端指的是用来控制远程电脑的那一端,为了方便使用,我们直接在服务器上使用即可。; X( m8 z( {4 v3 H2 l- y0 ~+ ^) T
       (1)导入使用到的模块:5 ^7 H/ ~8 u! n0 V

) I* B+ j2 S" y7 z  U
#服务器端
import socket
import threading
import numpy as np
import cv2
import os
      (2)创建鼠标点击事件函数,用来获取鼠标点击的位置坐标:' c, P7 S$ f" B- Z3 z+ ]0 x& \3 @
print("等待连接---")
def mouse_click(event, x, y, flags, para):
    if event == cv2.EVENT_LBUTTONDOWN:  # 左边鼠标点击
        f=open("1.txt","w")
        f.write(str(x)+","+str(y))
        f.close()
     (3)创建服务器端接收数据函数,用来实时接收传输过来的图像数据并显示:
* z  ?, v9 f7 z6 p" Q
def recv_msg(clientsocket):
    while True:
        # 接受客户端消息,设置一次最多接受10240字节的数据
        recv_msg = clientsocket.recv(102400)
        # 把接收到的东西解码
        msg = np.fromstring(recv_msg, np.uint8)
        img_decode = cv2.imdecode(msg, cv2.IMREAD_COLOR)
        try:
            s=img_decode.shape
            img_decode=img_decode
            temp=img_decode
        except:
            img_decode=temp
            pass
        cv2.imshow('SERVER', img_decode)
        cv2.setMouseCallback("SERVER", mouse_click)
        try:
            f=open("1.txt")
            txt=f.read()
            f.close()
            reply=txt
            print(reply)
            clientsocket.send(reply.encode('utf-8'))
            os.remove("1.txt")
        except:
            pass
        if cv2.waitKey(1) & 0xFF == ord('q'):
            break
      (4)主函数,用来建立连接和数据接收等功能。0 ^1 W5 ], S! O+ o9 O  d; z
def main():
    socket_server=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    host='服务器的本地ip'
    #设置被监听的端口号,小于1024的端口号不能使用
    port=设置的端口
    socket_server.bind((host,port))
     #设置最大监听数,也就是最多可以同时响应几个客户端请求,一般配合多线程使用
    socket_server.listen(5)
    #等待客户端连接,一旦有了连接就立刻向下执行,否则等待
    #accept()函数会返回一个元组,第一个元素是客户端socket对象,第二个元素是客户端地址(ip地址+端口号)
    clientsocket,addr=socket_server.accept()
    # 有了客户端连接后之后才能执行以下代码,我们先向客户端发送连接成功消息
    clientsocket.send('连接成功'.encode('utf-8'))
    # 和客户端一样开启一个线程接受客户端的信息
    t=threading.Thread(target=recv_msg,args=(clientsocket,))
    t.start()
三、远程控制GUI窗口9 Q4 L1 u- v0 D. V
1.jpg
( a( c! D  {: ~8 r6 }. G       远控桌面GUI主要是为了美观而用,需要大家根据远程代码进行集合修改。当然单独使用上述代码已经可以实现功能了,只是不够美观。由于考虑到此处代码量较大,且不是重点,故粗略讲解。
  k/ N/ ]. a6 y2 E$ q       (1)导入相关库:% v$ C0 n" l9 L( m% C1 F& Y
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import QPalette, QBrush, QPixmap
import os
import socket
import threading
import cv2
import numpy as np
from PIL import ImageGrab
from pynput.mouse import Button,Controller
import time
      (2)建立鼠标控制函数和点击函数
4 P: L( a1 H7 x
m = Controller()
def mouse_click(event, x, y, flags, para):
    if event == cv2.EVENT_LBUTTONDOWN:  # 左边鼠标点击
        print( x, y)
        m.position = (x, y)
        time.sleep(0.1)
        m.click(Button.left, 1)
     (3)GUI界面初始化,由于我们需要把实时的视频显示在窗口上,故也需要使用到opencv。: ~( R4 u9 f5 e8 M  S/ o
def __init__(self, parent=None):
    super(Ui_MainWindow, self).__init__(parent)
    # self.face_recong = face.Recognition()
    self.timer_camera = QtCore.QTimer()
    self.cap = cv2.VideoCapture()
    self.CAM_NUM = 0
    self.set_ui()
    self.slot_init()
    self.__flag_work = 0
    self.x = 0
    self.count = 0
      (4)设置窗口大小和控件位置等信息。创建布局和设置名称
8 Z6 d; F1 I9 \5 ^% t& f( z
def set_ui(self):
    self.__layout_main = QtWidgets.QHBoxLayout()
    self.__layout_fun_button = QtWidgets.QVBoxLayout()
    self.__layout_data_show = QtWidgets.QVBoxLayout()
    self.button_open_camera = QtWidgets.QPushButton(u'远程桌面')
    self.button_close = QtWidgets.QPushButton(u'退出')
    # Button 的颜色修改
    button_color = [self.button_open_camera, self.button_close]
    for i in range(2):
        button_color[i].setStyleSheet("QPushButton{color:black}"
                                      "QPushButton:hover{color:red}"
                                      "QPushButton{background-color:rgb(78,255,255)}"
                                      "QPushButton{border:2px}"
                                      "QPushButton{border-radius:10px}"
                                      "QPushButton{padding:2px 4px}")
    self.button_open_camera.setMinimumHeight(50)
    self.button_close.setMinimumHeight(50)
    # move()方法移动窗口在屏幕上的位置到x = 300,y = 300坐标。
    self.move(500, 500)
    # 信息显示
    self.label_show_camera = QtWidgets.QLabel()
    self.label_move = QtWidgets.QLabel()
    self.label_move.setFixedSize(100, 100)
    self.label_show_camera.setFixedSize(1530,863)
    self.label_show_camera.setAutoFillBackground(False)
    self.__layout_fun_button.addWidget(self.button_open_camera)
    self.__layout_fun_button.addWidget(self.button_close)
    self.__layout_fun_button.addWidget(self.label_move)
    self.__layout_main.addLayout(self.__layout_fun_button)
    self.__layout_main.addWidget(self.label_show_camera)
    self.setLayout(self.__layout_main)
    self.label_move.raise_()
    self.setWindowTitle(u'远控桌面GUI')
    '''
    # 设置背景图片
    palette1 = QPalette()
    palette1.setBrush(self.backgroundRole(), QBrush(QPixmap('background.jpg')))
    self.setPalette(palette1)
    '''
      (5)获取鼠标点击时的坐标:
) |+ _" P- L+ x
def mousePressEvent(self,event):
    if event.buttons() & QtCore.Qt.LeftButton:
        x = event.x()-120
        y = event.y()-10
        text = "x: {0},y: {1}".format(x,y)
        if x>=0 and y>=0:
            m.position = (x, y)
            time.sleep(0.1)
            m.click(Button.left, 1)
        print(text)
      (6)按钮绑定所设置的函数:, S! k+ u" u: O; w* {0 F
def slot_init(self):
    self.button_open_camera.clicked.connect(self.button_open_camera_click)
    self.timer_camera.timeout.connect(self.show_camera)
    self.button_close.clicked.connect(self.close)
      (7)显示桌面功能函数,并设置点击时修改名称,可以随时关闭桌面
. I3 K! ]8 \* Z* Z
def button_open_camera_click(self):
    if self.timer_camera.isActive() == False:
        self.timer_camera.start(30)
        self.button_open_camera.setText(u'关闭')
    else:
        self.timer_camera.stop()
        self.cap.release()
        self.label_show_camera.clear()
        self.button_open_camera.setText(u'远程桌面')
      (8)显示桌面函数和退出程序函数& ]( T" v- K: d2 Z" b4 S3 J, \
def show_camera(self):
    im = ImageGrab.grab()
    imm = cv2.cvtColor(np.array(im), cv2.COLOR_RGB2BGR)  # 转为opencv的BGR格式
    #imm = cv2.resize(imm, (1535, 863))
    self.image = imm
    # face = self.face_detect.align(self.image)
    # if face:
    #     pass
    show =cv2.resize(self.image, (1536,863))
    show = cv2.cvtColor(show, cv2.COLOR_BGR2RGB)
    print(show.shape[1], show.shape[0])
    # show.shape[1] = 640, show.shape[0] = 480
    showImage = QtGui.QImage(show.data, show.shape[1], show.shape[0], QtGui.QImage.Format_RGB888)
    self.label_show_camera.setPixmap(QtGui.QPixmap.fromImage(showImage))
    #cv2.setMouseCallback(showImage, mouse_click)
    # self.x += 1
    # self.label_move.move(self.x,100)
    # if self.x ==320:
    #     self.label_show_camera.raise_()
def closeEvent(self, event):
    ok = QtWidgets.QPushButton()
    cacel = QtWidgets.QPushButton()
    msg = QtWidgets.QMessageBox(QtWidgets.QMessageBox.Warning, u"关闭", u"是否关闭!")
    msg.addButton(ok, QtWidgets.QMessageBox.ActionRole)
    msg.addButton(cacel, QtWidgets.QMessageBox.RejectRole)
    ok.setText(u'确定')
    cacel.setText(u'取消')
    # msg.setDetailedText('sdfsdff')
    if msg.exec_() == QtWidgets.QMessageBox.RejectRole:
        event.ignore()
    else:
        #             self.socket_client.send_command(self.socket_client.current_user_command)
        if self.cap.isOpened():
            self.cap.release()
        if self.timer_camera.isActive():
            self.timer_camera.stop()
        event.accept()

  C/ y& r6 Q' n+ o! b' p$ A6 R/ s6 C4 h/ T9 w
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|小黑屋|paopaomj.COM ( 渝ICP备18007172号|渝公网安备50010502503914号 )

GMT+8, 2026-9-4 14:25

Powered by paopaomj X3.5 © 2016-2025 sitemap

快速回复 返回顶部 返回列表