?
Solved

Python and Curses, mangled mess

Posted on 2006-10-31
7
Medium Priority
?
1,416 Views
Last Modified: 2007-12-19
Hi,
I'm fairly new to Python and completely confused when it comes to Curses. Perhaps an expert here can lend me a helping hand as I'm having a heck of a time trying to get any reason out of Curses.. the documentation for it is vague and not very helpful to me anymore, I can only ever seem to get the same couple of guides a million times on Google, so if anyone has any good tutorials or examples of this python socket curses client, that'd be nice too.

I am making a 3rd party client for a game. This game is typically graphical, however this client will be text only. I need a big huge window for a chat buffer and an input box below it for inputting text. The game is pretty much just a chat program, so the buffer will be filled with lots of "He says: Hi" and "She says: Bye".

My code so far is below... I was so frustrated I got rid of the bottom input box and just made it all the same.. I guess my main questions would be:
1. How can I add "scrolling", such that text does not get overwritten in the same spot in the output window all the time. I want each new line to be at the bottom and all lines before it to move up.
2. Please please please show me how to seperate the text input window from the output text window.
3. Any other hints or help would be appreciated!


from socket import *
import thread
import curses
import traceback

win = curses.initscr()
curses.cbreak()
win.keypad(1)
curses.echo()
max_y, max_x = win.getmaxyx()
begin_x = 1 ; begin_y = 1
win = curses.newwin(max_y-1, max_x-1, begin_y, begin_x)
win.border()
win.refresh()
fsocket = socket(AF_INET, SOCK_STREAM)
ADDR = ('blah.blahblah.com', 80)
fsocket.connect(ADDR)
fsocket.send("connect Username password\n")
def readdata():
        while 1:
                data = fsocket.recv(1024)
                try:
                        win.addstr(data)
                        win.refresh()
                except:
                        data = ""
thread.start_new_thread(readdata,tuple())
while 1:
        data = win.getstr(0,0)
        fsocket.send(data + "\n")
        win.refresh()
0
Comment
Question by:Tabris42
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
7 Comments
 
LVL 29

Expert Comment

by:pepr
ID: 17847889
Curses were designed in the middle age of computers. The main goal is to move the cursor to line Y and position X on the screen, type a character here (possibly some special character) and set the colour of the text or of the background on that position (simplified). It was clear at that time that each character occupies box of exactly same size. You could not change the font size nor the font face. You often could not change the number of lines and the number of charactes on the line.

In my opinion, you do not want to use curses for your purpose if you use _also_ another graphical window. You should focus on creating a normal GUI window using Tkinter, wxPython, wax or some similar framework. I would recommend the wxPython + wax. Then create a window with one big TextBox for collected texts and one smaller below for entering text.

