How to reuse completions from PathCompleter in prompt_toolkit - python

I am creating a REPL tool for my project that (simplified for clarity) either directly executes entered commands or (if a command ".x some/path/to/file" is entered) reads and executes them from file. My question is related to auto-completing the user input (using prompt_toolkit).
I have something like (minimum executable example):
import prompt_toolkit
from prompt_toolkit.completion import Completer, Completion
from prompt_toolkit.document import Document
from prompt_toolkit.contrib.completers import PathCompleter
class CommandCompleter(Completer):
def __init__(self):
self.path_completer = PathCompleter()
self.commands = [".x", "command1", "command2"]
def get_completions(self, document, complete_event):
if document.text.startswith(".x "):
sub_doc = Document(document.text[3:])
yield from (Completion(cmd.text, -document.cursor_position)
# ???????? ?????????????????????????
for cmd
in self.path_completer.get_completions(sub_doc, complete_event))
# ???????
else:
yield from (Completion(cmd, -document.cursor_position)
for cmd in self.commands
if cmd.startswith(document.text))
if __name__ == "__main__":
while True:
other_args = {}
input = prompt_toolkit.prompt(">>> ", completer=CommandCompleter(), **other_args)
# Do something with input (omitted)
The second if-branch (for commands) works correctly but I don't know how to properly call the PathCompleter.get_completions() method and reconstruct the Completion objects from its result (where the ???'s are) in the first branch. The trick is that I am using the completion only for a part of the input and various sub-stringing, position calculations etc. did not (yet) lead to the satisfactory behaviour (i.e. offering the paths and constructing the correct input line).
I will definitely go on searching but if anyone knows how to rewrite this, it would be very useful.
Note: yield from self.path_completer.get_completions(document, complete_event) would be used if the whole input would be just the path (and this works correctly).

