Socket编程:一点点教你做个聊天工具——(四)菜单——用户自定义编辑IP

先简单提下顶端的canvas,直译就是“画布”,画画得有纸,这就是纸。

 

root = tk.Tk()
root.title('MyChat')
root.geometry('800x500')
root.config(background='#F8F8FF')

#create canvas to show image
canvas = tk.Canvas(root, width=500, height=100)
image_file = tk.PhotoImage(file='img/title.gif')
image = canvas.create_image(120, 0, anchor='nw', image=image_file)
canvas.pack(side='top')

 

很简单,把一个photoimage对象显示在canvas,仅此而已。

 

canvas.create_image()方法的参数提下。前两个参数是图片从canvas的哪里开始,比如:

 

黑框是canvas,红框是image。如果是create_image(0,0)就是图中的样子。如果是create_image(50,50),就是这样:

下一个参数anchor是图片的起始位置,n(north),w(west),s(south),e(east),center。按上北下南左西右东的方位记忆。nw就是左上角开始,比如,一张图片是这样的:

 

那create_image(0,0,anchor='nw'),就是这样的:

 

如果是create_image(0,0,anchor='center'),就是这样的:

行吧,canvas就这样了,不是必须的,随便去设计吧。

 

来看菜单的设计。为了界面的友好性,每当配置完一项后,我们就在界面里显示当前的配置消息。在button下面放三个label来显示配置:

 

button = tk.Button(root, text='submit', command=insert_point)
button.pack()

#my ip
var_ip = tk.StringVar()
#his/her ip
var_send = tk.StringVar()
#my id
var_name = tk.StringVar()

#some friendly labels
label1 = tk.Label(root, textvariable=var_ip)
label2 = tk.Label(root, textvariable=var_send)
label3 = tk.Label(root, textvariable=var_name)
label1.pack()
label2.pack()
label3.pack()

而label显示的内容是变量,根据用户的输入变化,因此label的显示消息需要给stringvar。

 

菜单的逻辑很简单,主菜单叫config,下面是三个小的子菜单,分别用于配置自己和别人的ip以及自己的id。

 

#create menu object
menubar = tk.Menu(root)
ip_menu = tk.Menu(menubar, tearoff=0)

#config menu and add submenu
menubar.add_cascade(label='Config', menu=ip_menu)
ip_menu.add_command(label='Config ip', command=conf_ip)
ip_menu.add_command(label='Config send ip', command=conf_send)
ip_menu.add_command(label='Edit ID', command=edit_id)

#show menu
root.config(menu = menubar)

子菜单的绑定事件,就是配置功能的逻辑。

 

拿自己ip的配置conf_ip来说吧。

 

def conf_ip():
    top = tk.Toplevel()
    top.title('Config ip')
    top.geometry('300x100')

    e = tk.Entry(top)
    e.pack()

    def confirm():
        global ip_port_recv, var_ip, sk_recv
        var = e.get()
        ip_port_recv = (var, 55555)
        var_ip.set('我的IP:' + var)
        sk_recv.bind(ip_port_recv)
        t1 = threading.Thread(target=loop)
        t1.setDaemon(True)
        t1.start()
        top.destroy()
    b = tk.Button(top, text='Confirm', command=confirm)
    b.pack()

当点击的时候,弹出一个子窗口,有一个输入框和点击按钮,点击按钮的功能逻辑:

 

输入框的IP作为接收数据的IP,及末尾label的显示变量,socket的对象绑定也放在这里面,同时接收数据的进程现在就可以启动了。

 

完成所有的东西后,记得把子窗口关掉。

 

另外两个功能的逻辑大同小异,记得还有一个ID变量,而我们最开始的文本框也就不是简单的插入输入框的数据了,前面要加上MyID:,因为这样更加真实些呀。

 

行吧,大概也就这样了。我把全部代码贴出来,有问题大家再讨论吧。

 

import tkinter as tk
import socket
import threading

#define object
sk_recv = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sk_send = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

ip_port_recv = ('127.0.0.1', 55555)
ip_port_send = ('127.0.0.1', 55555)

#recive data run in a threading
def loop():
    while True:
        var = sk_recv.recv(1024).decode()
        t.insert('insert', var + '\n')

