Skip to content

Commit c0f9c9a

Browse files
committed
update 5.3
# Important Feature 🔥 Finally! Now you can use arrow key for navigation cursor for edit or remove in-line. # New Feature - **Division** system for XHandler for separate one file multi-functions to multi-file functions and for easy to maintenance. - **VirtualSTD** (VStream) working like sys.stdin, sys.stdout, sys.stderr. - **Dialog+** (Beta) improved and more feature from **dialog**. - **AdvancedInput** is input system for input keyboard and mouse (Press/Release, Wheel, Drag, Move)
1 parent ad99033 commit c0f9c9a

30 files changed

Lines changed: 3601 additions & 1004 deletions

demo/demo1.py

Lines changed: 0 additions & 477 deletions
This file was deleted.

demo/demo2.py

Lines changed: 0 additions & 29 deletions
This file was deleted.

demo/divisions/barplus_test.py

Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
import random
2+
import time
3+
4+
from PyserSSH import Clear
5+
from PyserSSH.extensions.BarPlus import BarPlus_Display, BarPlus_ProgressBar
6+
from PyserSSH.extensions.XHandler import Division
7+
from PyserSSH.extensions.dialog import TextInputDialog, TextDialog
8+
from PyserSSH.extensions.processbar import Steps
9+
from PyserSSH.system.clientype import Client
10+
11+
div = Division("barplus", "Test command for bar+ extension module", category="Test Function (Bar+)")
12+
13+
@div.command(name="barplus")
14+
def xh_barplus(client: Client):
15+
running = True
16+
17+
def onkey(key):
18+
nonlocal running
19+
20+
if key == b"q":
21+
running = False
22+
23+
Clear(client, only_current_screen=True)
24+
25+
display = BarPlus_Display(client, "BarPlus Test Display", update_rate=0.1)
26+
27+
display.on_key_handle = onkey
28+
29+
display.add_static_line("Press Q to exit")
30+
# Create progress bars
31+
32+
# System 13 - Single bar (red)
33+
system13 = BarPlus_ProgressBar("System 13", width=30)
34+
system13.add_segment(0, 500, "255;0;0", "Processing")
35+
system13.set_info("")
36+
37+
# System 14 - Stacked bar with multiple segments
38+
system14 = BarPlus_ProgressBar("System 14", width=30, steps_animation=Steps.receiving, ena_stack_layer=True)
39+
system14.add_segment(0, 200, "0;255;0", "Complete")
40+
system14.add_segment(0, 150, "255;255;0", "Processing", layer=1)
41+
system14.add_segment(0, 100, "255;0;0", "Pending", layer=2)
42+
system14.set_info("")
43+
44+
system15 = BarPlus_ProgressBar("System 15", width=30, steps_animation=Steps.connecting)
45+
system15.add_segment(0, 300, "0;247;247", "Base") # Base layer
46+
system15.add_segment(0, 200, "255;255;0", "Buffer") # Buffer layer (can override base)
47+
system15.add_segment(0, 100, "255;0;0", "Current") # Current layer (highest priority)
48+
system15.set_info("")
49+
50+
# System 15 - Different colors
51+
system16 = BarPlus_ProgressBar("System 16", width=30, steps_animation=Steps.requesting)
52+
system16.add_segment(0, 300, "0;247;247", "Active")
53+
system16.add_segment(0, 100, "247;0;247", "Queue")
54+
system16.set_info("")
55+
56+
# System 16 - Small bar (blue)
57+
system17 = BarPlus_ProgressBar("System 17", width=30, steps_animation=Steps.waiting)
58+
system17.add_segment(0, 150, "0;0;255", "Tasks")
59+
system17.set_info("")
60+
61+
# Add bars to display
62+
display.add_progress_bar(system13)
63+
display.add_progress_bar(system14)
64+
display.add_progress_bar(system15)
65+
display.add_progress_bar(system16)
66+
display.add_progress_bar(system17)
67+
68+
display.start()
69+
70+
step = 0
71+
while True:
72+
# Update System 13
73+
system13.update_segment(0, min(step * 2, 500))
74+
75+
# Update System 14 (stacked)
76+
system14.update_segment(0, min(step * 1, 200)) # Complete
77+
system14.update_segment(1, min(step * 1, 150)) # Processing
78+
system14.update_segment(2, min(step * 1, 100)) # Pending
79+
80+
# Update System 15 (stacked)
81+
system16.update_segment(0, min(step * 4, 300)) # Active
82+
system16.update_segment(1, min(step * 1, 100)) # Queue
83+
84+
system15.update_segment(0, min(step * 4, 300)) # Base (cyan)
85+
system15.update_segment(1, min(step * 3, 200)) # Buffer (yellow) - overrides cyan
86+
system15.update_segment(2, min(step * 2, 100)) # Current (red) - overrides everything
87+
88+
# Update System 16
89+
system17.update_segment(0, min(step * 3, 150))
90+
91+
time.sleep(0.01)
92+
step += 1
93+
94+
if step > 250 or not running: # Reset after completion
95+
system13.stop()
96+
system14.stopfail()
97+
system15.stop()
98+
system16.stopfail()
99+
system17.stop()
100+
101+
time.sleep(0.1)
102+
103+
display.exit()
104+
break
105+
106+
class PID:
107+
def __init__(self, kp, ki, kd):
108+
self.kp = kp
109+
self.ki = ki
110+
self.kd = kd
111+
self.prev_error = 0
112+
self.integral = 0
113+
114+
def compute(self, target, actual):
115+
error = target - actual
116+
self.integral += error
117+
derivative = error - self.prev_error
118+
output = self.kp * error + self.ki * self.integral + self.kd * derivative
119+
self.prev_error = error
120+
return output
121+
122+
@div.command(name="engpid")
123+
def xh_engpid(client: Client):
124+
running = True
125+
max_rpm = 7000
126+
target_rpm = 3000
127+
128+
display = BarPlus_Display(client, "Engine RPM Controller", update_rate=0.1)
129+
pid = PID(kp=0.004, ki=0.003, kd=0.0)
130+
131+
def onkey(key):
132+
nonlocal running, target_rpm
133+
134+
if key == b"q":
135+
running = False
136+
elif key == b"w":
137+
target_rpm = min(target_rpm + 100, max_rpm)
138+
rpm_bar.update_segment(1, int(target_rpm))
139+
elif key == b"s":
140+
target_rpm = max(target_rpm - 100, 0)
141+
rpm_bar.update_segment(1, int(target_rpm))
142+
elif key == b"p":
143+
display.is_paused = True
144+
DiR = TextInputDialog(client, "PID Settings", "Enter new Kp value (Float Number)")
145+
DiR.render()
146+
try:
147+
pid.kp = float(DiR.output())
148+
Dii = TextDialog(client, f"New Kp value set to {pid.kp}", "PID Settings", exit_key=None)
149+
Dii.render()
150+
except:
151+
Dii = TextDialog(client, "Invalid input. Please enter a valid float number.", "PID Settings", exit_key=None)
152+
Dii.render()
153+
display.is_paused = False
154+
elif key == b"i":
155+
display.is_paused = True
156+
DiR = TextInputDialog(client, "PID Settings", "Enter new Ki value (Float Number)")
157+
DiR.render()
158+
try:
159+
pid.ki = float(DiR.output())
160+
Dii = TextDialog(client, f"New Ki value set to {pid.ki}", "PID Settings", exit_key=None)
161+
Dii.render()
162+
except:
163+
Dii = TextDialog(client, "Invalid input. Please enter a valid float number.", "PID Settings", exit_key=None)
164+
Dii.render()
165+
display.is_paused = False
166+
elif key == b"d":
167+
display.is_paused = True
168+
DiR = TextInputDialog(client, "PID Settings", "Enter new Kd value (Float Number)")
169+
DiR.render()
170+
try:
171+
pid.kd = float(DiR.output())
172+
Dii = TextDialog(client, f"New Kd value set to {pid.kd}", "PID Settings", exit_key=None)
173+
Dii.render()
174+
except:
175+
Dii = TextDialog(client, "Invalid input. Please enter a valid float number.", "PID Settings", exit_key=None)
176+
Dii.render()
177+
display.is_paused = False
178+
179+
Clear(client, only_current_screen=True)
180+
181+
display.on_key_handle = onkey
182+
183+
display.add_static_line("Press Q to exit, W to increase target RPM, S to decrease target RPM")
184+
display.add_static_line("Press P to change Kp, I to change Ki, D to change Kd")
185+
186+
rpm_bar = BarPlus_ProgressBar("RPM (Revving)", width=40, steps_animation=Steps.requesting, ena_stack_layer=True)
187+
188+
rpm_bar.add_segment(0, max_rpm, "255;0;0", "Current RPM")
189+
rpm_bar.add_segment(0, max_rpm, "255;255;0", "Target RPM", layer=1)
190+
191+
rpm_bar.set_info("")
192+
display.add_progress_bar(rpm_bar)
193+
194+
actual_rpm = 0
195+
throttle = 0
196+
197+
rpm_bar.update_segment(1, int(target_rpm))
198+
199+
display.start()
200+
201+
while True:
202+
# PID logic
203+
correction = pid.compute(target_rpm, actual_rpm)
204+
throttle += correction
205+
throttle = max(0, min(100, throttle))
206+
207+
# Simulate engine RPM + noise
208+
rpm_change = throttle * 80
209+
noise = random.uniform(-150, 150)
210+
actual_rpm = rpm_change + noise
211+
actual_rpm = max(0, min(max_rpm, actual_rpm))
212+
213+
# Update progress bar
214+
rpm_bar.update_segment(0, int(actual_rpm))
215+
216+
time.sleep(0.01)
217+
218+
if not running: # Reset after completion
219+
display.exit()
220+
break

