我如何有条件地将一个gui连接到另一个gui?

时间:2015-12-05 01:37:27

标签: python sockets user-interface tkinter network-programming

这是我第一次尝试使用GUI和客户端 - 服务器应用程序,所以我很挣扎。我(希望)有一个服务器端和客户端以及一个gui来提示用户输入用户名和密码(存​​储在服务器端的文本文档中)。 gui(有点)在客户端工作,但在成功输入匹配的用户名和密码组合后,我不知道如何调用单独的菜单' gui打开。如何从client.py导入menu.py,这样当用户成功登录时,会遇到一个单独的gui?代码如下。如果我遗漏任何东西,请告诉我。谢谢!

客户端:

import socket
from tkinter import *
import tkinter.messagebox as tm
from tkinter import ttk

serverName = 'localhost'
serverPort = 12000
clientSocket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

class LoginFrame(Frame):

    def __init__(self, master):
        super().__init__(master)

        self.label_1 = Label(self, text="Username")
        self.label_2 = Label(self, text="Password")

        self.entry_1 = Entry(self)
        self.entry_2 = Entry(self, show="*")

        self.label_1.grid(row=0, sticky=E)
        self.label_2.grid(row=1, sticky=E)
        self.entry_1.grid(row=0, column=1)
        self.entry_2.grid(row=1, column=1)

        self.logbtn = Button(self, text="Login", command = self._login_btn_clicked)
        self.logbtn.grid(columnspan=2)

        self.pack()

    def _login_btn_clicked(self):

        username = self.entry_1.get()
        clientSocket.sendto(username.encode('UTF-8'),(serverName, serverPort))

        password = self.entry_2.get()
        clientSocket.sendto(password.encode('UTF-8'),(serverName, serverPort))

        message, address = clientSocket.recvfrom(1024)
        message = message.decode('UTF-8')

        if message == "200 OK":
            tm.showinfo("Logged in", message)

        else:
            tm.showinfo("Login error", message)

root = Tk()
root.title("GUI")
root.geometry('{}x{}'.format(270, 80))
lf = LoginFrame(root)
root.mainloop()

服务器:

import socket
# Create a UDP socket
serverSocket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# Assign IP address and port number to socket
serverSocket.bind(('', 12000))

while True:
    # Receive the client packet along with the address it is coming from
    username, address = serverSocket.recvfrom(1024)
    username = username.decode('UTF-8')

    password, address = serverSocket.recvfrom(1024)
    password = password.decode('UTF-8')

    infile = open("sales.txt", 'r')

    for rows in infile:    #equivalent to: Recs=infile.readline()
                        #this gets 1 line at a time as a string including \n
        rows = rows.strip()
        rowsList = rows.split('\t')
        #message += row + "\n" # + str(rowsList)
        username2 = rowsList[0].strip(' ')
        if username == username2:

            password2 = rowsList[1]
            if password == password2:

                # the server responds
                message = "200 OK"
                message = message.encode('UTF-8')
                serverSocket.sendto(message, address)

            else:
                # the server responds
                message = "401 Unauthorized"
                message = message.encode('UTF-8')
                serverSocket.sendto(message, address)

    # the server responds
    message = "401 Unauthorized"
    message = message.encode('UTF-8')
    serverSocket.sendto(message, address)
    infile.close()

菜单:

from tkinter import *
from tkinter import ttk

