python實現遠端桌面
專案旨在讓大家理解遠控軟體的原理,通過遠控桌面可以實現遠端控制我們的電腦,更好更方便的管理電腦。文末將給出初始版的完整程式碼,需要使用到的其他工具也會有所說明。最終實現的效果就是隻要使用者點選了客戶端的程式執行,我們就可以在服務端對其進行控制。效果如下:左邊是客服端程式運行了,然後我們就可以在左邊的另一臺電腦上開啟服務端程式進行控制,可以看到左邊的螢幕影象也已經顯示在了右邊的電腦上。完整程式碼見文末!
01
遠控流程
1.1 環境要求
本次環境使用的是python3.6.5+windows平臺
主要用的庫有:影象處理庫opencv,包括用來目標檢測和影象處理等操作。
Socket用來遠端傳輸資料達到遠端控制的效果;
Threading模組用來建立多執行緒管理;
Numpy模組用來輔助opencv對影象進行一些畫素值操作;
PIL模組用來獲取螢幕影象資料;
pynput.mouse用來控制滑鼠點選事件。達到遠端控制滑鼠的作用。
1.2 客戶端講解
客戶端在這裡指的是被控制的電腦,就是我們需要受到控制的電腦。
(1)首先是匯入相關模組:
- 1#客戶端程式碼
- 2importsocket
- 3importthreading
- 4importcv2
- 5importnumpyasnp
- 6fromPILimportImageGrab
-
7frompynput.mouseimportButton,Controller
(2)接著建立一個滑鼠控制器和用來接收服務端資料的函式。因為需要一直都接收資料,故需要嵌入迴圈。在這裡客戶端還需要接收資料的原因是,用來接收服務端傳來的滑鼠控制資訊,要不然怎麼實現滑鼠控制桌面的效果呢。
- 1#接受伺服器返回的資料的函式
- 2m=Controller()
- 3defrecvlink(client):
- 4whileTrue:
- 5msg=client.recv(1024)
- 6msg=msg.decode('utf-8')
- 7print(msg)
- 8key=msg.split(",")
-
9xp=int(key[0])
- 10yp=int(key[1])
- 11m.position=((xp,yp))
- 12m.click(Button.left,1)
(3)建立ipv4的socket物件,使用TCP協議(SOCK_STREAM)。然後設定服務端IP地址,以及埠。這裡用來向服務端傳輸資料,即傳輸桌面影象資料。註釋程式碼如下:
- 1#建立ipv4的socket物件,使用TCP協議(SOCK_STREAM)
- 2client=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
- 3#設定伺服器ip地址,注意應該是伺服器的公網ip
- 4host='伺服器的公網ip'
- 5#設定要傳送到的伺服器埠,需要在雲伺服器管理介面開啟對應埠的防火牆
- 6port=設定的埠
- 7#建立TCP協議連線,這時候伺服器就會監聽到到連線請求,並開始等待接受client傳送的資料
- 8client.connect((host,port))
- 9#建立連線後,伺服器端會返回連線成功訊息
- 10start_msg=client.recv(1024)
- 11print(start_msg.decode('utf-8'))
- 12#開啟一個執行緒用來接受伺服器發來的訊息
- 13t=threading.Thread(target=recvlink,args=(client,))
- 14t.start()
- 15p=ImageGrab.grab()#獲得當前螢幕
- 16quality=25#影象的質量
- 17encode_param=[int(cv2.IMWRITE_JPEG_QUALITY),quality]
- 18whileTrue:
- 19im=ImageGrab.grab()
- 20imm=cv2.cvtColor(np.array(im),cv2.COLOR_RGB2BGR)#轉為opencv的BGR格式
- 21imm=cv2.resize(imm,(1535,863))
- 22img_encode=cv2.imencode(".jpg",imm,encode_param)[1]
- 23data_encode=np.array(img_encode)
- 24str_encode=data_encode.tostring()
- 25#print(len(str_encode))
- 26#輸入要傳送的資訊
- 27sendmsg="kehu"
- 28#向伺服器傳送訊息
- 29client.send(str_encode)
- 30ifsendmsg=='quit':
- 31break
- 32#結束時關閉客戶端
- 33client.close()
1.3 服務端講解
服務端指的是用來控制遠端電腦的那一端,為了方便使用,我們直接在伺服器上使用即可。
(1)匯入使用到的模組:
- 1#伺服器端
- 2importsocket
- 3importthreading
- 4importnumpyasnp
- 5importcv2
- 6importos
(2)建立滑鼠點選事件函式,用來獲取滑鼠點選的位置座標:
- 1print("等待連線---")
- 2defmouse_click(event,x,y,flags,para):
- 3ifevent==cv2.EVENT_LBUTTONDOWN:#左邊滑鼠點選
- 4f=open("1.txt","w")
- 5f.write(str(x)+","+str(y))
- 6f.close()
(3)建立伺服器端接收資料函式,用來實時接收傳輸過來的影象資料並顯示:
- 1defrecv_msg(clientsocket):
- 2whileTrue:
- 3#接受客戶端訊息,設定一次最多接受10240位元組的資料
- 4recv_msg=clientsocket.recv(102400)
- 5#把接收到的東西解碼
- 6msg=np.fromstring(recv_msg,np.uint8)
- 7img_decode=cv2.imdecode(msg,cv2.IMREAD_COLOR)
- 8try:
- 9s=img_decode.shape
- 10img_decode=img_decode
- 11temp=img_decode
- 12except:
- 13img_decode=temp
- 14pass
- 15cv2.imshow('SERVER',img_decode)
- 16cv2.setMouseCallback("SERVER",mouse_click)
- 17try:
- 18f=open("1.txt")
- 19txt=f.read()
- 20f.close()
- 21reply=txt
- 22print(reply)
- 23clientsocket.send(reply.encode('utf-8'))
- 24os.remove("1.txt")
- 25except:
- 26pass
- 27ifcv2.waitKey(1)&0xFF==ord('q'):
- 28break
(4)主函式,用來建立連線和資料接收等功能。
- 1defmain():
- 2socket_server=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
- 3host='伺服器的本地ip'
- 4#設定被監聽的埠號,小於1024的埠號不能使用
- 5port=設定的埠
- 6socket_server.bind((host,port))
- 7#設定最大監聽數,也就是最多可以同時響應幾個客戶端請求,一般配合多執行緒使用
- 8socket_server.listen(5)
- 9#等待客戶端連線,一旦有了連線就立刻向下執行,否則等待
- 10#accept()函式會返回一個元組,第一個元素是客戶端socket物件,第二個元素是客戶端地址(ip地址+埠號)
- 11clientsocket,addr=socket_server.accept()
- 12#有了客戶端連線後之後才能執行以下程式碼,我們先向客戶端傳送連線成功訊息
- 13clientsocket.send('連線成功'.encode('utf-8'))
- 14#和客戶端一樣開啟一個執行緒接受客戶端的資訊
- 15t=threading.Thread(target=recv_msg,args=(clientsocket,))
- 16t.start()
02
遠端控制GUI視窗
遠控桌面GUI主要是為了美觀而用,需要大家根據遠端程式碼進行集合修改。當然單獨使用上述程式碼已經可以實現功能了,只是不夠美觀。由於考慮到此處程式碼量較大,且不是重點,故粗略講解
(1)匯入相關庫:
- 1fromPyQt5.QtWidgetsimport*
- 2fromPyQt5.QtCoreimport*
- 3fromPyQt5.QtGuiimportQPalette,QBrush,QPixmap
- 4importos
- 5importsocket
- 6importthreading
- 7importcv2
- 8importnumpyasnp
- 9fromPILimportImageGrab
- 10frompynput.mouseimportButton,Controller
- 11importtime
(2)建立滑鼠控制函式和點選函式
- 1m=Controller()
- 2defmouse_click(event,x,y,flags,para):
- 3ifevent==cv2.EVENT_LBUTTONDOWN:#左邊滑鼠點選
- 4print(x,y)
- 5m.position=(x,y)
- 6time.sleep(0.1)
- 7m.click(Button.left,1)
(3)GUI介面初始化,由於我們需要把實時的視訊顯示在視窗上,故也需要使用到opencv。
- 1def__init__(self,parent=None):
- 2super(Ui_MainWindow,self).__init__(parent)
- 3#self.face_recong=face.Recognition()
- 4self.timer_camera=QtCore.QTimer()
- 5self.cap=cv2.VideoCapture()
- 6self.CAM_NUM=0
- 7self.set_ui()
- 8self.slot_init()
- 9self.__flag_work=0
- 10self.x=0
- 11self.count=0
(4)設定視窗大小和控制元件位置等資訊。建立佈局和設定名稱
- 1defset_ui(self):
- 2self.__layout_main=QtWidgets.QHBoxLayout()
- 3self.__layout_fun_button=QtWidgets.QVBoxLayout()
- 4self.__layout_data_show=QtWidgets.QVBoxLayout()
- 5self.button_open_camera=QtWidgets.QPushButton(u'遠端桌面')
- 6self.button_close=QtWidgets.QPushButton(u'退出')
- 7#Button的顏色修改
- 8button_color=[self.button_open_camera,self.button_close]
- 9foriinrange(2):
- 10button_color[i].setStyleSheet("QPushButton{color:black}"
- 11"QPushButton:hover{color:red}"
- 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}"
- 14"QPushButton{border-radius:10px}"
- 15"QPushButton{padding:2px4px}")
- 16self.button_open_camera.setMinimumHeight(50)
- 17self.button_close.setMinimumHeight(50)
- 18# move()方法移動視窗在螢幕上的位置到x = 300,y = 300座標。
- 19self.move(500,500)
- 20#資訊顯示
- 21self.label_show_camera=QtWidgets.QLabel()
- 22self.label_move=QtWidgets.QLabel()
- 23self.label_move.setFixedSize(100,100)
- 24self.label_show_camera.setFixedSize(1530,863)
- 25self.label_show_camera.setAutoFillBackground(False)
- 26self.__layout_fun_button.addWidget(self.button_open_camera)
- 27self.__layout_fun_button.addWidget(self.button_close)
- 28self.__layout_fun_button.addWidget(self.label_move)
- 29self.__layout_main.addLayout(self.__layout_fun_button)
- 30self.__layout_main.addWidget(self.label_show_camera)
- 31self.setLayout(self.__layout_main)
- 32self.label_move.raise_()
- 33self.setWindowTitle(u'遠控桌面GUI')
- 34'''
- 35#設定背景圖片
- 36palette1=QPalette()
- 37palette1.setBrush(self.backgroundRole(),QBrush(QPixmap('background.jpg')))
- 38self.setPalette(palette1)
- 39'''
(5)獲取滑鼠點選時的座標:
- 1defmousePressEvent(self,event):
- 2ifevent.buttons()&QtCore.Qt.LeftButton:
- 3x=event.x()-120
- 4y=event.y()-10
- 5text="x:{0},y:{1}".format(x,y)
- 6ifx>=0andy>=0:
- 7m.position=(x,y)
- 8time.sleep(0.1)
- 9m.click(Button.left,1)
- 10print(text)
(6)按鈕繫結所設定的函式:
- 1defslot_init(self):
- 2self.button_open_camera.clicked.connect(self.button_open_camera_click)
- 3self.timer_camera.timeout.connect(self.show_camera)
- 4self.button_close.clicked.connect(self.close)
(7)顯示桌面功能函式,並設定點選時修改名稱,可以隨時關閉桌面
- 1defbutton_open_camera_click(self):
- 2ifself.timer_camera.isActive()==False:
- 3self.timer_camera.start(30)
- 4self.button_open_camera.setText(u'關閉')
- 5else:
- 6self.timer_camera.stop()
- 7self.cap.release()
- 8self.label_show_camera.clear()
- 9self.button_open_camera.setText(u'遠端桌面')
(8)顯示桌面函式和退出程式函式
- 1defshow_camera(self):
- 2im=ImageGrab.grab()
- 3imm=cv2.cvtColor(np.array(im),cv2.COLOR_RGB2BGR)#轉為opencv的BGR格式
- 4#imm=cv2.resize(imm,(1535,863))
- 5self.image=imm
- 6#face=self.face_detect.align(self.image)
- 7#ifface:
- 8#pass
- 9show=cv2.resize(self.image,(1536,863))
- 10show=cv2.cvtColor(show,cv2.COLOR_BGR2RGB)
- 11print(show.shape[1],show.shape[0])
- 12#show.shape[1]=640,show.shape[0]=480
- 13showImage=QtGui.QImage(show.data,show.shape[1],show.shape[0],QtGui.QImage.Format_RGB888)
- 14self.label_show_camera.setPixmap(QtGui.QPixmap.fromImage(showImage))
- 15#cv2.setMouseCallback(showImage,mouse_click)
- 16#self.x+=1
- 17#self.label_move.move(self.x,100)
- 18#ifself.x==320:
- 19#self.label_show_camera.raise_()
- 20defcloseEvent(self,event):
- 21ok=QtWidgets.QPushButton()
- 22cacel=QtWidgets.QPushButton()
- 23msg=QtWidgets.QMessageBox(QtWidgets.QMessageBox.Warning,u"關閉",u"是否關閉!")
- 24msg.addButton(ok,QtWidgets.QMessageBox.ActionRole)
- 25msg.addButton(cacel,QtWidgets.QMessageBox.RejectRole)
- 26ok.setText(u'確定')
- 27cacel.setText(u'取消')
- 28#msg.setDetailedText('sdfsdff')
- 29ifmsg.exec_()==QtWidgets.QMessageBox.RejectRole:
- 30event.ignore()
- 31else:
- 32#self.socket_client.send_command(self.socket_client.current_user_command)
- 33ifself.cap.isOpened():
- 34self.cap.release()
- 35ifself.timer_camera.isActive():
- 36self.timer_camera.stop()
- 37event.accept()