I've only just started using pyQt6 as of today, as tkinter was giving me some issues, however now that I have the base of the app working, I'm faced with an issue, my chat (self.output) will only update with new messages anytime that the send button is pressed, this of course is not how I would like it to be working, I'd rather it update once a message has been sent from the server, is there any solution to this problem?
Here is the code
from ast import Delete
import socket
import threading
from PyQt6.QtWidgets import *
from PyQt6.QtGui import *
from PyQt6.QtCore import *
import rsa
import sys
app = QApplication(sys.argv)
HOST = '127.0.0.1'
PORT = 9090
shost = '127.0.0.1'
sport = 9090
def loadKeys():
with open('keys/publicKey.pem', 'rb') as p:
publicKey = rsa.PublicKey.load_pkcs1(p.read())
with open('keys/privateKey.pem', 'rb') as p:
privateKey = rsa.PrivateKey.load_pkcs1(p.read())
return privateKey, publicKey
def encrypt(message, key):
return rsa.encrypt(message.encode('utf-8'), key)
def decrypt(ciphertext, key):
try:
return rsa.decrypt(ciphertext, key).decode('utf-8')
except:
return False
def sign(message, key):
return rsa.sign(message.encode('utf-8'), key, 'SHA-1')
def verify(message, signature, key):
try:
return rsa.verify(message.encode('utf-8'), signature, key,) == 'SHA-1'
except:
return False
privateKey, publicKey = loadKeys()
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((shost,sport))
gui_done = False
running = True
class Chatroom(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("Harmonic")
self.resize(1920,1080)
self.nickname = QInputDialog.getText(self, 'Nickname', 'Enter your nickname:')
layout = QVBoxLayout()
self.setLayout(layout)
QApplication.processEvents()
self.inputField = QLineEdit()
button = QPushButton('&Send')
button.clicked.connect(self.write)
self.output = QTextEdit()
layout.addWidget(self.inputField)
layout.addWidget(button)
layout.addWidget(self.output)
self.Gui_done = True
self.restoremessages()
receive_thread = threading.Thread(target=self.receive)
receive_thread.start()
def restoremessages(self):
with open('chathistory.txt', 'r') as file:
lines = file.readlines()
for line in lines:
self.output.append('{0}'.format(line))
def write(self):
message = (f"{self.nickname}: {self.inputField.text()}\n")
emessage = encrypt(message, publicKey)
sock.send(emessage)
self.inputField.clear()
def stop(self):
running = False
sock.close
sys.exit(app.exec_())
def receive(self):
while running:
try:
message = sock.recv(10000)
dm = decrypt(message,privateKey)
if message == "NICK":
sock.send(self.nickname.encode())
else:
self.output.append('{0}'.format(dm))
QApplication.processEvents
except ConnectionAbortedError:
print("Connection aborted")
sock.close()
break
except:
print("Error")
sock.close()
break
def closeEvent(self,event):
close = QMessageBox.question(self, "QUIT", "Are you sure you want to stop the process?", QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No)
if close == QMessageBox.StandardButton.Yes:
event.accept()
self.stop()
else:
event.ignore()
window = Chatroom()
window.show()
app.exec()
There are certain parts of the code that are redundent that I am aware of, same the error with sys.exit(app.exec_()) for some reason the program would not exit without me throwing an error to do so, so this will have to do for now.
Related
I have separately developed programs for a pyqt5 app and a client for communicate with a server.
GUI:
from PyQt5 import QtCore, QtWidgets, QtGui
from screen_stack import Ui_MainWindow_screenstack
from screen1 import Ui_Form_screen1
from screen2 import Ui_Form_screen2
from screen3 import Ui_Form_screen3
from client_cli_oop_with_exception_printing import mainProg
import socket
import threading #maybe this won't useful
import sys
import os
class MainApp(QtWidgets.QMainWindow):
def __init__(self):
super(MainApp, self).__init__() #if init was provided with paren=None, then inside this init parent argument can be passed
self.screenstack = Ui_MainWindow_screenstack()
self.screenstack.setupUi(self)
self.screen1 = scr1()
self.screen2 = scr2()
self.screen3 = scr3()
self.screenstack.stackedWidget.addWidget(self.screen1)
self.screenstack.stackedWidget.addWidget(self.screen2)
self.screenstack.stackedWidget.addWidget(self.screen3)
self.screenstack.stackedWidget.setCurrentWidget(self.screen1)
# self.screen1.s1.connect_tcp.clicked.connect(
# lambda: self.screenstack.stackedWidget.setCurrentWidget(self.screen2)
# )
self.screen1.s1.connect_tcp.clicked.connect(self.init_connect_server)
self.screen2.s2.register_name.clicked.connect(
lambda: self.screenstack.stackedWidget.setCurrentWidget(self.screen3)
)
self.sock = None
self.errScr1 = QtWidgets.QMessageBox()
self.errScr1.setWindowTitle('Error')
def init_connect_server(self):
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
self.sock.connect((self.screen1.s1.tcp_link.text(), int(self.screen1.s1.tcp_port.text())))
self.screenstack.stackedWidget.setCurrentWidget(self.screen2)
except Exception as e:
self.errScr1.setText(str(e))
self.errScr1.exec_()
def start_client_threads(self):
#all the communication threads should start here..
pass
class scr1(QtWidgets.QWidget):
def __init__(self):
super(scr1,self).__init__()
self.s1=Ui_Form_screen1()
self.s1.setupUi(self)
class scr2(QtWidgets.QWidget):
def __init__(self):
super(scr2,self).__init__()
self.s2=Ui_Form_screen2()
self.s2.setupUi(self)
class scr3(QtWidgets.QWidget):
def __init__(self):
super(scr3,self).__init__()
self.s3=Ui_Form_screen3()
self.s3.setupUi(self)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
sw = MainApp()
sw.show()
sys.exit(app.exec_())
Client:
import socket
import threading
import time
import os
class mainProg:
def __init__(self, host, port):
self.is_connection_lost = threading.Event()
self.client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
self.client.connect((host, port))
except Exception as e:
print(e)
print('Server is not valid...quitting...')
time.sleep(5)
os._exit(0)
self.start_threads()
def recieve(self):
while True:
try:
message = self.client.recv(1024).decode('utf-8')
if message == 'Nickname':
self.client.send(nickname.encode('utf-8'))
elif message == '': #when server gone, we will recieve a lot of empty strings continuously.. we can make the clients shut the python if server gone, but client waiting server to init again will be cooler
self.is_connection_lost.set()
self.client.close()
print('Connection Lost: waiting for the connection,')
while True:
print('waiting...')
time.sleep(3)
if not self.is_connection_lost.is_set():
print('Connection restored!!!')
break
else:
print(message)
#print(type(message))
except Exception as e:
print(e)
print('An error occured!, Not yet connected!')
#client.close()
#is_connection_lost = True
break
def send_messages(self):
while True:
try:
message = f"{nickname}: {input('')}"
self.client.send(message.encode('utf-8'))
except Exception:
pass
def reconnect_server(self):
time.sleep(3)
while True:
if self.is_connection_lost.is_set():
print('Trying to connect!!!!')
time.sleep(3)
self.client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
self.client.connect((srv,port))
except Exception as e:
print(f'Reconnect failed due to {e}')
continue
if self.client.recv(1024).decode('utf-8') == '':
self.client.close()
pass
else:
self.client.send(nickname.encode('utf-8'))
self.is_connection_lost.clear() #no problem here, it connects to server when server is up again...
def start_threads(self):
t1 = threading.Thread(target=self.recieve)
t2 = threading.Thread(target=self.send_messages)
t3 = threading.Thread(target=self.reconnect_server)
t1.start()
t2.start()
t3.start()
if __name__ == '__main__':
nickname = input('Choose a nickname: ')
srv = '0.tcp.in.ngrok.io'
port = 14112
mainProg(srv, port)
I know that using qthreads I can rewrite the code for client in GUI and make a functional app. But is there a way to connect both classes, so that I can use self.labelMessage.setText(message) instead of print(message) in client. In another way I am asking is there a way to inherit from multiple classes (MainApp and mainProg) so that I can easily connect both codes?
I have set up a socket server with a client and a host.
It works fine until the client has disconnected, with both .shutdown() and .close().
When I then launch the client again, it can't connect.
I presume this is not because of how I've written my code but rather what I haven't written.
How do I make the server truly disconnect the client's connection so that it can connect again?
Server:
import socket, threading, time, json
ONLINE_USERS = []
SESSION = None
class User():
def __init__(user, connection, address):
print('for', address, '{Connection established}')
user.connection = connection
user.address = address
user.character = None
threading.Thread(target=user.process, args=(), daemon=True).start()
def process(user):
time.sleep(1)
user.send("&pLogin\n^^^^^\n")
username = user.send("&iUser>")
password = user.send("&iPass>")
print(user.ping())
print(user.logout())
def send(user, *x):
user.connection.sendall(str.encode(str(x)))
data = user.connection.recv(1024)
return data if data!=b'\x01' else True
def recv(user, x):
user.connection.recv(x)
def ping(user):
start = time.time()
user.connection.sendall(b'\x02')
end = float(user.connection.recv(1024))
return round((end - start) * 1000)
def logout(user):
user.connection.sendall(b'\x04')
return user.connection.recv(4)
class Session():
def __init__(session, host='', port=12345):
session.host = host
session.port = port
session.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
session.socket.bind((host, port))
session.socket.listen(10)
def accept():
conn = User(*session.socket.accept())
session.thread_accept = threading.Thread(target=accept, args=(), daemon=True).start()
def shutdown():
for user in ONLINE_USERS.keys():
ONLINE_USERS[user].connection.sendall(bytes([0xF]))
if __name__ == '__main__':
SESSION = Session()
input('Press heart to continue!\n')
Client:
import socket, sys, threading, time, os
def clear(t=0.5):
time.sleep(t)
os.system('cls')
def tryeval(x, default):
try:
return eval(x)
except:
return default
class Client():
def __init__(client):
try:
server_info = input('IP_ADDRESS:PORT>').split(':')
client.host = server_info[0]
client.port = int(server_info[1])
except:
client.host = 'localhost'
client.port = 12345
client.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.socket.settimeout(10)
try:
client.socket.connect((client.host, client.port))
clear()
client.data_exchange()
finally:
client.shutdown()
def data_exchange(client):
while True:
data = client.socket.recv(1024)
if data:
if data==b'\x02':
client.socket.sendall(str.encode(str(time.time())))
elif data==b'\x04':
client.shutdown()
else:
data = tryeval(data, ())
response = []
for item in data:
try:
prefix, content = item[:2], item[2:]
if prefix=='&p':
print(content, end='')
elif prefix=='&i':
response.append(input(content))
if prefix=='&c':
time.sleep(float(content))
clear()
except:
pass
if len(response)>0:
client.socket.sendall(str.encode(str(tuple(response))))
else:
client.socket.sendall(b'\x01')
time.sleep(0.001)
def shutdown(client):
try:
client.socket.sendall(b'\x04')
except:
pass
print('Shutting down program.')
client.socket.shutdown(socket.SHUT_RDWR)
print('Socket has been shutdown.')
client.socket.close()
print('Socket has been closed.')
print('Exiting program')
time.sleep(1)
sys.exit()
if __name__ == '__main__':
client = Client()
"The server repeatedly calls accept waiting for new incoming connections." No it doesn't. It calls accept once in a thread...which exits. – Mark Tolonen
currently I'm working on a Server-Client Application with python.
The Server-Client is working. My problem is the GUI.
I'm trying to do this with GTK.
Since 2 weeks I'm testing around but the login-screen wont work.
So here are my questions:
How can I create and connect multiple windows in GTK? e.g first login-screen then main_window
How can a Text_Entry-Dialog be created (all in PyGTK)?
[Edit-03.01.2016] Code:
#!/usr/bin/python3
# coding=utf8
import socket
from gi.repository import GObject, Gio, Gtk
class MyApplication(Gtk.Application):
# Main initialization routine
def __init__(self, application_id, flags):
Gtk.Application.__init__(self, application_id=application_id, flags=flags)
self.connect("activate", self.new_window)
#main_Window(self)
def new_window(self, *args):
self. snc =start_Connection_Window(self)
print("going on")
def main_Window(self):
print "Here I'm "
self.connect("activate", self.new_MainWindow)
self.snc.close()
def new_MainWindow(self):
main_Window(self)
print "main_Window started"
class start_Connection_Window(Gtk.Window):
def __init__(self, application):
self.Application = application
# Read GUI from file and retrieve objects from Gtk.Builder
try:
GtkBuilder = Gtk.Builder.new_from_file()#Some Glade file with two windows
GtkBuilder.connect_signals(self)
except GObject.GError:
print("Error reading GUI file")
raise
# Fire up the main window
self.start_Connection_Window = GtkBuilder.get_object("start_Connection")
self.start_Connection_Window.set_application(application)
self.ServerIP_Input = GtkBuilder.get_object("ServerIP-Input")
self.Username_Input = GtkBuilder.get_object("Username_Input")
self.Password_Input = GtkBuilder.get_object("Password_Input")
self.start_Connection_Window.show()
def on_btn_Connect_clicked(self, button):
button.set_sensitive(False)
try:
self.host = str(self.ServerIP_Input.get_text())
self.username = str(self.Username_Input)
self.password = str(self.Password_Input)
self.port=1317
self.clientsocket=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.clientsocket.connect((self.host, self.port))
self.LENGTH_SIZE = 4
data='ping'
lenght = str(len(data))
self.clientsocket.send(lenght.zfill(4))
print data
self.clientsocket.send(data)
except:
print "Error!"
button.set_sensitive(True)
self.Application.main_Window()
try:
lenght = self.clientsocket.recv(self.LENGTH_SIZE)
data = self.clientsocket.recv(int(lenght))
print data
if(data == str("ok")):
try:
self.Application.main_Window( )
self.close()
except :
print "Here is the mistake"
else:
print "Failed!"
button.set_sensitive(True)
except:
print "Fail!"
#print "Would Start Conn"
def close(self, *args):
self.start_Connection_Window.destroy()
def on_MainWindow_destroy(self, window):
#self.Application.main_Window()
print "Bye"
def on_Window_destroy(self):
print("Bye aus dem destroyten!")
class main_Window(Gtk.Window):
def __init__(self, application):
self.Application = application
# Read GUI from file and retrieve objects from Gtk.Builder
try:
GtkBuilder = Gtk.Builder.new_from_file()#someGladeFile with two Windows
GtkBuilder.connect_signals(self)
except GObject.GError:
print("Error reading GUI file")
raise
# Fire up the main window
self.MainWindow = GtkBuilder.get_object("main_Window")
self.MainWindow.set_application(application)
self.MainWindow.show()
def on_btn_Connect_clicked(self, button):
print "Would Start Conn"
def close(self, *args):
self.MainWindow.destroy()
def on_MainWindow_destroy(self, window):
#self.Application.new_window()
print "Bye"
def on_Window_destroy(self, window):
#self.Application.new_window()
print "Bye"
def start(self, socket, host, username, password):
self.SClient = SC(host, username, password, self)
self.MainWindow.show()
# Starter
def main():
# Initialize GTK Application
Application = MyApplication("App", Gio.ApplicationFlags.FLAGS_NONE)
print "starting"
# Start GUI
Application.run()
if __name__ == "__main__": main()
For some reason, self.connected of the asyncore.dispatcher class doesn't consider my socket to be connected on the client side. The server side sees the socket as connected and treats it as such, but the client doesn't know if it's connected or not and handle_connect doesn't "proc", so i can't use a overridden version of it to check if the socket is connected either.
Any thoughts on this code why it ain't working:
#!/usr/bin/python
# -*- coding: iso-8859-15 -*-
import asyncore
from threading import *
from socket import *
from time import sleep
from os import _exit
from logger import *
from config import *
class logDispatcher(asyncore.dispatcher):
def __init__(self, config=None):
self.inbuffer = ''
self.buffer = ''
self.lockedbuffer = False
self.is_writable = False
asyncore.dispatcher.__init__(self)
#Thread.__init__(self)
self.create_socket(AF_INET, SOCK_STREAM)
#self.is_writable = True
#self.start()
def compare(self, obj, otherobj):
return (str(obj).lower() == str(otherobj).lower()[:len(str(obj))])
def _in(self, obj, otherobj):
return (str(obj).lower() in str(otherobj).lower())
def parse(self):
if self.inbuffer[-2:] != '\r\n':
return False
self.lockedbuffer = True
self.inbuffer = ''
self.lockedbuffer = False
def readable(self):
return True
def handle_connect(self):
log('Connected to ' + str(server), 'SOCK_CORE')
def handle_close(self):
self.close()
def handle_read(self):
data = self.recv(8192)
while self.lockedbuffer:
sleep(0.01)
self.inbuffer += data
def writable(self):
return (len(self.buffer) > 0)
def handle_write(self):
while self.is_writable:
sent = self.send(self.buffer)
sleep(1)
self.buffer = self.buffer[sent:]
if len(self.buffer) <= 0:
self.is_writable = False
sleep(0.01)
def _send(self, what):
self.buffer += what + '\r\n'
self.is_writable = True
def handle_error(self):
log('Error, closing socket!', 'SOCK_CORE')
self.close()
def run(self):
log('Log socket engine initating', 'SOCK_CORE')
self.connect((server, server_port))
print self.connected
sleep(3)
print self.connected
class start(Thread):
def __init__(self):
Thread.__init__(self)
self.start()
def run(self):
asyncore.loop(0.1)
start()
logDisp = logDispatcher()
logDisp.run()
def handle_connect_event(self):
self.is_connected = True
Adding that to your dispatcher will give you a way to check if the socket is connected or not, thanks to some stack trace (python -m trace -t script.py) in Python I managed to figure out that the asyncore class automaticly created that function for whatever reason, and it was called continiously as long as the socket was connected or in a connected state.
After that, i also replaced the threaded asyncore.loop() and replaced it with a "static" placement locking your main thread, one of these two combinations (or both) solved the issue for now.. the logic isn't the same as in my problem which i don't like but i assume that i'll be needing to create my own dispach_event just like if i were to do a OpenGL GUI class where i would call dispatch_event() manually every loop some how in the thread to "keep things alive".. it's just a thought..
Anyway, here's a working example:
#!/usr/bin/python
# -*- coding: iso-8859-15 -*-
import asyncore, socket
from threading import *
from time import sleep
from os import _exit
from logger import *
from config import *
def _map():
return {}
def _array():
return []
class logDispatcher(Thread, asyncore.dispatcher):
def __init__(self, config=None):
self.inbuffer = ''
self.buffer = ''
self.lockedbuffer = False
self.is_writable = False
self.is_connected = False
self.exit = False
self.initated = False
asyncore.dispatcher.__init__(self)
Thread.__init__(self)
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
try:
self.connect((server, server_port))
except:
log('Could not connect to ' + server, 'LOG_SOCK')
return None
self.start()
def handle_connect_event(self):
self.is_connected = True
def handle_connect(self):
self.is_connected = True
log('Connected to ' + str(server), 'LOG_SOCK')
def handle_close(self):
self.is_connected = False
self.close()
def handle_read(self):
data = self.recv(8192)
while self.lockedbuffer:
sleep(0.01)
self.inbuffer += data
def handle_write(self):
while self.is_writable:
sent = self.send(self.buffer)
sleep(1)
self.buffer = self.buffer[sent:]
if len(self.buffer) <= 0:
self.is_writable = False
sleep(0.01)
def _send(self, what):
self.buffer += what + '\r\n'
self.is_writable = True
def run(self):
sleep(1)
log('Log engine initating (hooking on to main)', 'LOG_CORE')
main = None
for t in enumerate():
if t.getName() == 'MainThread':
main = t
log('Log engine attached to main', 'LOG_CORE')
while (main and main.isAlive()) and (self.connected or self.is_connected):
print 'WHAM', self.connected, self.is_connected
sleep(1)
while 1:
logDisp = logDispatcher()
asyncore.loop(0.1)
log('Logserver disconnected, trying to reconnect!', 'CORE')
sleep(10)
I have a confirmation box which gets called when sending a file over the network. However, the function for the Signal is in the MainWindow class and uses variables from the Worker thread. But it doesn't work because the variables in the worker thread are not in the right scope.
Any idea how I can pass the msg variable to the saveFile function in the MainWindow class from the signal?
Edit: What I needed to do was pass an argument to my signal but I figured it out. Sorry for the confusion. I was unsure what needed to be done.
Here's a working example:
import socket
import select
import sys, os
from PyQt4.QtCore import *
from PyQt4.QtGui import *
CMD_FILE = 1
CLIENT_PORT = 9001
CLIENT_HOST = '127.0.0.1'
class MainWindow(QWidget):
def __init__(self, parent=None):
#super(QWidget, self).__init__(parent)
super(MainWindow, self).__init__(parent)
self.resize(100, 50)
self.thread = Worker()
self.thread.start()
self.file_button = QPushButton('Send a File')
self.connect(self.file_button, SIGNAL("released()"), self.sendFile)
self.connect_button = QPushButton('Connect To Server')
self.connect(self.connect_button, SIGNAL("released()"), self.connectToServer)
self.connect(self.thread, SIGNAL('triggered(PyQt_PyObject)'), self.saveFile)
self.layout = QFormLayout()
self.layout.addRow(self.file_button, self.connect_button)
self.setLayout(self.layout)
def connectToServer(self):
global s
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((str(CLIENT_HOST).strip(), CLIENT_PORT))
print "Connected to ", str(CLIENT_HOST)
except:
print "unable to connect"
def clientAll(self, cmd, msg):
#check if connnected send a message
s.sendall(cmd + msg)
def sendFile(self):
filename=QFileDialog.getOpenFileName(self, 'Open File', '.')
f = open(filename, 'rb')
myFile = f.read()
self.clientAll(chr(CMD_FILE), myFile)
f.close()
print filename, "Sent\n"
def saveFile(self, msg):
reply = QMessageBox.question(self, 'Message',
"Are you sure you wish to download this file?", QMessageBox.Yes |
QMessageBox.No, QMessageBox.No)
if reply == QMessageBox.Yes:
filename = QFileDialog.getSaveFileName(self, 'Save File', '.')
f = open(filename, 'wb')
f.write(msg)
f.close()
print filename, "Recieved"
else:
pass
class Worker(QThread):
def __init__(self):
QThread.__init__(self)
self.exiting = False
def __del__(self):
self.exiting = True
self.wait()
def run(self):
source_ip = ''
#socket.gethostbyname(socket.gethostname())
PORT = 9001
### Initialize socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_socket.bind((source_ip, PORT))
#
server_socket.listen(5)
read_list = [server_socket]
### Start receive loop
while True:
readable, writable, errored = select.select(read_list, [], [])
for s in readable:
if s is server_socket:
conn, addr = s.accept()
read_list.append(conn)
else:
msg = conn.recv(12024)
if msg:
cmd, msg = ord(msg[0]),msg[1:]
if cmd == CMD_FILE:
if not msg: break
self.emit(SIGNAL('triggered(PyQt_PyObject)'), msg)
else:
s.close()
read_list.remove(s)
if __name__ == '__main__':
app = QApplication(sys.argv)
win = MainWindow()
win.setWindowTitle('CATS!!! <3')
win.show()
sys.exit(app.exec_())
I think I don't understand exactly your situation.
However, did you try to define a new (custom) signal?
If you define a new signal with pyqtSignal,
you can emit the signal with parameters.
Documentation on signal and slots (new style).