Probably the following should fix it:
sub_doc = Document(document.text[3:])
yield from (Completion(completion.text, completion.start_position, display=completion.display)
for completion
in self.path_completer.get_completions(sub_doc, complete_event))
completion.text contains the text that is going to be inserted;
completion.start_position contains the place where the text is going to be inserted, relative to the cursor position (in this particular example we can take the value from the nested completer).
completion.display is the value displayed in the pop-up menu. (In this case, the whole filename, rather than only the inserted string.
Feel free to open a GitHub issue if you have any more questions.

Related

Pymel: How do I close For loop after deleting null groups?

As I continue to study For Loops: I've run into some annoying errors. The problem is the script does exactly what I want it to. It deletes the null groups under the demo joints: but unlike other loops I've made for renaming which can be closed with a transform flag in the cmds.ls command: cmds.listRelatives doesn't allow a transform flag to close out the loop. You run the script by simply clicking Build Examples then hitting Delete Waste Groups
I've tried every flag according to the Maya documentation: but nothing seems to be closing the loop. I dont know if I need another variable, or a combination of some flags: or if I am using the wrong type of wording: but ideally what I would like this script to do is simply close out the loop so I dont get the error Error: No object matches name: curve
'''
import DS_wasteGroup_cleanerDemo
reload (DS_wasteGroup_cleanerDemo)
DS_wasteGroup_cleanerDemo.gui()
'''
import re
import maya.cmds as cmds
import maya.mel as mel
if cmds.window("renameWin", exists =True):
cmds.deleteUI("renameWin", window = True)
myWindow = cmds.window("renameWin",t='DS_wasteGroup_cleanerDemo',w=200, h=500, toolbox=True)
column = cmds.columnLayout(adj=True)
def gui():
cmds.button( label="Build Examples", c = buildExamples)
cmds.separator( w=200, h=3)
cmds.button( label="Delete Waste Groups", c = deleteWasteGrp)
cmds.separator( w=200, h=9)
cmds.setParent('..')
cmds.showWindow(myWindow)
def buildExamples(*args):
cmds.group(n='exampleGroup1',world=True,empty=True)
cmds.joint(n='demoJoint1')
cmds.group(n='curve1',world=True,empty=True)
cmds.parent('curve1','demoJoint1')
cmds.joint(n='demoJoint2')
cmds.parent('demoJoint2','exampleGroup1')
cmds.group(n='curve2',world=True,empty=True)
cmds.parent('curve2','demoJoint2')
cmds.joint(n='demoJoint3')
cmds.parent('demoJoint3','exampleGroup1')
cmds.group(n='curve3',world=True,empty=True)
cmds.parent('curve3','demoJoint3')
cmds.joint(n='demoJoint4')
cmds.parent('demoJoint4','exampleGroup1')
cmds.group(n='curve4',world=True,empty=True)
cmds.parent('curve4','demoJoint4')
cmds.joint(n='demoJoint5')
cmds.parent('demoJoint5','exampleGroup1')
cmds.group(n='curve5',world=True,empty=True)
cmds.parent('curve5','demoJoint5')
def deleteWasteGrp(*args):
grpList = cmds.listRelatives('demoJoint*',p=True,f=True)
for name in grpList:
print(grpList)
cmds.delete('curve*')
My apologies if I'm posting simple questions. I do write Python scripts to automate the most tedious tasks in rigging: but my knowledge is only intermediate. I want to learn more python so my scripts arent so clunky and brute forced: as well as the fact that I need them to be more adaptable to various types of characters: so any resources that dumb all this down would also be appreciated. Thank you for your help.
The error is correct, because the very first time the for loop executes, all "curve" obects are deleted, then in the next iteration, the same command does not find any curve objects because they are already deleted. If you place the delete command outside the for loop, the error should disappear.
Honestly I would take a whole different approach as you're hard-coding everything which could easily lead to disaster. When I mean hard-code, I mean you're trying to parent, let's say, "demoJoint2" to an object. This is bad because why are you assuming that "demoJoint2" even exists? If you create an object with a specific name that already exists, Maya will auto-rename the new object, and now you're referencing the wrong one right off the bat! Instead when you create your objects, capture their names in a variable then work with that, otherwise you'll be constantly shooting yourself in the foot.
Here's the same script with an approach I would try instead:
import maya.cmds as cmds
def gui():
if cmds.window("renameWin", exists=True):
cmds.deleteUI("renameWin", window=True)
myWindow = cmds.window("renameWin", t="DS_wasteGroup_cleanerDemo", w=200, h=500, toolbox=True)
column = cmds.columnLayout(adj=True)
cmds.button(label="Build Examples", c=buildExamples)
cmds.separator(w=200, h=3)
cmds.button(label="Delete Waste Groups", c=deleteWasteGrp)
cmds.separator(w=200, h=9)
cmds.setParent("..")
cmds.showWindow(myWindow)
def buildExamples(*args):
root = cmds.group(n="exampleGroup1", world=True, empty=True)
for i in range(5): # Loop to the amount of joints you want to create.
jnt = cmds.createNode("joint", name="demoJoint" + str(i + 1)) # Use `i` to help name the object.
jnt = cmds.parent(jnt, root)[0] # Parenting changes its long name, so recapture the joint in a variable.
crv = cmds.group(n="curve" + str(i + 1), world=True, empty=True) # Create empty group.
cmds.parent(crv, jnt) # Parent curve to joint.
def deleteWasteGrp(*args):
jnts = cmds.ls("demoJoint*", long=True, type="joint") # Get all `demoJoints`.
children = cmds.listRelatives(jnts, f=True, children=True, type="transform") or [] # Get all of their children, and only get transform types.
curves = [obj for obj in children if obj.split("|")[-1].startswith("curve")] # Don't assume we got the right objects. Run a final loop to collect any object that starts with `curve`. Need to use split as we're looping through long names but need to check its short name.
if curves: # `cmds.delete` will error if this list is empty, so don't assume.
cmds.delete(curves) # Delete all curves at once.
gui()
Now I can hit the build button as much as I want with no issues, and delete all the curves when pressing the delete button.
A few more notes:
Notice in buildExamples I'm using a loop to create all the objects instead of reusing redundant code that does the same thing. You could even have a spinbox in your gui that defines how many joints it creates now, where as before it wasn't possible because the count was hard-coded.
cmds.listRelatives does have a way to filter objects by transforms by setting parameter type="transform". In fact you'll see many commands have this same parameter (again start checking docs).
cmds.listRelatives('demoJoint*',p=True,f=True) was grabbing the joint's parent, not its children. The docs clearly state this.
Running cmds.delete('curve*') is going to delete ALL objects with names that start with curve, and since you're running this in a loop it's trying to do this multiple times.
maya.cmds is not pymel. There's a whole separate module called pymel.
If you're unsure with any parts of the code try adding in a print statement to see what it's doing.
I feel like you're going about this whole process a bit wrong, and I would love to elaborate if you're interested, but for now here is a fix for your loop situation:
def deleteWasteGrp(*args):
curveList = cmds.ls('curve*',transforms=True)
try:
cmds.delete(curveList)
print('Deleted the following objects: {}'.format(curveList))
except Exception as e:
cmds.warning('Cleanup failed: {}'.format(e))
The cmds.delete method accepts a list parameter, which in your case is the easiest way to get the job done. Keep in mind that when you delete a parent object, you also delete its children, so depending on your circumstances deleting objects can be order-specific.
Throwing any "likely to fail" calls in a try/except clause is generally a good idea, as it lets you handle the error gracefully. Be careful, however, to not suppress it and just move on -- you at the very least need to alert the user adequately.
Lastly, your buildExamples method will most likely fail if you run it more than once. Because you are addressing objects by string literals (hard coded names) instead of keeping track of their actual names (and full path). You will likely see this error eventually:
# Error: ValueError: file <maya console> line ??: More than one object matches name: demoJoint1 #
Edit: Some elaborations as requested
The commands cmds.group and cmds.joint return a string value indicating the actual name of the object created (in create mode). It's usually a good idea of storing this value in case Maya decides to name your object slightly differently than what you are expecting, usually when there is a naming clash. Eg:
print cmds.group(name='test', world=True, empty=True)
# Returns: test
print cmds.group(name='test', world=True, empty=True)
# Returns: test1
Example of how to capture object names as you create them. I've concatenated your five identical(ish) calls to create joints and curves in this loop:
import maya.cmds as cmds
topGroupName = 'exampleGroup'
actualTopGroupName = None
# Create top level group
actualTopGroupName = cmds.group(n=topGroupName, world=True, empty=True)
# Loop through 5 times and do the following:
for i in range(5):
# PS: hash character in name indicates "next available number"
cmds.select(clear=True)
jnt = cmds.joint(n='demoJoint#')
crv = cmds.group(n='curve#',world=True,empty=True)
cmds.parent(crv, jnt)
cmds.parent(jnt, actualTopGroupName)
Example of how to narrow down which objects to search for with cmds.ls:
topGroupName = 'exampleGroup'
print cmds.ls('|{}*|*|curve*'.format(topGroupName))
# Returns: [u'curve1', u'curve2', u'curve3', u'curve4', u'curve5']
# The string .format() expression is just a dynamic way of writing this:
# |exampleGroup*|*|curve*
Vertical pipes (|) indicate levels in a hierarchy, similar to how slashes (/) work in URLs. And asterisks/wildcards (*) indicate "any character, or none".
Hope this helps you along your way a little bit.

Python GTK get selected value from the treeview

I am working on a mini GUI project , I am currently struggling to figure out how to get selected value from the list and then return that value to the main function so that I can use that value in somewhere else . Can someone help me please !!!!
####
self.device_list_store = gtk.ListStore(str,str,str,str,str)
for device in self.get_dev_list():
self.device_list_store.append(list(device))
device_list_treeview = gtk.TreeView(self.device_list_store)
selected_row = device_list_treeview.get_selection()
selected_row.connect("changed",self.item_selected)
####
def item_selected(self,selection):
model,row = selection.get_selected()
if row is not None:
selected_device = model[row][0]
at the moment ,the item_selected function is not returning anything , I want to return selected_device back to the main function so I can use it in other functions as well .
EDIT: I've edited code above to remove formatting errors #jcoppens
As you can see in the documentation, the item_selected function is called with one parameter, tree_selection. But if you define the function inside a class, it requires the self parameter too, which is normally added automatically. In your (confusing) example, there is no class defined, so I suspect the problem is your program which is incomplete.
Also, I suspect you don't want device_list_treeview = gtk.T... in the for loop:
for device in self.get_dev_list():
self.device_list_store.append(list(device))
device_list_treeview = gtk.TreeView(self.device_list_store)
And I suspect you want selected_device = mod... indented below the if:
if row is not None:
selected_device = model[row][0]
Please convert your example in a complete program, and formatted correctly.
BTW: item_selected is not a good name for the signal handler. It is also called if the item is unselected (which is why the signal is called 'changed')!
And important: Even though you should first read the basic Python tutorials and Gtk tutorials, you should then consider using lazka's excellent reference for all the Python APIs. There's a link on the page to download it completely and have it at hand in your computer.

python dragonfly to recognize similar words

I am doing a program with dragon fly using wsr,where it has to analyse a word,any voice matching that word should output 'yes it matches'
If i say 'czechoslovakia' then it must print true even for all the similar matches of this world ,like words for 'circle slovakia, cat on slavia,seko vakia...'
What specific methods,should i use for this?
My program
from dragonfly.all import *
import pythoncom
import time
# Voice command rule combining spoken form and recognition processing.
class ExampleRule(CompoundRule):
spec = "czechoslovakia|circle slovalia|sceko bakia|cat on ania" # Spoken form of command.
def _process_recognition(self, node, extras): # Callback when command is spoken.
print "Voice command spoken."
# Create a grammar which contains and loads the command rule.
grammar = Grammar("example grammar") # Create a grammar to contain the command rule.
grammar.add_rule(ExampleRule()) # Add the command rule to the grammar.
grammar.load() # Load the grammar.
while True:
pythoncom.PumpWaitingMessages()
time.sleep(.1)
There is nothing built into Dragonfly to allow you to do this, but you have some other options.
If you're looking to dynamically generate the spec, you might want
to look at Fuzzy. You could give it a word and use it to generate
other similar sounding words from that word. Then you could create
the spec from them.
Here is the WSR engine class in Dragonfly.
I don't know much about SAPI5, but you might be able to ask it for
alternatives. If you can, you might be able to extend the
Dragonfly GrammarWrapper to expose the alternatives, and then use a
catchall grammar to save all utterances and then filter out what you
want (possibly using Fuzzy).
If you were using Natlink, I would recommend
looking at the results object. As you can see here, the results
object has access to all of Dragon's different hypotheses for what
you said in a given utterance. Just as with my second suggestion,
you could catch everything and then filter what you wanted:
.
from natlinkutils import GrammarBase
class CatchAll(GrammarBase):
# this spec will catch everything
gramSpec = """
<start> exported = {emptyList};
"""
def initialize(self):
self.load(self.gramSpec, allResults=1)
self.activateAll()
def gotResultsObject(self, recogType, resObj):
for x in range(0, 100):
try:
possible_interpretation = resObj.getWords(x)
# do whatever sort of filtering you want here
except Exception:
break
c = CatchAll()
c.initialize()
def unload():
global c
if c:
c.unload()
c = None

Copy FORTRAN (called via F2PY) output in Python

I am using some fortran code in python via f2py. I would like to redirect the fortran output to a variable I can play with. There is this question which I found helpful.
Redirecting FORTRAN (called via F2PY) output in Python
However, I would also like to optionally have the fortran code write to the terminal as well as recording it. Is this possible?
I have the following silly class which I cobbled together from the question above and also from
http://websrv.cs.umt.edu/isis/index.php/F2py_example.
class captureTTY:
'''
Class to capture the terminal content. It is necessary when you want to
grab the output from a module created using f2py.
'''
def __init__(self, tmpFile = '/tmp/out.tmp.dat'):
'''
Set everything up
'''
self.tmpFile = tmpFile
self.ttyData = []
self.outfile = False
self.save = False
def start(self):
'''
Start grabbing TTY data.
'''
# open outputfile
self.outfile = os.open(self.tmpFile, os.O_RDWR|os.O_CREAT)
# save the current file descriptor
self.save = os.dup(1)
# put outfile on 1
os.dup2(self.outfile, 1)
return
def stop(self):
'''
Stop recording TTY data
'''
if not self.save:
# Probably not started
return
# restore the standard output file descriptor
os.dup2(self.save, 1)
# parse temporary file
self.ttyData = open(self.tmpFile, ).readlines()
# close the output file
os.close(self.outfile)
# delete temporary file
os.remove(self.tmpFile)
My code currently looks something like this:
from fortranModule import fortFunction
grabber = captureTTY()
grabber.start()
fortFunction()
grabber.stop()
My idea is to have a flag called silent that I could use to check whether I allow the fortran output to be displayed or not. This would then be passed to the captureTTY when I construct it, i.e.
from fortranModule import fortFunction
silent = False
grabber = captureTTY(silent)
grabber.start()
fortFunction()
grabber.stop()
I am not really sure how to go about implementing this. The obvious thing to do is:
from fortranModule import fortFunction
silent = False
grabber = captureTTY()
grabber.start()
fortFunction()
grabber.stop()
if not silent:
for i in grabber.ttyData:
print i
I am not a big fan of this, as my fortran method takes a long time to run, and it would be nice to see it updated in real time and not just at the end.
Any ideas? The code will be run on Linux & Mac machines, not windows. I've had a look around the web, but haven't found the solution. If there is one, I am sure it will be painfully obvious!
Cheers,
G
Clarification:
From the comments I realise that the above isn't the clearest. What I currently have is the capability to record the output from the fortran method. However, this prevents it from printing to the screen. I can have it print to the screen, but then cannot record it. I want to have the option to do both simultaneously, i.e. record the output and have it print to the screen in real time.
Just as an aside, the fortran code is a fitting algorithm and the actual output that I am interested is the parameters for each iteration.
Have you tried something like this in the Fortran subroutine? (Assuming foo is what you want to print, and 52 is the unit number of your log file)
write(52,*) foo
write(*,*) foo
This should print foo to the log file and to the screen.

Python: Networked IDLE/Redo IDLE front-end while using the same back-end?

Is there any existing web app that lets multiple users work with an interactive IDLE type session at once?
Something like:
IDLE 2.6.4
Morgan: >>> letters = list("abcdefg")
Morgan: >>> # now, how would you iterate over letters?
Jack: >>> for char in letters:
print "char %s" % char
char a
char b
char c
char d
char e
char f
char g
Morgan: >>> # nice nice
If not, I would like to create one. Is there some module I can use that simulates an interactive session? I'd want an interface like this:
def class InteractiveSession():
''' An interactive Python session '''
def putLine(line):
''' Evaluates line '''
pass
def outputLines():
''' A list of all lines that have been output by the session '''
pass
def currentVars():
''' A dictionary of currently defined variables and their values '''
pass
(Although that last function would be more of an extra feature.)
To formulate my problem another way: I'd like to create a new front end for IDLE. How can I do this?
UPDATE: Or maybe I can simulate IDLE through eval()?
UPDATE 2: What if I did something like this:
I already have a simple GAE Python chat app set up, that allows users to sign in, make chat rooms, and chat with each other.
Instead of just saving incoming messages to the datastore, I could do something like this:
def putLine(line, user, chat_room):
''' Evaluates line for the session used by chat_room '''
# get the interactive session for this chat room
curr_vars = InteractiveSession.objects.where("chatRoom = %s" % chat_room).get()
result = eval(prepared_line, curr_vars.state, {})
curr_vars.state = curr_globals
curr_vars.lines.append((user, line))
if result:
curr_vars.lines.append(('SELF', result.__str__()))
curr_vars.put()
The InteractiveSession model:
def class InteractiveSession(db.Model):
# a dictionary mapping variables to values
# it looks like GAE doesn't actually have a dictionary field, so what would be best to use here?
state = db.DictionaryProperty()
# a transcript of the session
#
# a list of tuples of the form (user, line_entered)
#
# looks something like:
#
# [('Morgan', '# hello'),
# ('Jack', 'x = []'),
# ('Morgan', 'x.append(1)'),
# ('Jack', 'x'),
# ('SELF', '[1]')]
lines = db.ListProperty()
Could this work, or am I way off/this approach is infeasible/I'm duplicating work when I should use something already built?
UPDATE 3: Also, assuming I get everything else working, I'd like syntax highlighting. Ideally, I'd have some API or service I could use that would parse the code and style it appropriately.
for c in "characters":
would become:
<span class="keyword">for</span> <span class="var">c</span> <span class="keyword">in</span> <span class="string>"characters"</span><span class="punctuation">:</span>
Is there a good existing Python tool to do this?
I could implement something like this pretty quickly in Nevow. Obviously, access would need to be pretty restricted since doing something like this involves allowing access to a Python console to someone via HTTP.
What I'd do is create an Athena widget for the console, that used an instance of a custom subclass of code.InteractiveInterpreter that is common to all users logged in.
UPDATE: Okay, so you have something chat-like in GAE. If you just submit lines to a code.InteractiveInterpreter subclass that looks like this, it should work for you. Note that the interface is pretty similar to the InteractiveSession class you describe:
class SharedConsole(code.InteractiveInterpreter):
def __init__(self):
self.users = []
def write(self, data):
# broadcast output to connected clients here
for user in self.users:
user.addOutput(data)
class ConnectedUser(object):
def __init__(self, sharedConsole):
self.sharedConsole = sharedConsole
sharedConsole.users.append(self) # reference look, should use weak refs
def addOutput(self, data):
pass # do GAE magic to send data to connected client
# this is a hook for submitted code lines; call it from GAE when a user submits code
def gotCommand(self, command):
needsMore = self.sharedConsole.runsource(command)
if needsMore:
pass # tell the client to change the command line to a textarea
# or otherwise add more lines of code to complete the statement
The closest Python interpreter I know of to what you are looking for, in terms of interface, is DreamPie. It has separate input and output areas, much like a chat interface. Also, DreamPie runs all of the code in a subprocess. DreamPie also does completion and syntax coloring, much like IDLE, which means it doesn't just pipe input and output to/from the subprocess -- it has implemented the abstractions which you are looking for.
If you wish to develop a desktop application (not a web-app), I recommend basing your work on DreamPie and just adding the multiple-frontend functionality.
Update: For syntax highlighting (including HTML) see the Pygments project. But that is a completely different question; please ask one question at a time here.
As a proof of concept, you may be able to put something together using sockets and a command-line session.
this is likely possible with the upcoming implimentation of IPython using a 0MQ backend.
I would use ipython and screen. With this method, you would have to create a shared login, but you could both connect to the shared screen session. One downside would be that you would both appear as the same user.

Categories

Resources