How to access the Maya main menu and toolbar? - python

I use Autodesk Maya 2016 + Python.
I had a problem. I need to replace the functions of standard menu items and toolbars. For example, you must add the function to save a recording function. And because different people use different functionality, you have to change everything at once. Someone presses Ctrl+S, others go to the menu File->Save, and some press icon on the toolbar. In all areas it is necessary to replace the functionality. I understand that the problem boils down to in order to access the menus, toolbars and keyboard shortcuts. Next, find the associates item on the estate to know what function is now called. Then replace it with their cause and at the end of the function that invoked earlier in this menu.
how to get Maya main menu (like File and other)?
how to get Maya main toolbar?
how to get Maya hotkeys?
Maya is based on QT. So just need to get the objects, and then have the standard means of QT can do everything.
How to do it?

All three ways of doing this call the runTimeCommand named SaveScene. Override that command and you have hijacked all of the three ways you describe. This is a bit tricky since the command is marked with the flag -default which makes it impossible to change them on the fly. You can hook them where they are first defined. They are defined in the file:
Mayadir/scripts/startup/defaultRunTimeCommands.mel
Copy this file to your user profile maya script directory or studio script directry. These will get precedence of factory script due to resolution order. Do not overwrite the factory file. Then change -command in the lines,
runTimeCommand -default true
-annotation (uiRes("m_defaultRunTimeCommands.kSaveSceneAnnot"))
-category ("File")
-command ("checkForUnknownNodes(); FileMenu_SaveItem")
SaveScene;
to something else. Best would be just to add a hook here.
Obviously, you can also change the button/menuitem and hotkey too. So what you do is run following mel (it's easier to do this in mel since the bulk of the commands are built that way, port to python or c++ if you must):
runTimeCommand
-annotation "Print the word \"Hello\""
-command ("print \"foo\"")
MySave;
nameCommand
-annotation "Print the word \"Hello\""
-command ("print \"foo\"")
MySaveNamed;
buildFileMenu();
menuItem -e -c "MySave" "MayaWindow|mainFileMenu|saveItem";
iconTextButton -e -c "MySave2" openSceneButton;
hotkey -keyShortcut "s" -ctl -name ("MySaveNamed");
Warning: The hotkey will be permanent in prefs, until you change it back or reset prefs.
Please note: There are at least two other ways user could save and you could not have full control of those even if you wanted to.

Related

How do I make an interactive application at the command line with Python, rather than just a script which returns output?

I would like to make a command line application in Python which, when I call its name, launches a new "mode" or interface at the command line, like launching Vim or Mutt, where key presses like 'q' or 'y' have specific functions - rather than just a script you run which returns some output.
How do I make this? Is there a specific library I would use for making this application? Or are there specific commands that tell the shell to display a kind of "window", and to listen for key presses and to execute commands on them?
You can take a look at this question, there are some useful advices. In particular the best option seems to be the curses extension module.
I suggest also to check out this project that can give you an useful example to do what you ask, even if it uses a GUI kit.

AppleScript: How to create and communicate with a singleton Terminal.app window

I want to programmatically create one and only one Terminal.app window that's uniquely identified by a literal Terminal.app window title (let's call it 'MyConsole1') and send bash commands to said window for the life of the Terminal.app process.
Can this be done with Terminal.app, and if so, how?
Details
My program probably needs to check if 'MyConsole1' is already open before I go try to create a new one. And of course, start Terminal.app if it's not already running. I also want the commands issued to and over the life of 'MyConsole1' (by my program) to be bash-command-history retrievable by a human user; ie, the user can interact with 'MyConsole1' if desired. I'm not concerned about user-vs-program "conflicts" that might arise for use of the 'MyConsole1'.
I presume I need AppleScript to do this. I will be running the AppleScript as part of a much-larger Python application, possibly via a method like this, in case that matters.
I see how Terminal.app window/tab title(s) can be set like this, but this solution doesn't work for my requirements above. Further, I only want 1 tab in the window, and the title to apply to the entire window, and not just the tab.
I'm not sure if this is what you mean, but you can try this:
tell app "Terminal"
activate
do script ""
end tell

