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这种可靠的协议。保证每个数据到达,且保留顺序性

 

系列结束,再见。

  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
1.2 功能要求 (1)登录功能。 (2)客户可以通过服务器转发,实现一对一和多对多聊天。 (3)实现呼叫功能。 (4)客户端程序应该可以实时显示目前其它用户的状态。 (5)应该具有易用、美观的图形界面。 一、 实验目的: 1. 了解socket类的网络编程技术; 2. 熟悉socket聊天系统的结构和工作原理; 3. 掌握TCP传递消息的机制; 4. 应用delphi对该聊天系统进行程序的编写。 二、 实验描述: 通过delphi编程实现局域网内的一个聊天系统,支持客户与服务器、客户与客户之间的消息传递,服务器允许多个客户端的聊天的功能,聊天记录的保存和查看的功能等。 三、 实验硬件、软件平台: 1. 硬件平台:多台PC机的一个局域网、Windows XP/2000、AMD Athlon64 X2 4000+ 、内存256MB以上、硬盘80G以上。 2. 软件平台:delphi7 1.3 运行环境 本系统基于WIN NT 和ACCESS XP设计,适用于WIN2000/WIN XP等系统,并需要安装office2000/office xp. 1.4 功能实现 1.登录验证功能。 2. 客户通过服务器转发,实现聊天功能。 3. 实时显示目前其它用户的状态。 4. 保存并能察看聊天纪录。 5.申请新的用户号码。 6. 易用、美观的图形界面,实现系统托盘。 二.技术路线 2.1 总体方案 为实现网络聊天的功能,采用Windows Socket编程,服务器与客户端采用了TCP/IP连接方式,在设计聊天方案时,实行将所有信息发往服务器端,再由服务器进行分别处理的思路,服务器端是所有信息的中心。 由于服务器端要保存用户信息,我们利用数据库来实现这一功能,因此首先需要建立用户信息数据库。 在客户端保存聊天纪录和用户号码这一功能的实现中,采用了文件系统设计。 在信息到来及好友上线时,通过闪动托盘图标和播放不同的音乐进行提示。 建立消息链表来保存用户接收的各种消息。 服务器及客户端的功能可划分为以下模块: 客户端: 1) 登陆功能:建立与服务器的连接并登陆,能显示登陆错误信息。 2) 界面显示:将在线好友显示在好友列表中,并实现系统托盘,加入工具栏便于操作。 3) 聊天功能:与好友聊天。 4) 聊天纪录:能保存聊天纪录,并能察看聊天纪录。 5) 信息提示:闪动托盘图标提示到来信息,并播放不同音乐来提示。 6) 其他: 用户登陆成功,将保存其号码,以便下次登陆时,不必再输入而可以直接选择,显示登陆时间。
在Delphi中,套接字(Socket)编程用到的基本类是TServerSocket与TClientSocket。这两个类全部位于ScktComp单元中。其类型定义如下: type TServerSocket = class (ScktComp.TCustomServerSocket); TClientSocket = class (ScktComp.TCustomSocket)。      在编写程序时,首先要对TServerSocket(在服务器端)与TClientSocket(在客户端)进行实例化。对于TServerSocket的对象,主要设置其服务类型(ServerType)与端口(Port)的属性,然后编写“OnClientRead”事件处理程序的代码,处理来自客户机的请求。如要启动服务器,设置TServerSocket对象的Active属性为真(即Active := True),如要停止服务器,则设置TServerSocket对象的Active属性为假(即Active := False)。      对于TClientSocket的对象,主要设置对方服务器的服务类型(ServerType)、端口(Port)以及IP地址(Address)的属性,之后编写“OnConnect与OnRead”事件处理程序的代码“OnConnect”事件处理程序用来检查与服务器连接成功与否(必须在这里进行检查才有效),“OnRead”事件处理程序用来读取服务器发来的信息。如要连接服务器,设置TClientSocket对象的Active属性为真(即Active := True;注意:检查连接是否成功,必须在“OnConnect”事件处理程序中进行),如要断开与服务器的连接,则设置TClientSocket对象的Active属性为假(即Active := False)。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值