Have a look at wax primer (http://zephyrfalcon.org/labs/wax_primer.html) that in steps shows how to write a simple text editor on 70 lines of code. After understanding that, you will be able to write you chat-like window. Inside TextBox, you can use various fonts, colors, etc.

For wax, you have to download and install wxPython and the wax. But it is pretty easy.
0
 
LVL 29

Expert Comment

by:pepr
ID: 17848051
See also another page related to wax http://zephyrfalcon.org/labs/wax.html
There is also the project on SourceForge http://sourceforge.net/projects/waxgui

You can try the following quick hack -- two windows that you can type in, but no added functionality (just to start):

from wax import *

FIXED_FONT = ('Courier New', 10)

class MainFrame(Frame):

    def Body(self):

        vPanel = Panel(self, direction='vertical')

        self.bigTextBox = TextBox(vPanel, multiline=True, wrap=False)
        self.bigTextBox.SetFont(FIXED_FONT)
        self.bigTextBox.SetSize((600,400))
        vPanel.AddComponent(self.bigTextBox, expand='both')

        self.smallTextBox = TextBox(vPanel, multiline=True, wrap=True)
        self.smallTextBox.SetFont(FIXED_FONT)
        self.bigTextBox.SetSize((600,50))
        vPanel.AddComponent(self.smallTextBox, expand='h')

        vPanel.Pack()
   
        self.AddComponent(vPanel, expand='both')
        self.Pack()

app = Application(MainFrame, title='My title')
app.Run()
0
 

Author Comment

by:Tabris42
ID: 17851495
That's nifty and something I may use another time but for this specific application I need to run from a terminal (like through PuTTy).. this is why I need curses (or something like it), to be able to seperate input from output while still using the command line interface..
0
Enroll in August's Course of the Month

August's CompTIA IT Fundamentals course includes 19 hours of basic computer principle modules and prepares you for the certification exam. It's free for Premium Members, Team Accounts, and Qualified Experts!

 
LVL 29

Expert Comment

by:pepr
ID: 17856024
Frankly, I have never programmed using curses. I guess that you have looked at the curses module standard documentation and found also the URL for "Curses Programming with Python" http://www.amk.ca/python/howto/curses/curses.html. It would probably be better to ask in Unix Prog. or Linux Prog. channel, because curses are probably used mainly on unixes.

The following documets also seems to be interesting. (They deal with ncurses, but as far as I know it is a superset of the older curses which is already implemented in the Python curses module.)

http://dickey.his.com/ncurses/ncurses-intro.html
http://www.tldp.org/HOWTO/NCURSES-Programming-HOWTO/

Good luck
0
 
LVL 1

Expert Comment

by:pmalherbe
ID: 17856094
Hi, I have written a small class which may be of use to you, I hope it helps.

#! /usr/bin/python
           
import curses, os, sys
           
class Start:
    def __init__(self):
        curses.wrapper(self.doDialog)
        self.endCurses()
        if self.next == "y": self.doProcess()
           
    def doDialog(self, event=None):
        win = self.createWindow(10, 2, 60, 15)    # x, y, w, h
        win.box()
        win.addstr(1, 24, "Primary Setup", curses.A_REVERSE)
        win.addstr(3, 1, "New Installation (y/n):")
        win.addstr(4, 1, "Date and Time (MMDDHHMM):")
        win.addstr(13, 1, "Continue (y/n):")
        self.new = self.inputString(win, 3, 30, 1, ("in", (chr(27), "y", "n")))
        if self.new == chr(27):
            self.endCurses()
            sys.exit()
        self.date  = self.inputString(win, 4, 30, 8, ("notblank",))
        self.next = self.inputString(win, 13, 30, 1, ("in", ("y", "n")))
        win.refresh()
       
    def createWindow(self, x, y, w, h):
        return curses.newwin(h, w, y, x)
       
    def createBox(self, scr, x, y, w, h):
        scr.addch(y, x, curses.ACS_ULCORNER)
        scr.addch(y, (x + w - 1), curses.ACS_URCORNER)
        scr.addch((y + h - 1), x, curses.ACS_LLCORNER)
        scr.addch((y + h - 1), (x + w - 1), curses.ACS_LRCORNER)
        scr.vline((y + 1), x, curses.ACS_VLINE, (h - 2))
        scr.hline(y, (x + 1), curses.ACS_HLINE, (w - 2))
        scr.vline((y + 1), (x + w - 1), curses.ACS_VLINE, (h - 2))
        scr.hline((y + h - 1), (x + 1), curses.ACS_HLINE, (w - 2))
        scr.refresh()

    def inputChar(self, w, y, x, l=1):
        curses.echo()
        c = ""
        for x in range(x, (x+l)):
            i = w.getch(y, x)
            if i == 10: break
            c += chr(i)
        curses.noecho()
        return c

    def inputString(self, w, y, x, l, verify=None):
        def doInput(w, y, x, l, verify):
            curses.echo()
            c = w.getstr(y, x, l)
            curses.noecho()
            if not verify: return c
            if verify[0] == "notblank" and c: return c
            if verify[0] == "in" and c in verify[1]: return c
            if verify[0] == "dir" and os.path.isdir(c): return c
        c = None
        while c == None: c = doInput(w, y, x, l, verify)
        return c

    def endCurses(self):
        curses.nocbreak()
        ##self.stdscr.keypad(0)
        curses.echo()
        curses.endwin()

    def doProcess(self):
        # Do whatever you want with the input
        pass

if __name__ == "__main__":
    Start()

0
 
LVL 14

Accepted Solution

by:
RichieHindle earned 2000 total points
ID: 17905980
There are much higher-level libraries that curses for this kind of thing.  One of them is urwid - here's roughly the program you describe, implemented using urwid.  It provides automatic scrolling as you describe, and also provides scrollback using the Up and Down keys.  It resizes as you resize your terminal window as well.  It's a simplified version of the urwid IRC example here: http://excess.org/urwid/inyyssonen_twisted_irc.py.html

#!/usr/bin/python

import urwid.curses_display
import urwid

class Screen:
    def __init__(self, tui):
        self.tui = tui

    def addLine(self, text):
        self.lines.append(urwid.Text(text))
        self.listbox.set_focus(len(self.lines) - 1)
        self.redisplay()

    def redisplay(self):
        canvas = self.frame.render(self.size, focus=True)
        self.tui.draw_screen(self.size, canvas)

    def run(self):
        self.size = self.tui.get_cols_rows()
       
        self.lines = [urwid.Text('Hello')]
        self.listbox = urwid.ListBox(self.lines)
        self.input = urwid.Edit()

        self.frame = urwid.Frame(self.listbox, footer=self.input)
        self.frame.set_focus('footer')

        self.redisplay()

        while 1:
            keys = self.tui.get_input()

            for key in keys:
                if key == 'window resize':
                    self.size = self.tui.get_cols_rows()
                elif key == 'enter':
                    text = self.input.get_edit_text()
                    self.input.set_edit_text('')
                    self.addLine(text)
                elif key in ('up', 'down', 'page up', 'page down'):
                    self.listbox.keypress(self.size, key)
                else:
                    self.frame.keypress(self.size, key)

                self.redisplay()

if __name__ == '__main__':
    tui = urwid.curses_display.Screen()
    screen = Screen(tui)
    tui.run_wrapper(screen.run)
0
 

Author Comment

by:Tabris42
ID: 17908454
That is exactly the kind of thing I'm looking for. Thanks a lot!
0

Featured Post

Free Tool: Path Explorer

An intuitive utility to help find the CSS path to UI elements on a webpage. These paths are used frequently in a variety of front-end development and QA automation tasks.

One of a set of tools we're offering as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Installing Python 2.7.3 version on Windows operating system For installing Python first we need to download Python's latest version from URL" www.python.org " You can also get information on Python scripting language from the above mentioned we…
Flask is a microframework for Python based on Werkzeug and Jinja 2. This requires you to have a good understanding of Python 2.7. Lets install Flask! To install Flask you can use a python repository for libraries tool called pip. Download this f…
Learn the basics of strings in Python: declaration, operations, indices, and slicing. Strings are declared with quotations; for example: s = "string": Strings are immutable.: Strings may be concatenated or multiplied using the addition and multiplic…
Learn the basics of while and for loops in Python.  while loops are used for testing while, or until, a condition is met: The structure of a while loop is as follows:     while <condition>:         do something         repeate: The break statement m…
Suggested Courses

771 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question