Using Mac’s Dictation Inside Python

Does anyone have any ideas on how to use the Mac’s built-in dictation tool to create strings to be used by Python?
To launch a dictation, you have to double-press the Fn key inside any text editor. If this is the case, is there a way to combine the keystroke command with the input command? Something like:
Step 1: Simulate a keystroke to double-press the Fn key, launching the Dictation tool, and then
Step 2. Creating a variable by using the speech-to-text content as part of the input function, i.e. text_string = input(“Start dictation: “)
In this thread (Can I use OS X 10.8's speech recognition/dictation without a GUI?) a user suggests he figured it out with CGEventCreateKeyboardEvent(src, 0x3F, true), but there is no code.
Any ideas? Code samples would be appreciated.
UPDATE: Thanks to the suggestions below, I've imported AppScript. I'm trying the code to work along these lines, with no success:
from appscript import app, its
se = app('System Events')
proc = app.processes[its.frontmost == True]
mi = proc.menu_bars[1].menu_bar_items['Edit'].menus[1].menu_items['Start Dictation']
user_voice_text = input(mi.click())
print(user_voice_text)
Any ideas on how I can turn on the dictation tool to be input for a string?
UPDATE 2:
Here is a simple example of the program I'm trying to create:
Ideally i want to launch the program, and then have it ask me: "what is 1 + 1?"
Then I want the program to turn on the dictation tool, and I want the program to record my voice, with me answering "two".
The dictation-to-text function will then pass the string value = "two" to my program, and an if statement is then used to say back "correct" or "incorrect".
Im trying to pass commands to the program without ever typing on the keyboard.
First, FnFn dictation is a feature of the NSText (or maybe NSTextView?) Cocoa control. If you've got one of those, the dictated text gets inserted into that control. (It also uses that control's existing text for context.) From the point of view of the app using an NSTextView, if you just create a standard Edit menu, the Start Dictation item gets added to the end, with FnFn as a shortcut, and anything that gets dictated appears as input, just like input typed on a keyboard, or pasted or dragged with the mouse, or via any other input method.
So, if you don't have a GUI app, enabling dictation is going to be pointless, because you have no way to get the input.
If you do have a GUI app, the simplest thing to do is just get the menu item via NSMenu, and click the item.
You're almost certainly using some kind of GUI library, like PyQt or Tkinter, which has its own way of accessing your app's menu. But if not, you can do it directly through Cocoa (using PyObjC—which comes with Apple's pre-installed Python, but which you'll have to pip install if you're using a third-party Python):
import AppKit
mb = AppKit.NSApp.mainMenu()
edit = mb.itemWithTitle_('Edit').submenu()
sd = edit.indexOfItemWithTitle_('Start Dictation')
edit.performActionForItemAtIndex_(sd)
But if you're writing a console program that runs in the terminal (whether Terminal.app or an alternative like iTerm), the app you're running under has its own text widget and Edit menu, and you can parasitically use its menu instead.
The problem is that you don't have permission to just control other apps unless the user allows it. In older versions of OS X, this was done just by turning on "assistive scripting for accessibility" globally. As of 10.10, there's an Accessibility anchor in the Privacy tab of the Security & Privacy pane of System Preferences that has a list of apps that have permissions. Fortunately, if you're not on the list, the first time you try to use accessibility features, it'll pop up a dialog, and if the user clicks on it, it'll launch System Preferences, reveal that anchor, add your app to the list with the checkbox disabled, and scroll it into view, so all the user has to do is click the checkbox.
The AppleScript to do this is:
tell application "System Events"
click (menu item "Start Dictation" of menu of menu bar item "Edit"
of menu bar of (first process whose frontmost is true))
end tell
The "right" way to do the equivalent in Python is via ScriptingBridge, which you can access via PyObjC… but it's a lot easier to use the third-party library appscript:
from appscript import app, its
se = app('System Events')
proc = app.processes[its.frontmost == True]
mi = proc.menu_bars[1].menu_bar_items['Edit'].menus[1].menu_items['Start Dictation']
mi.click()
If you really want to send the Fn key twice, the APIs for generating and sending keyboard events are part of Quartz Events Services, which (even though it's a CoreFoundation C API, not a Cocoa ObjC API) is also wrapped by PyObjC. The documentation can be a bit tricky to understand, but basically, the idea is that you create an event of the appropriate type, then either post it to a specific application, an event tap, or a tap location. So, you can create and send a system-wide key-down Fn-key event like this:
evt = Quartz.CGEventCreateKeyboardEvent(None, 63, True)
Quartz.CGEventPost(Quartz.kCGSessionEventTap, evt)
To send a key-up event, just change that True to False.

How can I get the main window's handle in Python?

In python, I enumerate top-level windows through EnumWindows, and also I enumerate the processes through EnumProcesses.
Then in the python script, I put all the window handles which belongs to the same pid into one list (I did this through GetWindowThreadProcessId).
Later I found out something: there are 3 window handles which belong to notepad.exe, but I only open one text file.
Why?
Besides, I tried to set the text window as the foreground window through SetForegroundWindow, I passed the three window handles to this function, and two work.
How could this be ?
Processes sometimes create invisible windows for their own purposes. You should ignore them (use IsWindowVisible function).
To investigate this kind of things your best friend is Spy++, that comes with several versions of Visual Studio, if you can get it.
According to it, notepad.exe creates three top-level windows:
The visible main window, class name "Notepad", overlapped.
A hidden, disabled, pop-up window, class name "MSCTFIME UI", caption "M".
Another hidden, disabled, pop-up window, class name "IME", caption "Default IME".
The two hidden windows are used internally by notepad to implement the IME (Input Method Editor), the GUI to type complex scripts.
Many programs create top-level hidden windows for a lot of things. For what you intend, you can ignore them all and use only the visible ones.

Quickly switching between run configurations in PyCharm

Two actions that I perform on a regular basis are "run my tests" and "run my application". Unfortunately, PyCharm uses the same keystroke (Shift+F10, i.e., "Run") for both, and that keystroke uses my "current" run/debug configuration. I have to do a separate step to change configurations, and that step involves finding and selecting the right option from a longish list -- and it also requires taking the time to think about whether I need to switch configurations.
With as frequently as I run both the tests and the app (I'm still relatively new to Python, so I want to check frequently to make sure I haven't broken anything), this is pretty cumbersome -- especially since my usual pattern is "run the tests, and if they pass, then run the app", so I'm switching configurations all the time.
Is there an easier way to run a given configuration? Ideally I'd like to be able to bind a single keystroke to "select and run my 'main.py' configuration", and another single keystroke to "select and run my 'py.test' configuration" -- but anything that means I'm not constantly hunting through a list of configurations would be helpful.
I've tried recording a macro that switches configurations and then runs, but the macro recorder doesn't actually record the "change configurations" step -- all that ends up in the macro is "run". And there doesn't appear to be a macro editor that would let me manually add that step.
I also know about the pop-up "select configuration and run" list (F9), but that still requires me to read which configuration is selected in the list, decide whether it's the one I want, and cursor up or down in the list before hitting Enter. It's not all that complicated, but it still interrupts my train of thought pretty thoroughly every time I do it. Running -- both the tests and the app -- should be simpler than this.
I know this question is super old, but to anybody with the same problem.. here it is.
I had the same problem, because I had lots and lots of scripts on the same project, and I wanted to run a script individually.
What you need to do is, open up the script (by double-clicking), and then once it is open, right-click on anything, and you will see "run [the script]" with a green play button.
here is the photo

Categories