class MenuFrame(Frame):

    def __init__(self, master):
        super().__init__(master)

        mainframe = ttk.Frame(root, padding="3 3 12 12")
        mainframe.grid(column = 0, row = 0, sticky = (N, W, E, S))
        mainframe.columnconfigure(0, weight = 1)
        mainframe.rowconfigure(0, weight = 1)

        name = "John"
        sales = StringVar()
        moreSales = StringVar()
        sales.set("1")

        welcome = "Welcome, " + name + "!"

        sales_entry = ttk.Entry(mainframe, width = 7, textvariable = moreSales)

        ttk.Label(mainframe, text = welcome).grid(column = 2, row = 1, sticky = W)

        ttk.Label(mainframe, text = "Current Sales:").grid(column = 1, row = 2, sticky = W)
        ttk.Label(mainframe, textvariable = sales).grid(column = 2, row = 2, sticky = (E))
        ttk.Label(mainframe, text = "sales").grid(column = 3, row = 2, sticky = W)

        ttk.Label(mainframe, text = "Add Sales").grid(column = 1, row = 3, sticky = W)
        sales_entry.grid(column = 2, row = 3, sticky = (W, E))
        ttk.Button(mainframe, text = "Add", command = MenuFrame.add(sales, moreSales)).grid(column = 3, row = 3, sticky = W)

        ttk.Button(mainframe, text = "Log Out", command = MenuFrame.logout).grid(column = 2, row = 4, sticky = W)

    def add(sales, moreSales):

        try:
            value1 = int(sales.get())
            value2 = int(moreSales.get())
            sales.set(value1 + value2)
        except ValueError:
            pass

    def logout():
        root.destroy()

root = Tk()
root.title("GUI")
mf = MenuFrame(root)
root.mainloop()

编辑:我试过了

import Menu

并添加了

    root = Tk()
    root.title("GUI")
    mf = MenuFrame(root)
    root.mainloop()

之后

if message == "200 OK" in client.py 

但是我收到了错误:

Exception in Tkinter callback
Traceback (most recent call last):
  File "C:\Users\Pat\AppData\Local\Programs\Python\Python35-32\lib\tkinter\__init__.py", line 1549, in __call__
    return self.func(*args)
  File "C:\Users\Pat\Desktop\DunderMifflinSales-master\DunderMifflinClient.py", line 49, in _login_btn_clicked
    mf = MenuFrame(root)
NameError: name 'MenuFrame' is not defined

1 个答案:

答案 0 :(得分:1)

第一步是确保第二个GUI可以正确导入。必须没有实际在模块的全局范围内创建窗口的代码。您需要从Menu.py完全删除此代码块:

root = Tk()
root.title("GUI")
mf = MenuFrame(root)
root.mainloop()

完成后,您的代码应该可以正常工作。假设您希望“Menu.py”中的代码出现在单独的窗口中,您可以执行以下操作:

from tkinter import *
import Menu
...
if should_display_new_gui:
    toplevel = Toplevel(root)
    mf = Menu.MenuFrame(toplevel)
    mf.pack(fill="both", expand=True)

如果您希望菜单框显示在同一根窗口而不是新窗口中,则可以跳过创建Toplevel的实例,而是将root作为参数传递给{{1 }}

您的MenuFrame代码中还有其他与此特定问题无关的错误。例如,请考虑以下代码:

MenuFrame

第一个问题是ttk.Button(..., command = MenuFrame.add(...)).grid(...) 是一个类,而MenuFrame是该类的实例的方法。因此,需要修复的第一件事是使用add而不是self.add(...)

第二个问题是,当您执行MenuFrame.add(...)时,此_立即调用..., command=add(...)并将结果分配给add。这与你做到这一点没有什么不同:

command

您需要将cmd = MenuFrame.add(...) ttk.Button(..., command = cmd).grid(...) 引用传递给函数,而不是函数本身。如果您要传递变量,通常会使用commandlambda。但是,您不需要传递任何内容,因为您的functools.partial方法正在获取所需的数据。因此,您可以将命令更改为:

add

然后,像这样定义ttk.Button(..., command=self.add).grid(...)

add

,由于def add(self): value1 = int(sales.get()) value2 = int(moreSales.get()) sales是局部变量,因此不起作用。您需要将它们保存为类的属性:

moreSales

然后您可以像这样访问它们:

def __init__(self, master):
    super().__init__(master)
    ...
    self.sales = StringVar()
    self.moreSales = StringVar()
    ...