保持控制台输入行在输出下方

4 投票
2 回答
4271 浏览
提问于 2025-04-18 17:30

[编辑:] 目前我正在尝试制作一个小的TCP聊天应用程序。发送和接收消息的功能已经正常工作了……但是问题是:

当我在接收消息的同时开始输入消息时,接收到的消息会出现在我正在输入的文本之后。

截图:http://s7.directupload.net/images/140816/6svxo5ui.png

[用户发送了 > "hello",然后我开始写 "i am writing...",接着用户写了 "i sent a...",在我发送我的消息之前……所以这条消息被放在了我的输入之后……我希望接收到的消息总是出现在我的输入之前!

这是我现在的代码:
Client.py

con = connect.User()
server = raw_input("Type in the server adress \n[leave blank to use xyr.no-ip.info]\n>:")
nick =""

while nick == "":
    nick = raw_input("Type in your nickname\n>:")

con.connect(server, nick)


def sender():
    print("Sender started")
    while 1:
        msg = raw_input()
        if msg == "q":
            break
        con.send(msg)

    con.disconnect()



def receiver(server):

        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

        if server == "":
            server="xyr.no-ip.info"

        sock.connect((server, 8000))

        sock.send("%pyreceiver\n")

        print("Receiver started")

        while 1:
           msg_in = sock.recv(1024)
           if not str(msg_in).startswith("[py]" + nick):
               if str(msg_in).startswith("/ua"):
                   print(str(msg_in)[3:])

               elif str(msg_in).startswith("/u "):
                   print(str(msg_in)[2:])
               else:

                print(str(msg_in[:-1]))
#

if nick == "":
    nick = "guest"
    print("Name changed to ""guest""")
    time.sleep(.5)

thread.start_new_thread(receiver, (server, ))
time.sleep(.5)
thread.start_new_thread(sender())

Connect.py

import socket

import time

class User():


    nickel =""
    def connect(self, server="xyr.no-ip.info", nick="guest"):
        nickel = nick
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        if server == "":
            server="xyr.no-ip.info"
            print("server changed to xyr.no-ip.info")
            time.sleep(.5)


        print("Connecting...")
        self.sock.connect((server, 8000))
        print("Connected")
        time.sleep(.4)
        self.sock.send("[py]" + nick + "\n")


        self.sock.send(nick + " connected with a python client\n")
        print("registered as " + nick)
        time.sleep(.3)


    def send(self, msg):
        self.sock.send(msg + "\n")


    def disconnect(self):
        self.sock.close()

        print("disconnected")

2 个回答

2

我之前遇到过类似的问题,后来发现一个更简单的解决办法,就是用readchar这个库来获取输入(https://github.com/magmax/python-readchar/tree/master/readchar)。

使用readchar的时候,我会把每次按键的输入存起来(检查按下的是否是退格键和回车键 - 下面有代码片段)。

每次输出的时候,我会在前面加上"/033[1A"(这个是让光标上移),然后打印输出的内容,再加一个"/n"...

在每次输出后,我会把光标移到行首,然后重新打印self.input_buff里的内容。

这样用户在输入的时候,控制台会显示他们正在输入的内容:

            keypress = readkey()

            if keypress == key.BACKSPACE:
                self.input_buff = self.input_buff[:-1]
                print("\033[0A%s         " % self.input_buff)
                continue

            if keypress != key.CR:
                self.input_buff = "%s%s" % (self.input_buff, keypress)
                print("\033[0A%s" %  self.input_buff)
                continue

这样就能把输入行保持在底部,而所有的终端输出都在它上面。

我知道这个方法来得有点晚,如果你对curses很在行,也许那是更好的选择...

2

你的代码把所有内容都写到了标准输出(stdout)上。每当你的发送者或接收者线程有东西到达时,它就会打印到标准输出上。这样做的问题是,由于输出流的基本特性,你无法做到以下几点:

  • 把收到的消息放在当前输入/回显内容的上面

事情发生的顺序是严格按照发生的先后进行的。只要有东西进来,无论光标在哪里,打印语句就会把数据直接输出到那个位置。你不能改变这种行为,除非使用更复杂或更强大的工具。

为了实现你想要的效果,我建议使用 ncurses。你似乎是在Windows上使用Python,所以你需要花点时间去了解如何获得类似的功能。可以看看这个讨论:Windows的Curses替代方案

撰写回答