I am automating the creation of a new product with a Python script and have run into a problem with the interactive events getting stuck at the "Part Number" dialog. This does not occur when creating a new part, just a new product. Here is the applicable portion of the script (CATIA is open):
import win32com.client.dynamic
CATIA = win32com.client.Dispatch("CATIA.Application")
catDocs = CATIA.Documents
# Create a new product
newProductDoc = catDocs.Add("Product")
# "Part Number" window appears, requesting a name for the product
# Interactive processes will not proceed
newProduct = newProductDoc.Product
newProductSet = newProduct.Products
newPart = newProductSet.AddNewComponent("Part", "dummyPart")
...
The problem is that I am developing a small tool for others to use and it is not very useful if it hangs up.
Clicking on "Cancel" gets rid of the dialog box, but no interactive actions occur afterwards. Clicking on "Ok" resolves the problem, but it would be preferable for the script to be able to prepare the product as a final result without interaction in order to restrict user error and improve ease of use.
I know that I can create a product and manipulate it (i.e. add parts, add new products, etc.), then successfully save it. So the processes are being executed, they just aren't being displayed anymore. I just can't seem to find a way to get past the "Part Number" dialog box. I even tried naming it programmatically, which worked but didn't kill the dialog box.
Opening an existing product works very well, and any scripting processes can continue without problems. However, programmatically creating the product, saving, and closing causes CATIA to lock up... so the option of saving and re-opening as an existing product is out the window.
I also referenced the v5Automation.chm, but I couldn't find a way of interacting with dialog boxes.
I also tried .Update() on the new product and it's parts. Some other assurances were CATIA.Visible = True and CATIA.RefreshDisplay = True.
Disclaimer: I know that VBA can be used and does not pose this problem. I am looking for a solution to this problem using Python (2 or 3, doesn't matter).
This post is old but since I found this page when having the same issue I figured I'll add my solution. I've found a handful of methods in CATIA that behave this way - works fine in CATIA VBA but not through the COM interface. The best solution I've found is to write a mini VBA function in a string and then call it in CATIA through Python. Here is an example:
import random
import win32com.client
CATIA = win32com.client.GetActiveObject('CATIA.Application')
CATVBALanguage = 1
# This should work, but CATIA leaves up the dialog window and it can affect
# the rest of the code execution
# NewProductDocument = CATIA.Documents.Add('Product')
# Instead, write the code in VBA and then have CATIA execute it. You can
# pass in arguments and capture the results as demonstrated below.
CREATE_PRODUCT_VBA_CODE = '''
Public Function create_product(part_number as CATBSTR) as Document
Set create_product = CATIA.Documents.Add("Product")
create_product.Product.PartNumber = part_number
End Function
'''
PART_NUMBER = 'test_product_{}'.format(random.randint(1, 100))
NewProductDocument = CATIA.SystemService.Evaluate(
CREATE_PRODUCT_VBA_CODE, # String with the VBA code to execute
CATVBALanguage, # 1 to indicate this string is VBA code
'create_product', # VBA function to call and return result from
[PART_NUMBER] # Array of arguments, in order for VBA function
)
# Can still interact with this returned object as if we had created it
print(NewProductDocument.Product.PartNumber)
The only way I have found, so far, to circumvent this problem is to create a template product (in this case, just an empty product) and do a catDocs.NewFrom(<templateProductPath>) and add the product structure as necessary.
I was trying to replicate your issue, but I dind't encounter it. Products just created fine using incremental default names.
Then I thought it was something related to Settings since the dialog is simlar to the one that optionally pops up when addin a new part.
I discovered that I had the option Infrastructure > Product Infrastructure > Product structure > Part Number: Manual input unchecked.
I don't know how this was related to using VBA or not, but checking it created the issue and unchecking it removed the issue, while still sending the same command from Python.
Related
I am developing a program that parses a file searching specific data requested by a user and returns that requested data as a .csv file. I have the main functionality of the program completed and tested it through manually changing variables which represent potential user inputs. The program is comprised of several scripts forming specific functionality modules.
At this point I am working on development of the GUI with Tkinter in order to obtain the user inputs for use in running the program. All of the user inputs are stored in the user_input.py file I have created. The other scripts then import specific items from user_input.py for use in the parsing functions. The issue I am running into is I select a file to parse through the GUI and store that file name as a global variable into user_input.py but cannot run the other scripts which need that input (the actual parsing of the file) until the GUI window is closed (i.e. mainloop() is completed).
I am new to programming and just learning on my own through development of this program. This boils down to two questions.
Do I have some fundamental misunderstanding of project structure which is causing this issue (i.e, I want to gather user input, run a program that relies on that input, output the results of that program back to the user, have them act on those results to generate final results which are stored as a .csv file)?
How do I run a program relying on user inputs while maintaining the GUI on screen without breaking mainloop()
I cannot figure out a way to do what I want in the order specified without closing the GUI to allow the rest of the program to run. Any help is appreciated.
I searched the web and SO but did not find an aswer.
Using Python, I would like to know how (if possible) can I stop the screen from updating its changes to the user.
In other words, I would like to buid a function in Python that, when called, would freeze the whole screen, preventing the user from viewing its changes. And, when called again, would set back the screen to normal. Something like the Application.ScreenUpdating Property of Excel VBA, but applied directly to the whole screen.
Something like:
FreezeScreen(On)
FreenScreen(Off)
Is it possible?
Thanks for the help!!
If by "the screen" you're talking about the terminal then I highly recommend checking out the curses library. It comes with the standard version of Python. It gives control of many different aspects of the terminal window including the functionality you described.
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.
Is it possible to use the Sublime Text 3 quick panel to retrieve user input as the goto_line command does?
When I hit CTRL+G, a quick panel appears and I can hit enter with any value. I cannot do this when I use this panel manually
If I use the quickpanel without values, I cannot validate it and therefore cannot retrieve the value inserted by the user.
I'm trying to do a plugin to create a Lua addon skeleton for a game, and I need to retrieve multiple user inputs as follows:
Is it possible to use the quick panel or I must use the show_input_panel multiple times ?
AFAIK there is no way to accept arbitrary input from a quick panel. Input is simply used to fuzzy filter the list.
For your plugin, here's a crazy idea: maybe you can create a snippet with text and fields for the input you want. Your plugin can have a command which when run creates a new scratch buffer where you insert this snippet. Once the user has filled out the fields, they can either run another command, or simply close the view (your plugin should have a listener for that event) to execute and do whatever.
I'm not familiar with PowerBuilder but I have a task to create Automatic UI Test Application for PB. We've decided to do it in Python with pywinauto and iaccesible libraries. The problem is that some UI elements like newly added lists record can not be accesed from it (even inspect32 can't get it).
Any ideas how to reach this elements and make them testable?
I'm experimenting with code for a tool for automating PowerBuilder-based GUIs as well. From what I can see, your best bet would be to use the PowerBuilder Native Interface (PBNI), and call PowerScript code from within your NVO.
If you like, feel free to send me an email (see my profile for my email address), I'd be interested in exchanging ideas about how to do this.
I didn't use PowerBuilder for a while but I guess that the problem that you are trying to solve is similar to the one I am trying to address for people making projects with SCADA systems like Wonderware Intouch.
The problem with such an application is that there is no API to get or set the value of a control. So a pywinauto approach can't work.
I've made a small tool to simulate the user events and to get the results from a screencapture. I am usig PIL and pytesser ORM for the analysis of the screen captures. It is not the easiest way but it works OK.
The tool is open-source and free of charge and can be downloaded from my website (Sorry in french). You just need an account but it's free as well. Just ask.
If you can read french, here is one article about testing Intouch-based applications
Sorry for the self promotion, but I was facing a similar problem with no solution so I've written my own. Anyway, that's free and open-source...
I've seen in AutomatedQa support that they a recipe recommending using msaa and setting some properties on the controls. I do not know if it works.
If you are testing DataWindows (the class is pbdwxxx, e.g. pbdw110) you will have to use a combination of clicking at specific coordinates and sending Tab keys to get to the control you want. Of course you can also send up and down arrow keys to move among rows. The easiest thing to do is to start with a normal control like an SLE and tab into the DataWindow. The problem is that the DataWindow is essentially just an image. There is no control for a given field until you move the focus there by clicking or tabbing. I've also found that the DataWindow's iAccessible interface is a bit strange. If you ask the DataWindow for the object with focus, you don't get the right answer. If you enumerate through all of the children you can find the one that has focus. If you can modify the source I also advise that you set AccessibleName for your DataWindow controls, otherwise you probably won't be able to identify the controls except by position (by DataWindow controls I mean the ones inside the DataWindow, not the DataWindow itself). If it's an MDI application, you may also find it useful to locate the MicroHelp window (class fnhelpxxx, e.g. fnhelp110, find from the main application window) to help determine your current context.
Edited to add:
Sikuli looks very promising for testing PowerBuilder. It works by recognizing objects on the screen from a saved fragment of screenshot. That is, you take a screenshot of the part of the screen you want it to find.