1. 程式人生 > 其它 >python實現遠端桌面

python實現遠端桌面

專案旨在讓大家理解遠控軟體的原理,通過遠控桌面可以實現遠端控制我們的電腦,更好更方便的管理電腦。文末將給出初始版的完整程式碼,需要使用到的其他工具也會有所說明。最終實現的效果就是隻要使用者點選了客戶端的程式執行,我們就可以在服務端對其進行控制。效果如下:左邊是客服端程式運行了,然後我們就可以在左邊的另一臺電腦上開啟服務端程式進行控制,可以看到左邊的螢幕影象也已經顯示在了右邊的電腦上。完整程式碼見文末!

01

遠控流程

1.1 環境要求

本次環境使用的是python3.6.5+windows平臺

主要用的庫有:影象處理庫opencv,包括用來目標檢測和影象處理等操作。

Socket用來遠端傳輸資料達到遠端控制的效果;

Threading模組用來建立多執行緒管理;

Numpy模組用來輔助opencv對影象進行一些畫素值操作;

PIL模組用來獲取螢幕影象資料;

pynput.mouse用來控制滑鼠點選事件。達到遠端控制滑鼠的作用。

1.2 客戶端講解

客戶端在這裡指的是被控制的電腦,就是我們需要受到控制的電腦。

(1)首先是匯入相關模組:

  1. 1#客戶端程式碼
  2. 2importsocket
  3. 3importthreading
  4. 4importcv2
  5. 5importnumpyasnp
  6. 6fromPILimportImageGrab
  7. 7frompynput.mouseimportButton,Controller

(2)接著建立一個滑鼠控制器和用來接收服務端資料的函式。因為需要一直都接收資料,故需要嵌入迴圈。在這裡客戶端還需要接收資料的原因是,用來接收服務端傳來的滑鼠控制資訊,要不然怎麼實現滑鼠控制桌面的效果呢。

  1. 1#接受伺服器返回的資料的函式
  2. 2m=Controller()
  3. 3defrecvlink(client):
  4. 4whileTrue:
  5. 5msg=client.recv(1024)
  6. 6msg=msg.decode('utf-8')
  7. 7print(msg)
  8. 8key=msg.split(",")
  9. 9xp=int(key[0])
  10. 10yp=int(key[1])
  11. 11m.position=((xp,yp))
  12. 12m.click(Button.left,1)

(3)建立ipv4的socket物件,使用TCP協議(SOCK_STREAM)。然後設定服務端IP地址,以及埠。這裡用來向服務端傳輸資料,即傳輸桌面影象資料。註釋程式碼如下:

  1. 1#建立ipv4的socket物件,使用TCP協議(SOCK_STREAM)
  2. 2client=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
  3. 3#設定伺服器ip地址,注意應該是伺服器的公網ip
  4. 4host='伺服器的公網ip'
  5. 5#設定要傳送到的伺服器埠,需要在雲伺服器管理介面開啟對應埠的防火牆
  6. 6port=設定的埠
  7. 7#建立TCP協議連線,這時候伺服器就會監聽到到連線請求,並開始等待接受client傳送的資料
  8. 8client.connect((host,port))
  9. 9#建立連線後,伺服器端會返回連線成功訊息
  10. 10start_msg=client.recv(1024)
  11. 11print(start_msg.decode('utf-8'))
  12. 12#開啟一個執行緒用來接受伺服器發來的訊息
  13. 13t=threading.Thread(target=recvlink,args=(client,))
  14. 14t.start()
  15. 15p=ImageGrab.grab()#獲得當前螢幕
  16. 16quality=25#影象的質量
  17. 17encode_param=[int(cv2.IMWRITE_JPEG_QUALITY),quality]
  18. 18whileTrue:
  19. 19im=ImageGrab.grab()
  20. 20imm=cv2.cvtColor(np.array(im),cv2.COLOR_RGB2BGR)#轉為opencv的BGR格式
  21. 21imm=cv2.resize(imm,(1535,863))
  22. 22img_encode=cv2.imencode(".jpg",imm,encode_param)[1]
  23. 23data_encode=np.array(img_encode)
  24. 24str_encode=data_encode.tostring()
  25. 25#print(len(str_encode))
  26. 26#輸入要傳送的資訊
  27. 27sendmsg="kehu"
  28. 28#向伺服器傳送訊息
  29. 29client.send(str_encode)
  30. 30ifsendmsg=='quit':
  31. 31break
  32. 32#結束時關閉客戶端
  33. 33client.close()