demo/divisions/dialog_test.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import requests
2+
from bs4 import BeautifulSoup
3+
4+
from PyserSSH import Send
5+
from PyserSSH.extensions.XHandler import Division
6+
from PyserSSH.extensions.dialog import TextDialog, TextInputDialog, MenuDialog
7+
from PyserSSH.extensions.processbar import indeterminateStatus
8+
from PyserSSH.system.clientype import Client
9+
10+
div = Division("dialog", "Test command for dialog extension module", category="Test Function (Dialog)")
11+
12+
@div.command(name="dialogtest")
13+
def xh_dialogtest(client: Client):
14+
Di1 = TextDialog(client, "Hello Dialog!", "PyserSSH Extension")
15+
Di1.render()
16+
17+
@div.command(name="dialogtest2")
18+
def xh_dialogtest2(client: Client):
19+
Di2 = MenuDialog(client, ["H1", "H2", "H3"], "PyserSSH Extension", "Hello world")
20+
Di2.render()
21+
Send(client, f"selected index: {Di2.output()}")
22+
23+
@div.command(name="dialogtest3")
24+
def xh_dialogtest3(client: Client):
25+
Di3 = TextInputDialog(client, "PyserSSH Extension")
26+
Di3.render()
27+
Send(client, f"input: {Di3.output()}")
28+
29+
@div.command(name="passdialogtest3")
30+
def xh_passdialogtest3(client: Client):
31+
Di3 = TextInputDialog(client, "PyserSSH Extension", inputtitle="Password Here", password=True)
32+
Di3.render()
33+
Send(client, f"password: {Di3.output()}")
34+
35+
@div.command(name="vieweb")
36+
def xh_vieweb(client: Client, url: str):
37+
loading = indeterminateStatus(client, desc=f"requesting {url}...")
38+
loading.start()
39+
try:
40+
content = requests.get(url).content
41+
except:
42+
loading.stopfail()
43+
return
44+
loading.stop()
45+
loading = indeterminateStatus(client, desc=f"parsing html {url}...")
46+
loading.start()
47+
try:
48+
soup = BeautifulSoup(content, 'html.parser')
49+
# Extract only the text content
50+
text_content = soup.get_text()
51+
except:
52+
loading.stopfail()
53+
return
54+
loading.stop()
55+
Di1 = TextDialog(client, text_content, url)
56+
Di1.render()