#create windons
root = tk.Tk()
root.title('MyChat')
root.geometry('800x500')
root.config(background='#F8F8FF')

#create canvas to show image
canvas = tk.Canvas(root, width=500, height=100)
image_file = tk.PhotoImage(file='img/title.gif')
image = canvas.create_image(120, 0, anchor='nw', image=image_file)
canvas.pack(side='top')

#a label to say something
labe = tk.Label(root, text='Message Bar (No need to operate) :')
labe.pack()
t = tk.Text(root, height=10)
t.pack()

#config my ip
def conf_ip():
    #create sub windon
    top = tk.Toplevel()
    top.title('Config ip')
    top.geometry('300x100')

    #a entry
    e = tk.Entry(top)
    e.pack()

    #the function of button
    def confirm():
        global ip_port_recv, var_ip, sk_recv
        var = e.get()
        #change ip
        ip_port_recv = (var, 55555)
        #change label var
        var_ip.set('我的IP:' + var)
        #bing port
        sk_recv.bind(ip_port_recv)
        #loop to recive data
        t1 = threading.Thread(target=loop)
        t1.setDaemon(True)
        t1.start()
        #close subwindon
        top.destroy()

    #a button
    b = tk.Button(top, text='Confirm', command=confirm)
    b.pack()

#config his/her id
def conf_send():
    top = tk.Toplevel()
    top.title('Config send ip')
    top.geometry('300x100')

    e = tk.Entry(top)
    e.pack()

    def confirm():
        global ip_port_send, var_send
        var = e.get()
        ip_port_send = (var, 55555)
        var_send.set('他的IP:' + var)
        top.destroy()
    b = tk.Button(top, text='Confirm', command=confirm)
    b.pack()

#Edit my id
def edit_id():
    top = tk.Toplevel()
    top.title('Edit ID')
    top.geometry('300x100')

    e = tk.Entry(top)
    e.pack()

    def confirm():
        global var_name, name
        var_name.set('我的ID:' + e.get())
        name.set(e.get())
        top.destroy()
    b = tk.Button(top, text='Confirm', command=confirm)
    b.pack()

#create menu
menubar = tk.Menu(root)
ip_menu = tk.Menu(menubar, tearoff=0)

#config menu and add submenu
menubar.add_cascade(label='Config', menu=ip_menu)
ip_menu.add_command(label='Config ip', command=conf_ip)
ip_menu.add_command(label='Config send ip', command=conf_send)
ip_menu.add_command(label='Edit ID', command=edit_id)

#show menu
root.config(menu = menubar)

#a friendly label
label = tk.Label(root, text='Input something:')
label.pack()

#a entry to input something for user
entry = tk.Entry(root)
entry.pack()

def insert_point():
    global name
    var = entry.get()
    message = name.get() + ' : ' + var
    t.insert('insert', message + '\n')
    sk_send.sendto(message.encode(), ip_port_send)
    entry.delete(0, tk.END)
    
#a button for sending message
button = tk.Button(root, text='submit', command=insert_point)
button.pack()

#some var
#my ip
var_ip = tk.StringVar()
#his/her ip
var_send = tk.StringVar()
#my id
var_name = tk.StringVar()
name = tk.StringVar()

#some friendly labels
label1 = tk.Label(root, textvariable=var_ip)
label2 = tk.Label(root, textvariable=var_send)
label3 = tk.Label(root, textvariable=var_name)
label1.pack()
label2.pack()
label3.pack()

root.mainloop()

 

这里可以给大家留下个问题,意犹未尽的可以继续玩。既然我可以在两台机器间传递文本消息了,那我为何不能传首歌、传个照片、发个视频呢?

 

逻辑上也很简单,拿.mp3文件来举例,在client端用二进制的方式读取文件,每次读取1024个字节,循环发送。server那边一边接收,一边用二进制的方式往文件里面写,最后保存成.mp3的文件就可以了。

 

在这里因为发送的数据是有序的,比如我依次发送了数据是A、B、C,你往文件里写的顺序也必须是A、B、C,顺序一打乱,文件也就错误了。也因此,传递数据时,我们需要TCP这种可靠的协议。保证每个数据到达,且保留顺序性

 

系列结束,再见。

发布了53 篇原创文章 · 获赞 80 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/qq_41500251/article/details/90244473