a callback function
Callback function is a common concept in programming. It is usually used in asynchronous programming or event-driven programming. A callback function is a function that is passed as a parameter to another function and is called when a specific event occurs.
Here are some basic concepts and usage of callback functions in Python:
-
Functions as objects : In Python, functions are first-class citizens, which means they can be passed around like variables. You can pass a function as a parameter to another function, or assign a function to a variable.
-
Definition of callback function: A callback function is usually an ordinary Python function that can be called when needed. This function accepts one or more parameters, usually the data when the event is triggered.
-
Registration of callback function: In some cases, you need to register the callback function to an event handler or asynchronous task. This is usually done by passing a callback function as a parameter to another function or method of the object.
-
Event triggering: When a specific event occurs, the registered callback function will be called. This event can be the completion of an asynchronous operation, user interaction, timer triggering, etc.
Here is a simple example demonstrating how to use callback functions in Python:
# 定义一个回调函数
def callback_function(result):
print(f"Callback received result: {result}")
# 模拟异步操作,当操作完成时调用回调函数
def async_operation(callback):
# 模拟异步操作,假设操作需要一段时间
import time
time.sleep(2)
result = 42 # 模拟操作的结果
# 调用回调函数,将结果传递给回调函数
callback(result)
# 注册回调函数并执行异步操作
async_operation(callback_function)
# 主程序可以继续执行其他操作
print("Main program continues...")
1.1 Process pool callback function
pool.apply_async (func=test1 , callback=test3) # Call the callback function
import time, os
from multiprocessing import Pool
def test1():
print(f'妈妈的进程ID:{os.getpid()}, 当前进程的父进程:{os.getppid()}')
print(f'你女儿叫你起床,你慢吞吞的起床')
time.sleep(3)
print('你起来了')
return 'good morning'
def test2():
print(f'女儿进程,女儿开始早读,当前进程ID:{os.getpid()}')
time.sleep(3)
print('早读完成')
def test3(args):
print('一起吃早餐')
print(args)
if __name__ == '__main__':
pool = Pool(4)
pool.apply_async(func=test1, callback=test3)
test2()
Two-person chat room server code
import wx
from socket import *
import threading
import time
class ChatServer(wx.Frame):
def __init__(self):
"""创建窗口"""
wx.Frame.__init__(self, None, id=102, title='服务器界面', pos=wx.DefaultPosition, size=(400, 470))
pl = wx.Panel(self) # 在窗口中初始化一个面板
# 在面板里面会放一些按钮,文本框,文本输入框等,把这些对象统一放入一个盒子里面
box = wx.BoxSizer(wx.VERTICAL) # 在盒子里面垂直方向自动排版
g1 = wx.FlexGridSizer(wx.HORIZONTAL) # 可升缩的网格布局,水平方向
# 创建三个按钮
start_server_button = wx.Button(pl, size=(133, 40), label="启动")
record_save_button = wx.Button(pl, size=(133, 40), label="聊天记录保存")
stop_server_button = wx.Button(pl, size=(133, 40), label="停止")
g1.Add(start_server_button, 1, wx.TOP)
g1.Add(record_save_button, 1, wx.TOP)
g1.Add(stop_server_button, 1, wx.TOP)
box.Add(g1, 1, wx.ALIGN_CENTER) # ALIGN_CENTER 联合居中
# 创建只读的文本框,显示聊天记录
self.text = wx.TextCtrl(pl, size=(400, 400), style=wx.TE_MULTILINE | wx.TE_READONLY)
box.Add(self.text, 1, wx.ALIGN_CENTER)
pl.SetSizer(box)
'''以上代码窗口结束 '''
'''服务准备执行的一些属性'''
self.isOn = False # 服务器没有启动
self.host_port = ('', 8888)
self.server_socket = socket(AF_INET, SOCK_STREAM) # TCP协议的服务器端套接字
self.server_socket.bind(self.host_port)
self.server_socket.listen(5)
self.session_thread_map = {} # 存放所有的服务器会话线程,字典:客户端名字为Key,会话线程为Value
'''给所有的按钮绑定相应的动作''' # 给启动按钮,绑定一个按钮事件,事件触发的时候会自动调用一个函数
self.Bind(wx.EVT_BUTTON, self.start_server, start_server_button)
self.Bind(wx.EVT_BUTTON, self.save_record, record_save_button)
# 服务器开始启动函数
def start_server(self, event):
print('服务器开始启动')
if not self.isOn:
# 启动服务器
self.isOn = True
# 创建线程对象
main_thread = threading.Thread(target=self.do_work)
main_thread.setDaemon(True) # 设置守护线程
main_thread.start()
# 服务运行之后的函数
def do_work(self):
print("服务器开始工作")
while self.isOn: # 在start_server中被赋值为True
session_socket, client_addr = self.server_socket.accept()
# 服务首先接受客户端发过来的第一条消息,我们规定第一条消息为客户端的名字
username = session_socket.recv(1024).decode('UTF-8') # 接受客户端名字
# 创建一个会话线程 【采用的是继承式实现多线程】
session_thread = SessionThread(session_socket, username, self)
# 字典元素的添加 {客户名称key:客户端的线程对象为值}
self.session_thread_map[username] = session_thread
session_thread.start() # 启动客户端的线程
# 表示有客户端进入到聊天室
self.show_info_and_send_client("服务器通知", f"欢迎{username}进入聊天室!",
time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()))
self.server_socket.close()
# 在文本中显示接受和发送的信息
def show_info_and_send_client(self, source, data, data_time):
send_data = f'{source}:{data}\n 时间:{data_time}\n'
self.text.AppendText(f'--------------------\n{send_data}') # 在服务器聊天框显示
for client in self.session_thread_map.values():
if client.isOn:
client.user_socket.send(send_data.encode('utf-8')) # 数据转发到所有没有关闭的客户端
# 服务保存聊天记录
def save_record(self, event):
record = self.text.GetValue()
with open("record.log", "w+") as f:
f.write(record)
# 服务器端会话线程的类
class SessionThread(threading.Thread):
def __init__(self, socket, un, server):
threading.Thread.__init__(self)
self.user_socket = socket
self.username = un
self.server = server
self.isOn = True # 会话是否已经启动
def run(self):
print(f'客户端{self.username}, 已经和服务器连接成功,服务器启动一个会话线程')
while self.isOn:
data = self.user_socket.recv(1024).decode('utf8') # 接收聊天信息
if data == 'A^disconnect^B': # 客户端点击断开按钮,约定内容
self.isOn = False
self.server.show_info_and_send_client(f'服务器通知,{self.username}离开了聊天室!'
f'{time.strftime("%Y-%m-%d %H:%M:%S")}')
else:
# 其他聊天信息
self.server.show_info_and_send_client(self.username, data, time.strftime("%Y-%m-%d %H:%M:%S"))
self.user_socket.close()
if __name__ == '__main__':
app = wx.App()
ChatServer.Show()
app.MainLoop()
Three-person chat room client code
import wx
from socket import *
import threading
class ChatClient(wx.Frame):
def __init__(self, c_name): # c_name:客户端名字
# 调用父类的初始化函数
wx.Frame.__init__(self, None, id=101, title=f'{c_name}的客户端界面', pos=wx.DefaultPosition, size=(400, 470))
pl = wx.Panel(self) # 在窗口中初始化一个面板
# 在面板里面会放一些按钮,文本框,文本输入框等,把这些对象统一放入一个盒子里面
box = wx.BoxSizer(wx.VERTICAL) # 在盒子里面垂直方向自动排版
g1 = wx.FlexGridSizer(wx.HORIZONTAL) # 可升缩的网格布局,水平方向
# 创建两个按钮
conn_button = wx.Button(pl, size=(200, 40), label="连接")
dis_conn_button = wx.Button(pl, size=(200, 40), label="离开")
g1.Add(conn_button, 1, wx.TOP | wx.LEFT) # 连接按钮布局在左边
g1.Add(dis_conn_button, 1, wx.TOP | wx.RIGHT) # 断开按钮布局在右边
box.Add(g1, 1, wx.ALIGN_CENTER) # ALIGN_CENTER 联合居中
# 创建聊天内容的文本框,不能写消息 :TE_MULTILINE -->多行 TE_READONLY-->只读
self.text = wx.TextCtrl(pl, size=(400, 250), style=wx.TE_MULTILINE | wx.TE_READONLY)
box.Add(self.text, 1, wx.ALIGN_CENTER)
# 创建聊天的输入文本框,可以写
self.input_text = wx.TextCtrl(pl, size=(400, 100), style=wx.TE_MULTILINE)
box.Add(self.input_text, 1, wx.ALIGN_CENTER)
# 最后创建两个按钮,分别是发送和重置
g2 = wx.FlexGridSizer(wx.HORIZONTAL)
clear_button = wx.Button(pl, size=(200, 40), label="重置")
send_button = wx.Button(pl, size=(200, 40), label="发送")
g2.Add(clear_button, 1, wx.TOP | wx.LEFT)
g2.Add(send_button, 1, wx.TOP | wx.RIGHT)
box.Add(g2, 1, wx.ALIGN_CENTER)
pl.SetSizer(box) # 把盒子放入面板中
''' 以上代码完成了客户端界面(窗口) '''
'''给所有按钮绑定点击事件'''
self.Bind(wx.EVT_BUTTON, self.connect_to_server, conn_button)
self.Bind(wx.EVT_BUTTON, self.send_to, send_button)
self.Bind(wx.EVT_BUTTON, self.go_out, dis_conn_button)
self.Bind(wx.EVT_BUTTON, self.reset, clear_button)
'''客户端的属性'''
self.name = c_name
self.isConnected = False # 客户端是否已经连上服务器
self.client_socket = None
# 连接服务器
def connect_to_server(self, event):
print(f"客户端{self.name},开始连接服务器")
if not self.isConnected:
server_host_port = ('localhost', 8888)
self.client_socket = socket(AF_INET, SOCK_STREAM)
self.client_socket.connect(server_host_port)
# 之前规定,客户端只要连接成功,马上把自己的名字发给服务器
self.client_socket.send(self.name.encode('UTF-8'))
t = threading.Thread(target=self.receive_data) # 客户端,采用函数式实现多线程
t.setDaemon(True) # 客户端UI界面如果关闭,当前守护线程也自动关闭
self.isConnected = True
t.start()
# 接受聊天数据
def receive_data(self):
print("客户端准备接收服务器的数据")
while self.isConnected:
data = self.client_socket.recv(1024).decode('UTF-8')
# 从服务器接收到的数据,需要显示
self.text.AppendText(f'{data}\n')
# 客户端发送信息到聊天室
def send_to(self, event):
if self.isConnected:
info = self.input_text.GetValue()
if info != '':
self.client_socket.send(info.encode('UTF-8'))
# 输入框中的数据如果已经发送了,输入框重新为空
self.input_text.SetValue('')
# 客户端离开聊天
def go_out(self, event):
self.client_socket.send('A^disconnect^B'.encode('UTF-8'))
# 客户端主线程也关闭
self.isConnected = False
# 客户端的输入信息重置
def reset(self, event):
self.input_text.Clear()
if __name__ == '__main__':
app = wx.App()
name = input("请输入客户端名字:")
ChatClient(name).Show()
app.MainLoop() # 循环刷新显示