I need help with my Python desktop app.
The issue is that I have set up the clipboard_button
from the main.py to trigger the send_message_test
function in the Server class. The function has clearly run since the output from the two print functions ([server] ran send_message_test
and [server] done running send_message_test
) appears, and the self.connected_sid
that is also printed out appears to valid and correct.
However, the self.sio.emit("test", "test", room=self.connected_sid)
doesn't seem to do anything, the connection is valid, and the file-ack emit works just fine! I already searched everywhere for a solution and tried many different ways, but none seemed to help.
main.py:
import sys
import threading
import time
import json
import qrcode
import pyperclip
from PIL.ImageQt import ImageQt
from PySide6.QtCore import (
Qt, Property, QEasingCurve, QPropertyAnimation, Signal, QSize
)
from PySide6.QtGui import QCursor, QColor, QEnterEvent, QFont, QFontDatabase, QIcon, QPixmap
from PySide6.QtWidgets import (
QApplication, QWidget, QPushButton, QLabel,
QHBoxLayout, QVBoxLayout
)
from server import Server
from win10toast import ToastNotifier
from PySide6.QtWidgets import QLabel
toaster = ToastNotifier()
class AnimatedButton(QPushButton):
def __init__(self, text):
super().__init__(text)
self._color = QColor("#4C4C4C")
self.setStyleSheet(f"background-color: {self._color.name()}")
self.animation = QPropertyAnimation(self, b"bgColor")
self.animation.setDuration(140)
self.animation.setEasingCurve(QEasingCurve.InOutQuad)
def enterEvent(self, event: QEnterEvent):
self.animate_to(QColor("#2D2D2D"))
super().enterEvent(event)
def leaveEvent(self, event):
self.animate_to(QColor("#4C4C4C"))
super().leaveEvent(event)
def animate_to(self, color):
self.animation.stop()
self.animation.setStartValue(self._color)
self.animation.setEndValue(color)
self.animation.start()
def get_bg_color(self):
return self._color
def set_bg_color(self, color):
self._color = color
self.setStyleSheet(f"background-color: {color.name()};")
bgColor = Property(QColor, get_bg_color, set_bg_color)
class MainWindow(QWidget):
file_received_signal = Signal(dict)
clipboard_received_signal = Signal(str)
text_received_signal = Signal(str)
test_button_clicked_signal = Signal(any)
def __init__(self):
super().__init__()
self.server = server
self.setWindowTitle("Connection Button")
self.setFixedSize(846, 520)
# Status bar unfinished
# self.status_bar = QWidget()
# self.status_bar.setObjectName("status_bar")
# self.status_bar.setFixedSize(758, 112)
# self.status_bar_layout = QHBoxLayout(self.status_bar)
# self.status_label = QLabel("Status: Waiting...")
# self.status_label.setAlignment(Qt.AlignCenter)
# self.status_bar_layout.addWidget(self.status_label)
# Clipboard button
self.clipboard_button = AnimatedButton("Clipboard")
self.clipboard_button.setObjectName("clipboardButton")
self.clipboard_button.setFixedSize(352, 112)
self.clipboard_button.setCursor(QCursor(Qt.PointingHandCursor))
self.clipboard_button.setIcon(QIcon("assets/icons/Paperclip.svg"))
self.clipboard_button.setIconSize(QSize(32, 32))
self.clipboard_button.setLayoutDirection(Qt.RightToLeft)
# Filesharing button
self.filesharing_button = AnimatedButton("Filesharing")
self.filesharing_button.setObjectName("filesharingButton")
self.filesharing_button.setFixedSize(352, 112)
self.filesharing_button.setCursor(QCursor(Qt.PointingHandCursor))
self.filesharing_button.setIcon(QIcon("assets/icons/file.svg"))
self.filesharing_button.setIconSize(QSize(32, 32))
self.filesharing_button.setLayoutDirection(Qt.RightToLeft)
# Share text button
self.text_button = AnimatedButton("Share text")
self.text_button.setObjectName("textButton")
self.text_button.setFixedSize(352, 112)
self.text_button.setCursor(QCursor(Qt.PointingHandCursor))
self.text_button.setIcon(QIcon("assets/icons/placeholder.svg"))
self.text_button.setIconSize(QSize(32, 32))
self.text_button.setLayoutDirection(Qt.RightToLeft)
# QR box
self.qrbox = QWidget()
self.qrbox.setObjectName("box")
self.qrbox.setFixedSize(382, 382)
self.qrbox_layout = QVBoxLayout(self.qrbox)
# Layout
buttons_layout = QVBoxLayout()
buttons_layout.addWidget(self.clipboard_button)
buttons_layout.addWidget(self.filesharing_button)
buttons_layout.addWidget(self.text_button)
buttons_layout.setSpacing(24)
button_box_layout = QHBoxLayout()
button_box_layout.addWidget(self.qrbox)
button_box_layout.addLayout(buttons_layout)
button_box_layout.setSpacing(24)
button_box_layout.setAlignment(Qt.AlignCenter)
layout = QVBoxLayout(self)
# layout.addWidget(self.status_bar)
layout.setAlignment(Qt.AlignCenter)
layout.addSpacing(24)
layout.addLayout(button_box_layout)
#signals
self.file_received_signal.connect(self.handle_file)
self.clipboard_received_signal.connect(self.handle_clipboard)
self.text_received_signal.connect(self.handle_text)
self.test_button_clicked_signal.connect(self.server.send_message_test)
self.clipboard_button.clicked.connect(self.test_button_clicked_signal)
# self.clipboard_button.clicked.connect(self.text_received_signal)
def send_clipboard_to_phone(self):
text = pyperclip.paste()
if text:
# self.server.send_clipboard(text=text)
pass
else:
print("clipboard is empty")
def handle_callback(self, msg_type, content):
if msg_type == "file":
self.file_received_signal.emit(content)
elif msg_type == "clipboard":
self.clipboard_received_signal.emit(content)
elif msg_type == "text":
self.text_received_signal.emit(content)
else:
print(f"Unknown type: {msg_type}")
def handle_file(self, file_info):
fname = file_info.get("filename", "unknown")
ftype = file_info.get("filetype", "unknown")
toaster.show_toast(f"Received {ftype}", fname)
def handle_clipboard(self, text):
pyperclip.copy(text)
toaster.show_toast("Copied to clipboard", text)
while toaster.notification_active(): time.sleep(0.1)
def handle_text(self, text):
print(f"UI: Text received - {text}")
def update_qr(self, data: dict,
fill_color: str = "#000",
back_color: str = "#000"):
for i in reversed(range(self.qrbox_layout.count())):
w = self.qrbox_layout.itemAt(i).widget()
if w:
w.setParent(None)
payload = json.dumps(data, separators=(',', ':'))
qr = qrcode.QRCode(
version=1, error_correction=qrcode.constants.ERROR_CORRECT_L,
box_size=10, border=4
)
qr.add_data(payload)
qr.make(fit=True)
pil_img = qr.make_image(fill_color=fill_color, back_color=back_color).convert("RGB")
qt_img = ImageQt(pil_img)
pix = QPixmap.fromImage(qt_img)
lbl = QLabel(alignment=Qt.AlignCenter)
lbl.setPixmap(pix.scaled(400, 400, Qt.KeepAspectRatio))
lbl.setStyleSheet("background: transparent;")
self.qrbox_layout.addWidget(lbl)
if __name__ == "__main__":
server = Server()
app = QApplication(sys.argv)
font_id = QFontDatabase.addApplicationFont("assets/fonts/poppins-v23-latin-700.ttf")
if font_id != -1:
app.setFont(QFontDatabase.applicationFontFamilies(font_id)[0])
with open("styles/theme.qss", "r") as f:
app.setStyleSheet(f.read())
win = MainWindow()
win.update_qr({"ip": server.ip_address, "port": server.port, "authToken": server.token, "name": "pc"},
fill_color="#2D2D2D", back_color="#D3D3D3")
print(server.ip_address)
server.start()
server.register_callback('file', win.handle_file)
server.register_callback('message', win.handle_callback)
win.show()
sys.exit(app.exec())
server.py:
import eventlet
from eventlet import wsgi
from urllib.parse import parse_qs
import socket
import socketio
import threading
import json
import base64
import random
class Server:
def __init__(self):
# core socket.io server
self.sio = socketio.Server(cors_allowed_origins='*')
self.app = socketio.WSGIApp(self.sio)
# track exactly one connected client
self.connected_sid = None
# auth token, IP, port
self.token = self.generate_token()
self.ip_address = self.get_local_ip()
self.port = 5000
# GUI callbacks (for file/text), registered later
self._callbacks = {
'message': None,
'file': None,
}
# ——— event handlers ———
@self.sio.event
def connect(sid, environ):
print(f"[CONNECT] raw sid = {sid}, qs = {environ.get('QUERY_STRING')}")
query = parse_qs(environ.get('QUERY_STRING', ''))
token = query.get('token', [None])[0]
self.sio.emit("test", {}, room=sid)
if token == self.token:
self.connected_sid = sid
print(f"✅ Client {sid} connected with valid token.")
self.sio.emit("message", {})
self.sio.emit("wtv", "wtv", room=sid)
self.sio.emit("test", {}, room=sid)
else:
print(f"❌ Client {sid} invalid token: {token}")
raise socketio.exceptions.ConnectionRefusedError('Unauthorized')
self.sio.emit("test", {}, room=sid)
@self.sio.event
def disconnect(sid):
print(f"[DISCONNECT] sid = {sid}")
if sid == self.connected_sid:
self.connected_sid = None
print("→ Cleared connected_sid")
@self.sio.event
def file(sid, data):
print(f"[FILE] from {sid}")
try:
filename = data.get("name")
filetype = data.get("type", "application/octet-stream")
b64 = data.get("data", "")
content = base64.b64decode(b64)
with open(filename, "wb") as f:
f.write(content)
print(f"Saved file: {filename}")
if self._callbacks['file']:
self._callbacks['file']({
"filename": filename,
"filetype": filetype,
})
self.sio.emit(
"file-ack",
{"status": "received", "filename": filename},
room=sid
)
except Exception as e:
print("Error in file handler:", e)
@self.sio.event
def message(sid, data):
print(f"[MESSAGE] raw data from {sid}:", data)
try:
if isinstance(data, str):
data = json.loads(data)
msg_type = data.get("type", "text")
content = data.get("content", "")
if self._callbacks['message']:
self._callbacks['message'](msg_type, content)
except Exception as e:
print("Error in message handler:", e)
def generate_token(self):
return str(random.randint(1000, 9999))
def send_message_test(self):
print("[server] ran send_message_test")
print(f"[server] self: {self.connected_sid}")
self.sio.emit("test", "test", room=self.connected_sid)
print("[server] done running send_message_test")
def get_local_ip(self):
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
try:
s.connect(('8.8.8.8', 80))
return s.getsockname()[0]
except:
return '127.0.0.1'
finally:
s.close()
def start(self):
threading.Thread(
target=lambda: wsgi.server(
eventlet.listen(('0.0.0.0', self.port)), self.app
),
daemon=True
).start()
print(f"Server started on {self.ip_address}:{self.port}")
print(f"Auth token = {self.token}")
print(f"[server] {self}")
def register_callback(self, event_name, callback):
if event_name in self._callbacks:
self._callbacks[event_name] = callback
else:
raise ValueError(f"Unknown event '{event_name}'")
if __name__ == "__main__":
server = Server()
server.start()
I'm using expo react native to connect: main.tsx:
//#region
// main.tsx
import * as Clipboard from 'expo-clipboard';
import * as DocumentPicker from 'expo-document-picker';
import * as FileSystem from 'expo-file-system';
import React, { useEffect, useRef, useState } from 'react';
import { Alert, Platform, Pressable, StyleSheet, Text, View } from 'react-native';
import 'react-native-url-polyfill/auto';
import { Socket } from 'socket.io-client';
import Fileicon from '../../assets/images/file.svg';
import ClipboardIcon from '../../assets/images/Paperclip.svg';
import PlaceHolderIcon from '../../assets/images/placeholder.svg';
import WifiIcon from '../../assets/images/wifi.svg';
import { useConnection } from '../(tabs)/test';
export default function MainScreen() {
const { activeConnection } = useConnection();
const [message, setMessage] = useState('');
const socketRef = useRef<Socket | null>(null);
const [isConnected, setIsConnected] = useState(false);
useEffect(() => {
if (!activeConnection || !activeConnection.socket) {
socketRef.current?.disconnect();
socketRef.current = null;
setIsConnected(false);
return;
}
const socket = activeConnection.socket;
socketRef.current = socket;
socket.onAny((event, ...args) => {
console.log(`📲 [onAny] event=’${event}’, args=`, args);
console.error(event, args);
});
socket.on('connect', () => {
console.log('Socket connected');
setIsConnected(true);
});
socket.on('disconnect', () => {
console.log('Socket disconnected dnwd');
setIsConnected(false);
});
socket.on('connect_error', (err) => {
console.log('Socket connect error:', err);
setIsConnected(false);
});
socket.on('message', (data: string) => {
console.log('Received message:', data);
});
socket.on('test', ()=>{
console.log("test");
Alert.alert("test");
});
socket.on('clipboard', (data) => {
console.log('🐛 Received clipboard event with content:', data);
if (data && data.content) {
Alert.alert('Clipboard updated', `Clipboard content: ${data.content}`);
} else {
Alert.alert('Clipboard updated', 'Clipboard event received but content empty');
}
});
socket.on('file-ack', (data: { status: string; filename: string }) => {
console.log('File ACK:', data);
if (data.status === 'received') {
Alert.alert('File Uploaded', `${data.filename} was saved on the server.`);
} else {
Alert.alert('Upload Failed', `Server responded with status: ${data.status}`);
}
});
return () => {
// socket.off('connect');
// socket.off('disconnect');
// socket.off('connect_error');
// socket.off('message');
// socket.off('file-ack');
// socket.off('clipboard')
// socket.off('test')
socket.disconnect();
socketRef.current = null;
setIsConnected(false);
};
}, [activeConnection]);
const sendMessage = () => {
if (!message.trim()) {
return Alert.alert('Error', 'Please enter a message');
}
if (!socketRef.current?.connected) {
return Alert.alert('Error', 'Not connected to any server');
}
socketRef.current.emit('message', message);
setMessage('');
};
const sendClipboard = async () => {
if (!activeConnection) {
return Alert.alert('Error', 'Not connected to any server');
}
if (!socketRef.current?.connected) {
return Alert.alert('Error', 'Not connected to any server');
}
const clipboardContent = await Clipboard.getStringAsync();
if (!clipboardContent) {
return Alert.alert('Clipboard', 'Clipboard is empty');
}
socketRef.current.emit(
'message',
{ type: 'clipboard', content: clipboardContent },
Alert.alert('clipboard sent successfully')
);
};
async function pickAndSendFile() {
if (Platform.OS === 'web') {
return Alert.alert("File picking isn't available on web.");
}
try {
const result = await DocumentPicker.getDocumentAsync({
type: '*/*',
copyToCacheDirectory: true,
});
// If user cancelled
if (result.canceled === true) {
return;
}
// Ensure assets array exists
if (!Array.isArray(result.assets) || result.assets.length === 0) {
return Alert.alert('Error', 'No file selected.');
}
const asset = result.assets[0];
const { uri, name, mimeType } = asset;
if (typeof uri !== 'string' || typeof name !== 'string') {
return Alert.alert('Error', 'Invalid file data.');
}
await sendFile(uri, name, mimeType ?? 'application/octet-stream');
} catch (error: any) {
Alert.alert('Error', 'Failed to pick/send file: ' + error.message);
}
}
async function sendFile(uri: string, filename: string, mimeType: string) {
if (!socketRef.current?.connected) {
return Alert.alert('Error', 'Not connected to any server');
}
try {
const base64 = await FileSystem.readAsStringAsync(uri, {
encoding: FileSystem.EncodingType.Base64,
});
socketRef.current.emit('file', {
name: filename,
type: mimeType,
data: base64,
});
} catch (error: any) {
Alert.alert('Error', 'Failed to read and send file: ' + error.message);
}
}
return (
<View style={styles.container}>
{/* Connection Status */}
<Pressable style={[styles.cardBase, activeConnection ? styles.connectionCard : styles.disconnectedCard]}>
<View style={styles.cardRow}>
<View>
<Text style={styles.cardTitle}>
{activeConnection
? `Connected to ${activeConnection.name}`
: 'Not Connected'}
</Text>
<View style={styles.cardDetails}>
<Text style={styles.cardSub}>
{activeConnection ? activeConnection.ip : 'XXX.XXX.XX.XXX'}
</Text>
<Text style={styles.cardSub}>
{new Date().toLocaleTimeString([], {
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
})}
</Text>
</View>
</View>
<WifiIcon width={28} height={28} />
</View>
</Pressable>
{/* Clipboard */}
<Pressable onPress={sendClipboard} style={[styles.cardBase, styles.actionCard]}>
<View style={styles.iconRow}>
<Text style={styles.buttonText}>Send Clipboard</Text>
<ClipboardIcon width={28} height={28} />
</View>
</Pressable>
{/* File */}
<Pressable onPress={pickAndSendFile} style={[styles.cardBase, styles.actionCard]}>
<View style={styles.iconRow}>
<Text style={styles.buttonText}>Send File</Text>
<Fileicon width={28} height={28} />
</View>
</Pressable>
{/* Message */}
<Pressable onPress={sendMessage} style={[styles.cardBase, styles.actionCard]}>
<View style={styles.iconRow}>
<Text style={styles.buttonText}>Send Message</Text>
<PlaceHolderIcon width={28} height={28} />
</View>
</Pressable>
<Text>{activeConnection?.ip}</Text>
</View>
);
}
//#region Styles
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 20,
paddingTop: 150,
backgroundColor: '#fff',
alignItems: 'center',
fontFamily: 'poppins-regular',
},
cardBase: {
width: '100%',
borderRadius: 16,
padding: 16,
marginBottom: 16,
height: 112,
justifyContent: 'center',
},
connectionCard: {
backgroundColor: '#49944E',
},
disconnectedCard: {
backgroundColor: '#D95353',
},
cardRow: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
paddingHorizontal: 8,
width: '100%',
},
cardTitle: {
color: '#fff',
fontSize: 16,
marginBottom: 6,
fontFamily: 'poppins-500',
},
cardDetails: {
flexDirection: 'row',
gap: 16,
},
cardSub: {
color: '#fff',
fontSize: 12,
opacity: 0.8,
},
actionCard: {
backgroundColor: '#4C4C4C',
},
iconRow: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
width: '100%',
height: '100%',
paddingHorizontal: 8,
},
buttonText: {
color: '#fff',
fontSize: 18,
fontFamily: 'poppins-500',
},
response: {
marginTop: 20,
fontSize: 16,
color: '#333',
},
});
//#endregion
This works just fine besides not receiving said emit, this is very much a debug version.
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4