1.3 服務端講解

服務端指的是用來控制遠端電腦的那一端,為了方便使用,我們直接在伺服器上使用即可。

(1)匯入使用到的模組:

  1. 1#伺服器端
  2. 2importsocket
  3. 3importthreading
  4. 4importnumpyasnp
  5. 5importcv2
  6. 6importos

(2)建立滑鼠點選事件函式,用來獲取滑鼠點選的位置座標:

  1. 1print("等待連線---")
  2. 2defmouse_click(event,x,y,flags,para):
  3. 3ifevent==cv2.EVENT_LBUTTONDOWN:#左邊滑鼠點選
  4. 4f=open("1.txt","w")
  5. 5f.write(str(x)+","+str(y))
  6. 6f.close()

(3)建立伺服器端接收資料函式,用來實時接收傳輸過來的影象資料並顯示:

  1. 1defrecv_msg(clientsocket):
  2. 2whileTrue:
  3. 3#接受客戶端訊息,設定一次最多接受10240位元組的資料
  4. 4recv_msg=clientsocket.recv(102400)
  5. 5#把接收到的東西解碼
  6. 6msg=np.fromstring(recv_msg,np.uint8)
  7. 7img_decode=cv2.imdecode(msg,cv2.IMREAD_COLOR)
  8. 8try:
  9. 9s=img_decode.shape
  10. 10img_decode=img_decode
  11. 11temp=img_decode
  12. 12except:
  13. 13img_decode=temp
  14. 14pass
  15. 15cv2.imshow('SERVER',img_decode)
  16. 16cv2.setMouseCallback("SERVER",mouse_click)
  17. 17try:
  18. 18f=open("1.txt")
  19. 19txt=f.read()
  20. 20f.close()
  21. 21reply=txt
  22. 22print(reply)
  23. 23clientsocket.send(reply.encode('utf-8'))
  24. 24os.remove("1.txt")
  25. 25except:
  26. 26pass
  27. 27ifcv2.waitKey(1)&0xFF==ord('q'):
  28. 28break

(4)主函式,用來建立連線和資料接收等功能。

  1. 1defmain():
  2. 2socket_server=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
  3. 3host='伺服器的本地ip'
  4. 4#設定被監聽的埠號,小於1024的埠號不能使用
  5. 5port=設定的埠
  6. 6socket_server.bind((host,port))
  7. 7#設定最大監聽數,也就是最多可以同時響應幾個客戶端請求,一般配合多執行緒使用
  8. 8socket_server.listen(5)
  9. 9#等待客戶端連線,一旦有了連線就立刻向下執行,否則等待
  10. 10#accept()函式會返回一個元組,第一個元素是客戶端socket物件,第二個元素是客戶端地址(ip地址+埠號)
  11. 11clientsocket,addr=socket_server.accept()
  12. 12#有了客戶端連線後之後才能執行以下程式碼,我們先向客戶端傳送連線成功訊息
  13. 13clientsocket.send('連線成功'.encode('utf-8'))
  14. 14#和客戶端一樣開啟一個執行緒接受客戶端的資訊
  15. 15t=threading.Thread(target=recv_msg,args=(clientsocket,))
  16. 16t.start()

02

遠端控制GUI視窗

