Skip to content
This repository was archived by the owner on Mar 15, 2024. It is now read-only.

Commit 85df62d

Browse files
Use kivy for client app
1 parent e7b517c commit 85df62d

File tree

2 files changed

+133
-33
lines changed

2 files changed

+133
-33
lines changed

client.py

Lines changed: 122 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,60 @@
11
# standard imports
22
from socket import socket
33

4+
# kivy imports
5+
from kivy.config import Config
6+
from kivy.core.window import Window
7+
from kivy.app import App
8+
from kivy.lang import Builder
9+
from kivy.uix.boxlayout import BoxLayout
10+
from kivy.uix.gridlayout import GridLayout
11+
from kivy.clock import Clock
12+
from kivy.uix.popup import Popup
13+
from kivy.uix.label import Label
14+
from kivy.uix.textinput import TextInput
15+
from kivy.uix.button import Button
16+
417
# lib imports
518
from lz4.frame import decompress
619
import pygame
720

8-
# this display to show on
9-
display = 0
21+
Config.set('graphics', 'resizable', 0)
22+
Window.size = (1280, 720)
1023

11-
# todo - allow different resolution than server output
12-
WIDTH = 1920
13-
HEIGHT = 1080
24+
kv = '''
25+
Main:
26+
BoxLayout:
27+
orientation: 'vertical'
28+
padding: root.width * 0.05, root.height * .05
29+
spacing: '5dp'
30+
BoxLayout:
31+
size_hint: [1,.85]
32+
Image:
33+
id: image
34+
source: 'foo.png'
35+
BoxLayout:
36+
size_hint: [1,.15]
37+
GridLayout:
38+
cols: 3
39+
spacing: '10dp'
40+
Button:
41+
id: status
42+
text:'Play'
43+
bold: True
44+
on_press: root.play_pause()
45+
Button:
46+
text: 'Close'
47+
bold: True
48+
on_press: root.close()
49+
Button:
50+
text: 'Setting'
51+
bold: True
52+
on_press: root.setting()
53+
'''
1454

1555

1656
def recv_all(conn, length):
17-
""" Retreive all pixels. """
57+
"""Retrieve all pixels."""
1858

1959
buf = b''
2060
while len(buf) < length:
@@ -25,36 +65,85 @@ def recv_all(conn, length):
2565
return buf
2666

2767

28-
def main(host='127.0.0.1', port=5000):
29-
pygame.init()
30-
screen = pygame.display.set_mode(size=(WIDTH, HEIGHT), display=display)
31-
clock = pygame.time.Clock()
32-
watching = True
68+
class Main(BoxLayout):
69+
def __init__(self, **kwargs):
70+
super().__init__(**kwargs)
71+
self.server_address = None
72+
self.server_port = None
73+
self.server_input = None
74+
self.port_input = None
75+
self.popup = None
76+
self.sock = socket()
77+
78+
def play_pause(self):
79+
if not self.server_address or not self.server_port:
80+
box = GridLayout(cols=1)
81+
box.add_widget(Label(text="Ip or Port Not Set"))
82+
btn = Button(text="OK")
83+
btn.bind(on_press=self.close_popup)
84+
box.add_widget(btn)
85+
self.popup = Popup(title='Error', content=box, size_hint=(.8, .3))
86+
self.popup.open()
87+
else:
88+
if self.ids.status.text == "Stop":
89+
self.stop()
90+
else:
91+
self.sock = socket()
92+
self.sock.connect((self.server_address, self.server_port))
93+
self.ids.status.text = "Stop"
94+
Clock.schedule_interval(self.recv, 0.05)
95+
96+
def close_popup(self, btn):
97+
self.popup.dismiss()
98+
99+
def stop(self):
100+
Clock.unschedule(self.recv)
101+
self.sock.close()
102+
self.ids.status.text = "Play"
103+
104+
def recv(self, dt):
105+
size_len = int.from_bytes(self.sock.recv(1), byteorder='big')
106+
size = int.from_bytes(self.sock.recv(size_len), byteorder='big')
107+
pixels = decompress(recv_all(self.sock, size))
108+
109+
image = pygame.image.fromstring(pixels, (1920, 1080), "RGB") # convert received image from string
110+
111+
try:
112+
pygame.image.save(image, "foo.png")
113+
self.ids.image.reload()
114+
except:
115+
pass
116+
117+
def close(self):
118+
App.get_running_app().stop()
33119

34-
sock = socket()
35-
sock.connect((host, port))
36-
try:
37-
while watching:
38-
for event in pygame.event.get():
39-
if event.type == pygame.QUIT:
40-
watching = False
41-
break
120+
def setting(self):
121+
box = GridLayout(cols=2)
122+
box.add_widget(Label(text="IpAddress: ", bold=True))
123+
self.server_input = TextInput()
124+
box.add_widget(self.server_input)
125+
box.add_widget(Label(text="Port: ", bold=True))
126+
self.port_input = TextInput()
127+
box.add_widget(self.port_input)
128+
btn = Button(text="Set", bold=True)
129+
btn.bind(on_press=self.setting_save)
130+
box.add_widget(btn)
131+
self.popup = Popup(title='Settings', content=box, size_hint=(.6, .4))
132+
self.popup.open()
42133

43-
# Retrieve the size of the pixels length, the pixels length and pixels
44-
size_len = int.from_bytes(sock.recv(1), byteorder='big')
45-
size = int.from_bytes(sock.recv(size_len), byteorder='big')
46-
pixels = decompress(recv_all(sock, size))
134+
def setting_save(self, btn):
135+
# todo - validate ip address and port
136+
try:
137+
self.server_address = self.server_input.text
138+
self.server_port = int(self.port_input.text)
139+
except:
140+
pass
141+
self.popup.dismiss()
47142

48-
# Create the Surface from raw pixels
49-
img = pygame.image.fromstring(pixels, (WIDTH, HEIGHT), 'RGB')
50143

51-
# Display the picture
52-
screen.blit(img, (0, 0))
53-
pygame.display.flip()
54-
clock.tick(60)
55-
finally:
56-
sock.close()
144+
class PyStreamApp(App):
145+
def build(self):
146+
return Builder.load_string(kv)
57147

58148

59-
if __name__ == '__main__':
60-
main()
149+
PyStreamApp().run()

requirements.txt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,17 @@
1+
# client app
2+
Kivy==2.1.0
3+
kivymd==0.104.2
4+
5+
# compression
16
lz4==4.0.1
7+
8+
# capture
29
mss==6.1.0
310
numpy==1.23.0
411
Pillow==9.1.1
512
pygame==2.1.2
613
git+https://github.com/ra1nty/DXcam#egg=dxcam
14+
15+
# control and input
16+
pyautogui==0.9.53 # can be used to control mouse and keyboard on server
17+
# https://pyautogui.readthedocs.io/en/latest/index.html

0 commit comments

Comments
 (0)