demo/divisions/dialogplus_test.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
from PyserSSH.extensions.XHandler import Division
2+
from PyserSSH.extensions.dialogplus import Window, TextWidget, Dialog, MenuWidget, TextInputWidget
3+
from PyserSSH.system.clientype import Client
4+
5+
div = Division("dialogplus", "Test command for dialog+ extension module", category="Test Function (Dialog+)")
6+
7+
@div.command("dialogplus")
8+
def dialogplus_text(client: Client):
9+
window = Window(title="Information", border=True)
10+
window.add_widget(TextWidget(text="Hello World!"), x=0, y=0)
11+
12+
dialog = Dialog(client, window)
13+
dialog.show()
14+
15+
@div.command("dialogplus_menu")
16+
def dialogplus_menu(client: Client):
17+
window = Window(title="Choose Option", border=True)
18+
19+
description = "Please choose the options"
20+
21+
window.add_widget(TextWidget(text=description), x=0, y=0)
22+
menu_y = len(description.split('\n')) + 1
23+
24+
window.add_widget(MenuWidget(items=["Option 1", "Option 2", "Option 3"]), x=0, y=menu_y)
25+
26+
dialog = Dialog(client, window)
27+
result = dialog.show()
28+
if result:
29+
client.sendln(f"Selected: {result['item']} at index {result['index']}")
30+
31+
@div.command("dialogplus_input")
32+
def dialogplus_input(client: Client):
33+
window = Window(title="User Input", border=True)
34+
35+
window.add_widget(TextWidget(text="Enter your name:"), x=0, y=0)
36+
window.add_widget(TextInputWidget(password=False, max_length=None), x=0, y=2)
37+
38+
dialog = Dialog(client, window)
39+
result = dialog.show()
40+
41+
if result:
42+
client.sendln(f"Selected: {result['item']} at index {result['index']}")
43+
44+
@div.command("dialogplus_form")
45+
def dialogplus_form(client: Client):
46+
window = Window(title="User Registration Form", border=True)
47+
48+
# Add form fields
49+
window.add_widget(TextWidget(text="Please fill out the registration form:"), x=0, y=0)
50+
51+
window.add_widget(TextWidget(text="Username:"), x=0, y=2)
52+
username_input = window.add_widget(TextInputWidget(placeholder="Enter username"), x=0, y=3)
53+
54+
window.add_widget(TextWidget(text="Password:"), x=0, y=5)
55+
password_input = window.add_widget(TextInputWidget(password=True, placeholder="Enter password"), x=0, y=6)
56+
57+
window.add_widget(TextWidget(text="User Type:"), x=0, y=8)
58+
user_type_menu = window.add_widget(MenuWidget(items=["Regular User", "Administrator", "Guest"]), x=0, y=9)
59+
60+
# Custom dialog handling
61+
dialog = Dialog(client, window)
62+
result = dialog.show()
63+
64+
if result:
65+
# Collect all form data
66+
form_data = {
67+
'username': username_input.get_text(),
68+
'password': password_input.get_text(),
69+
'user_type': user_type_menu.get_selected_item()
70+
}
71+
client.sendln(str(form_data))
72+

0 commit comments

Comments
 (0)