Python - Curses - Addstr -multicolored string/understanding functions - python

I don't know what the right words are to ask my question, so please excuse the extra detail below. I am as much asking for the right words/concepts as for an answer to the specific question.
I'm trying to put a simple console in front of a script of mine using curses in Python. I want the console to look relatively familiar and have key shortcuts for 3 commands (Load, Exit, Continue). To highlight which key is the hotkey for an action, I wanted that letter to be in a different colour. (e.g. Exit with hotkey being the x). I figure this must be made up of 3 "addstr" commands- write the first letter in normal ("E"), then the x with a colour attribute, then "it" in normal colour again.
I thought that because I do this 3 times, and maybe more in future screens, I should make a function to do it for me to see if that works. What I can't figure out though, is how to edit the screen without hardcoding the function to the variable name. I want to be able to call the function in a number of different windows. I thought at first I could just pass the screen's variable into my function but that doesn't seem right.
Here is my pseudo code I started working on:
def keyString(menuString,fastChar,highlight,startX,startY,cScreen):
#menuString is the word that has a letter to bring to attention
#fastChar is the character that will be in a different colour
#highlight is binary value to determine which colour pair to use
#definition expects 'h' and 'n' to be colour pairs
#startX and startY are the beginning cursor positions
#cScreen would be global screen variable
fCidx = menuString.find(fastChar) #index of the character to highlight
fXadj = startX + fCidx #set the x position for character to highlight
sHx = fXadj + 1 #set the x position for the remainder of the string
fH = menuString[0:fCidx] #Slice the first half of the string
sH = menuString[(fCidx+1):] #slice the remainder of the string
if highlight:
txtColor = h
else:
txtColor = n
cScreen.addstr(startY,startX,fH,txtColor)
cScreen.addstr(startY,fXadj,fastChar)
cScreen.addstr(startY,sHx,sH,txtColor)
return cScreen
Please ignore the awful variable names..I was getting tired of typing and started shorthanding. I realise that I didn't need to worry about explicitly stating x,y coords because the cursor position is remembered. So a lot of that can be cut out. I'm not asking for someone to fix my function. I just don't have a concept of how to have a function that will write out a word using different colours for different characters. I could probably stick a "global screen" in the function and only use it for editing "screen", but then (for example) I wouldn't be able to use the function for "screen2".

If it helps anyone searching in the future, I found that I can use Windows (curses.newwin) and those can be fed into, and returned from functions.
So for example, if the above code was in a file called "curse_tools.py":
import curses
import curse_tools
def Main(screen):
curses.init_pair(1,curses.COLOR_GREEN, curses.COLOR_BLACK)
curses.init_pair(2,curses.COLOR_BLACK, curses.COLOR_GREEN)
n = curses.color_pair(1)
h = curses.color_pair(2)
curse_tools.n = n
curse_tools.h = h
try:
screen.border(0)
box1 = curses.newwin(20, 20, 5, 5)
box1.box()
box1=curse_tools.keyString("Exit","x",False,1,1,box1)
screen.refresh()
box1.refresh()
screen.getch()
finally:
curses.endwin()
curses.wrapper(Main)
This code would work. I'm going to re-write my original code because I learned a lot along the way but maybe a future beginner will somehow come across this question so I thought I'd post the 'solution'. Although I still don't know the right words.
Most of the code in this post came from Why won't my curses box draw? (in case it looks familiar)

Related

Characters S, 5, and 3 in Python with Turtle Graphics?