遠控桌面GUI主要是為了美觀而用,需要大家根據遠端程式碼進行集合修改。當然單獨使用上述程式碼已經可以實現功能了,只是不夠美觀。由於考慮到此處程式碼量較大,且不是重點,故粗略講解

(1)匯入相關庫:

  1. 1fromPyQt5.QtWidgetsimport*
  2. 2fromPyQt5.QtCoreimport*
  3. 3fromPyQt5.QtGuiimportQPalette,QBrush,QPixmap
  4. 4importos
  5. 5importsocket
  6. 6importthreading
  7. 7importcv2
  8. 8importnumpyasnp
  9. 9fromPILimportImageGrab
  10. 10frompynput.mouseimportButton,Controller
  11. 11importtime

(2)建立滑鼠控制函式和點選函式

  1. 1m=Controller()
  2. 2defmouse_click(event,x,y,flags,para):
  3. 3ifevent==cv2.EVENT_LBUTTONDOWN:#左邊滑鼠點選
  4. 4print(x,y)
  5. 5m.position=(x,y)
  6. 6time.sleep(0.1)
  7. 7m.click(Button.left,1)

(3)GUI介面初始化,由於我們需要把實時的視訊顯示在視窗上,故也需要使用到opencv。

  1. 1def__init__(self,parent=None):
  2. 2super(Ui_MainWindow,self).__init__(parent)
  3. 3#self.face_recong=face.Recognition()
  4. 4self.timer_camera=QtCore.QTimer()
  5. 5self.cap=cv2.VideoCapture()
  6. 6self.CAM_NUM=0
  7. 7self.set_ui()
  8. 8self.slot_init()
  9. 9self.__flag_work=0
  10. 10self.x=0
  11. 11self.count=0

(4)設定視窗大小和控制元件位置等資訊。建立佈局和設定名稱

  1. 1defset_ui(self):
  2. 2self.__layout_main=QtWidgets.QHBoxLayout()
  3. 3self.__layout_fun_button=QtWidgets.QVBoxLayout()
  4. 4self.__layout_data_show=QtWidgets.QVBoxLayout()
  5. 5self.button_open_camera=QtWidgets.QPushButton(u'遠端桌面')
  6. 6self.button_close=QtWidgets.QPushButton(u'退出')
  7. 7#Button的顏色修改
  8. 8button_color=[self.button_open_camera,self.button_close]
  9. 9foriinrange(2):
  10. 10button_color[i].setStyleSheet("QPushButton{color:black}"
  11. 11"QPushButton:hover{color:red}"
  12. 12"QPushButton{ outline: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; list-style: none; overflow-wrap: break-word; height: 22px;"> 13"QPushButton{border:2px}"
  13. 14"QPushButton{border-radius:10px}"
  14. 15"QPushButton{padding:2px4px}")
  15. 16self.button_open_camera.setMinimumHeight(50)
  16. 17self.button_close.setMinimumHeight(50)
  17. 18# move()方法移動視窗在螢幕上的位置到x = 300,y = 300座標。
  18. 19self.move(500,500)
  19. 20#資訊顯示
  20. 21self.label_show_camera=QtWidgets.QLabel()
  21. 22self.label_move=QtWidgets.QLabel()
  22. 23self.label_move.setFixedSize(100,100)
  23. 24self.label_show_camera.setFixedSize(1530,863)
  24. 25self.label_show_camera.setAutoFillBackground(False)
  25. 26self.__layout_fun_button.addWidget(self.button_open_camera)
  26. 27self.__layout_fun_button.addWidget(self.button_close)
  27. 28self.__layout_fun_button.addWidget(self.label_move)
  28. 29self.__layout_main.addLayout(self.__layout_fun_button)
  29. 30self.__layout_main.addWidget(self.label_show_camera)
  30. 31self.setLayout(self.__layout_main)
  31. 32self.label_move.raise_()
  32. 33self.setWindowTitle(u'遠控桌面GUI')
  33. 34'''
  34. 35#設定背景圖片
  35. 36palette1=QPalette()
  36. 37palette1.setBrush(self.backgroundRole(),QBrush(QPixmap('background.jpg')))
  37. 38self.setPalette(palette1)
  38. 39'''