Currently I am trying to use commands to tell a turtle to draw the characters "S," "3," and "5" using the turtle module - without using the command turtle.write. Currently my code looks like:
import turtle
def halfcircle(parts=360, line=1, direction=1):
for x in range(parts//2):
turtle.forward(line)
turtle.left(360/parts * direction)
turtle.tracer(False)
for x in range(2):
halfcircle(20, 360/30, 1)
halfcircle(20, 360/30, -1)
turtle.update()
But the loop is never ending. Additionally, I am unable to incorporate linear and nonlinear lines into one character in order to draw a 5 or 3. Any help would be much appreciated.
Before I could actually check this out, I had to add an extra line of turtle.exitonclick() to the script to prevent it from disappearing from my screen as soon as I ran it.
From what I'm seeing, it doesn't appear to actually be never ending. The turtle draws four half-circle shapes then stops. When you say never ending, do you mean that it's repeating more than you wanted/expected? Or are you calling this code somewhere else and it's really never stopping in some other part?
If you just mean that it's drawing too many half-circles (4, when you want 2 to make an S), that's because of the 2 in your for-loop for x in range(2): that for-loop has two half-circles in it, so when the loop runs twice (because of range(2)), you get 2 * 2 = 4 half-circles. [BTW, as a convention, we generally use a single underscore _ to indicate "throw-away" variables. You'll see that the var x does not actually get used in your for-loop, it's just necessary to state some var for the for-loop syntax. A lot of Python programmers just write for _ in range(2) as a way of saying, "Don't pay attention to this var, it's not going to get used."]
So if you change the for-loop from range(2) to being range(1), you get a single pair of half-circles and the program halts on this image:
Which, admittedly, is not a great S shape, but it's getting there. In general, drawing symbols is hard. I think a really good S is probably the hardest one. I think the easiest might be a 5? The best approach to figuring out how to code symbols is to draw it very large and very carefully on graph paper, then break it down into its component parts. Let's use 5 as an example, working from bottom to top:
A curved section, maybe half-circle, maybe more like 2/3's of a circle, maybe more of an ellipse, kinda hard to be certain.
A straight line going (mostly) up (or maybe just a hair to the right)
A straight line going directly right, a little longer than the previous line
Then, with that series of motions in mind, create code to draw each piece on its own. Once you've done that, just have the turtle draw the first piece, then immediately draw the next, then the next. You'll have to figure out how to reset the turtle's direction between pieces, but other than that, you'll have a 5! It's not so much that you want to have nonlinear and linear segments together, as a single symbol is created by drawing one segment after another, and you just write code that draws the specific segment one at a time.

_curses.error: add_wch() returned an error

I have the following code rendering the display for my roguelike game. It includes rendering the map.
def render_all(self):
for y in range(self.height):
for x in range(self.width):
wall = self.map.lookup(x,y).blocked
if wall:
self.main.addch(y, x, "#")
else:
self.main.addch(y, x, ".")
for thing in self.things:
draw_thing(thing)
It errors out every time. I think it's because it's going off the screen, but the height and width variables are coming from self.main.getmaxyx(), so it shouldn't do that, right? What am I missing? Python 3.4.3 running in Ubuntu 14.04 should that matter.
That's expected behavior. Python uses ncurses, which does this because other implementations do this.
In the manual page for addch:
The addch, waddch, mvaddch and mvwaddch routines put the
character ch into the given window at its current window
position, which is then advanced. They are analogous to
putchar in stdio(3). If the advance is at the right margin:
The cursor automatically wraps to the beginning of the
next line.
At the bottom of the current scrolling region, and if
scrollok is enabled, the scrolling region is scrolled
up one line.
If scrollok is not enabled, writing a character at the
lower right margin succeeds. However, an error is
returned because it is not possible to wrap to a new
line
Python's curses binding has scrollok. To add characters without scrolling, you would call it with a "false" parameter, e.g.,
self.main.scrollok(0)
If you do not want to scroll, you can use a try/catch block, like this:
import curses
def main(win):
for y in range(curses.LINES):
for x in range(curses.COLS):
try:
win.addch(y, x, ord('.'))
except (curses.error):
pass
curses.napms(1)
win.refresh()
ch = win.getch()
curses.wrapper(main)

Issue with differentiating between two of the same items in a tkinter menu

The program I created allows for any letter on the keyboard the user types to be written on the turtle graphics canvas. In my program, I have also created a Python menu to which a Point object (for each letter function) is written to every time a user executes a function/draws a letter. However, because of the nature of my program, the user can also attach two of the same functions to the menu. If two of the same things get attached to the menu, for example two functions, I need a way to differentiate between them somehow. To do this, I have created a counter in another function and called that counter function in the function where the menu is written to, like so:
Counter function:
def increase():
if not hasattr(increase, "counter"):
increase.counter = 0
increase.counter += 1
Code block when menu is written to:
global loki
kli.config(state = NORMAL)
loki = ("{}".format(po.getfunction()))
increase() #<-- Counter function
undo1.add_command(label = str(increase.counter) + Point.__str__(po), command = lambda: selectundo(undo1.index(po)))
Point.__str__ is this method in the Point class:
def __str__(self):
return "({})".format(self.function)
However, I keep getting this error whenever I select something from the menu:
undo1.add_command(label = str(increase.counter) + Point.__str__(po), command = lambda: selectundo(undo1.index(po)))
File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/tkinter/__init__.py", line 2782, in index
i = self.tk.call(self._w, 'index', index)
tkinter.TclError: bad menu entry index "(<function draw_O at 0x105834d90>)"
I am thinking it has something to do with the following function, which undoes the function that is selected from the menu, but I am not sure:
def selectundo(x):
# This function undoes the function selected from the menu
for ty in range(x, len(function)):
undoHandler()
update()
listen()
Although, before I concatenated str(increase.counter) to Point.__str__(po), it worked perfectly.
So, what am I doing wrong here? Any help at all is much appreciated! :)
EDIT: Just to clear up what I am trying to do and why, I am trying to differentiate between two (or more) of the same functions if they are written to the menu and I am doing this because of the selectundo function (shown above) since, for example, if the user draws two (or more) of the same letter, I need to be able to differentiate between them, because right now, when I can't, the selectundo function undoes ALL instances of that letter, NOT just the first instance of what is pressed on the menu, which is what I actually want the program to do. If what I am trying to do to accomplish the task is not possible or if there is a better way to accomplish the task, please tell be about any/the other way that I can use to accomplish the task. I hope this helps alleviate any confusion! :)