(5)獲取滑鼠點選時的座標:

  1. 1defmousePressEvent(self,event):
  2. 2ifevent.buttons()&QtCore.Qt.LeftButton:
  3. 3x=event.x()-120
  4. 4y=event.y()-10
  5. 5text="x:{0},y:{1}".format(x,y)
  6. 6ifx>=0andy>=0:
  7. 7m.position=(x,y)
  8. 8time.sleep(0.1)
  9. 9m.click(Button.left,1)
  10. 10print(text)

(6)按鈕繫結所設定的函式:

  1. 1defslot_init(self):
  2. 2self.button_open_camera.clicked.connect(self.button_open_camera_click)
  3. 3self.timer_camera.timeout.connect(self.show_camera)
  4. 4self.button_close.clicked.connect(self.close)

(7)顯示桌面功能函式,並設定點選時修改名稱,可以隨時關閉桌面

  1. 1defbutton_open_camera_click(self):
  2. 2ifself.timer_camera.isActive()==False:
  3. 3self.timer_camera.start(30)
  4. 4self.button_open_camera.setText(u'關閉')
  5. 5else:
  6. 6self.timer_camera.stop()
  7. 7self.cap.release()
  8. 8self.label_show_camera.clear()
  9. 9self.button_open_camera.setText(u'遠端桌面')

(8)顯示桌面函式和退出程式函式

  1. 1defshow_camera(self):
  2. 2im=ImageGrab.grab()
  3. 3imm=cv2.cvtColor(np.array(im),cv2.COLOR_RGB2BGR)#轉為opencv的BGR格式
  4. 4#imm=cv2.resize(imm,(1535,863))
  5. 5self.image=imm
  6. 6#face=self.face_detect.align(self.image)
  7. 7#ifface:
  8. 8#pass
  9. 9show=cv2.resize(self.image,(1536,863))
  10. 10show=cv2.cvtColor(show,cv2.COLOR_BGR2RGB)
  11. 11print(show.shape[1],show.shape[0])
  12. 12#show.shape[1]=640,show.shape[0]=480
  13. 13showImage=QtGui.QImage(show.data,show.shape[1],show.shape[0],QtGui.QImage.Format_RGB888)
  14. 14self.label_show_camera.setPixmap(QtGui.QPixmap.fromImage(showImage))
  15. 15#cv2.setMouseCallback(showImage,mouse_click)
  16. 16#self.x+=1
  17. 17#self.label_move.move(self.x,100)
  18. 18#ifself.x==320:
  19. 19#self.label_show_camera.raise_()
  20. 20defcloseEvent(self,event):
  21. 21ok=QtWidgets.QPushButton()
  22. 22cacel=QtWidgets.QPushButton()
  23. 23msg=QtWidgets.QMessageBox(QtWidgets.QMessageBox.Warning,u"關閉",u"是否關閉!")
  24. 24msg.addButton(ok,QtWidgets.QMessageBox.ActionRole)
  25. 25msg.addButton(cacel,QtWidgets.QMessageBox.RejectRole)
  26. 26ok.setText(u'確定')
  27. 27cacel.setText(u'取消')
  28. 28#msg.setDetailedText('sdfsdff')
  29. 29ifmsg.exec_()==QtWidgets.QMessageBox.RejectRole:
  30. 30event.ignore()
  31. 31else:
  32. 32#self.socket_client.send_command(self.socket_client.current_user_command)
  33. 33ifself.cap.isOpened():
  34. 34self.cap.release()
  35. 35ifself.timer_camera.isActive():
  36. 36self.timer_camera.stop()
  37. 37event.accept()