Get pixel location from mouse click in TKinter

I'm quite new to Python and have been unsuccessful in finding a way around this problem. I have a GUI using TKinter that displays an image using Label. I would like the user to be able to click on two places in the image and use those two pixel locations elsewhere.
Below is the basic code I'm using so far, but I'm unable to return the pixel locations. I believe bind is not what I want to use, is there another option?
px = []
py = []
def onmouse(event):
px.append(event.x)
py.append(event.y)
return px,py
self.ImgPanel.bind('<button-1>',onmouse)
If I try to use:
px,py = self.ImgPanel.bind('<button-1>',onmouse)
I get an error "Too many values to unpack"
bind is what you want, if you want to capture the x,y coordinate of the click. However, functions called from bindings don't "return". Technically they do, but they return a value to the internals of Tkinter.
What you need to do is set an instance or global variable within the bound function. In the code you included in your question, if you add global px,py, you can then use those values in other code.

top down friction in pymunk

Been struggling with this for a couple of days, hard to find code examples on the net.
I'm making a topdown game and having trouble getting the player to move on key press. At the moment i'm using add_force or add_impulse to move the player in a direction, but the player doesn't stop.
I've read about using surface friction between the space and the player to simulate friction and here is how it's done in the tank.c demo.
However I don't understand the API enough to port this code from chipmunk into pymunk.
cpConstraint *pivot = cpSpaceAddConstraint(space, cpPivotJointNew2(tankControlBody, tankBody, cpvzero, cpvzero));
So far, I have something that looks like this:
class Player(PhysicalObject):
BASE_SPEED = 5
VISIBLE_RANGE = 400
def __init__(self, image, position, world, movementType=None):
PhysicalObject.__init__(self, image, position, world)
self.mass = 100
self.CreateBody()
self.controlBody = pymunk.Body(pymunk.inf, pymunk.inf)
self.joint = pymunk.PivotJoint(self.body, self.controlBody, (0,0))
self.joint.max_force = 100
self.joint.bias_coef = 0
world.space.add(self.joint)
I don't know how to add the constraint of the space/player to the space.
(Need someone with 1500+ rep to create a pymunk tag for this question).
Joe crossposted the question to the Chipmunk/pymunk forum, and it got a couple of more answers there. http://www.slembcke.net/forums/viewtopic.php?f=1&t=1450&start=0&st=0&sk=t&sd=a
Ive pasted/edited in parts of my answer from the forum below:
#As pymunk is python and not C, the constructor to PivotJoint is defined as
def __init__(self, a, b, *args):
pass
#and the straight conversion from c to python is
pivot1 = PivotJoint(tankControlBody, tankBody, Vec2d.zero(), Vec2d.zero())
# but ofc it works equally well with 0 tuples instead of the zero() methods:
pivot2 = PivotJoint(tankControlBody, tankBody, (0,0), (0,0))
mySpace.add(pivot1, pivot2)
Depending on if you send in one or two arguments to args, it will either use the cpPivotJointNew or cpPivotJointNew2 method in the C interface to create the joint. The difference between these two methods is that cpPivotJointNew want one pivot point as argument, and the cpPivotJointNew2 want two anchor points. So, if you send in one Vec2d pymunk will use cpPivotJointNew, but if you send in two Vec2d it will use cpPivotJointNew2.
Full PivotJoint constructor documentation is here: PivotJoint constructor docs
I'm not familiar with either system you've mentioned, but some basic game ideas that may relate are:
If you add a force (or impulse) which affects movement, for the entity to stop, you must also take it away. In my games if I had a function AddImpulse()/AddForce() I would have a corresponding one such as Apply_Friction() which would reverse the effect by however much you want (based on terrain?) until movespeed is zero or less. I personally wouldn't bother with this method for movement unless needed for gameplay since it can add more computations that its worth each update.
There should be some way to track KeyPressed and/or KeyPosition and then using those x/y coordinates are incrememnted based on player speed. Without knowing what you've tried or how much the API does for you, it's hard to really say more.
Hope this helps. If this is stuff you already knew kindly ignore it.

Categories