pywinauto doesn't see ListView object using .print_control_identifiers() - python-3.x

I am evaluating pywinauto library to be able to automate win desktop applications and so far it looks very promising. For evaluation I am using simple BookDB2 app which I used many times before (e.g.: by testComplete or UFT evaluations). Unlike other objects in this app - which I am able to find and work with, pywinauto for some reason cannot see its ListView object.
When used:
from pywinauto.application import Application
app = Application(backend='uia').start(r"C:\Program Files (x86)\BookDB2\BookDB2.exe")
app.top_window().print_control_identifiers()
python prints all found objects to log, however ListView object is missing. I also tried to find it directly using its properties (with help of AutoIt spy tool), but was unsuccessful. Normally I would think that this ListView is customized (non-standard) - because that could be good explanation why it is not recognized. But I know that UFT and testComplete did not have a slightest problem to recognize&work with it. What am I missing?
Update:
I tried SWAPY - which is kind of pywinauto support tool capable of object spying and generating simple code.
I found out that SWAPY actually can see this ListView object and it generated for me click operation:
from pywinauto.application import Application
app = Application().Start(cmd_line=u'"C:\\Program Files (x86)\\BookDB2\\BookDB2.exe" ')
thunderrtformdc = app[u'BookDB Main Menu']
thunderrtformdc.Wait('ready')
listviewwndclass = thunderrtformdc[u'3']
listviewwndclass.Click()
app.Kill_()
When executed in PyCharm - this error appeared:
raise MatchError(items = name_control_map.keys(), tofind = search_text)
pywinauto.findbestmatch.MatchError: Could not find '3' in 'dict_keys(['ThunderRT6Frame', 'FiltersThunderRT6Frame', 'Filters', 'ApplyButton', 'Button', 'Apply', 'ClearButton', 'Button0', 'Button1', 'Button2', 'Clear', 'Add BookEdit', 'Edit', 'Add BookEdit0', 'Add BookEdit1', 'Add BookEdit2', 'Edit0', 'Edit1', 'Edit2', 'ComboBox', 'ApplyComboBox', 'ComboBox0', 'ComboBox1', 'ComboBox2', 'Add BookComboBox', 'Button3', 'Copies', 'CopiesButton', 'Button4', 'Add Book', 'Add BookButton', 'BooksListView', 'ListView', 'BooksHeader', 'Header', 'Button5', 'BorrowersButton', 'Borrowers', 'Button6', 'CategoriesButton', 'Categories', 'Button7', 'Publishers', 'PublishersButton', 'Authors', 'Button8', 'AuthorsButton', 'Button9', 'BooksButton', 'Books'])'
So it seems to me that SWAPY sees something which pywinauto cannot.

Ok... so it was all my mistake. This is correct:
app = Application().start(r"C:\Program Files (x86)\BookDB2\BookDB2.exe")
for some reason I forgot backend='uia' in code when starting the app, probably copy&paste error. Still not sure why other objects were visible and this (ListView) wasn't but at least my problem is solved.

Related

Excel VSTO Addin Using ChromiumFX Issue

I'm trying to invoke browser control in a Window Form using ChromiumFX framework in the Excel VSTO Addin.
The excel application throws the following alerts and another excel instance gets started.
Sorry, we couldn't find Files\Microsoft.xlsx. Is is possible it was removed, renamed of deleted ?
Sorry, we couldn't find Office\root\Office16\debug.log. Is is possible it was removed, renamed of deleted ?
Although I’m able to load the URL in the Web Browser control but i get the above errors multiple time and every time another instance of excel gets initiated.
I’m invoking the following piece of code after which the alerts start appearing.
var assemblyDir = System.IO.Path.GetDirectoryName(new System.Uri(System.Reflection.Assembly.GetExecutingAssembly().EscapedCodeBase).LocalPath);
Environment.CurrentDirectory = System.IO.Path.Combine(assemblyDir, #".");
if (CfxRuntime.PlatformArch == CfxPlatformArch.x64)
CfxRuntime.LibCefDirPath = #"cef\Release64";
else
CfxRuntime.LibCefDirPath = #"cef\Release";
Chromium.WebBrowser.ChromiumWebBrowser.OnBeforeCfxInitialize += ChromiumWebBrowser_OnBeforeCfxInitialize;
ChromiumWebBrowser.OnBeforeCommandLineProcessing += ChromiumWebBrowser_OnBeforeCommandLineProcessing;
Chromium.WebBrowser.ChromiumWebBrowser.Initialize();
Any suggestions on what i might be doing wrong ? Or what piece of code i’m missing ?

HWND handle being returned via FindWindowW differs from top-level parent

I'm trying to create a utility that will selectively hide and show windows based on pre-assigned hotkeys and I'm working with the Windows API code.
I use a FindWindowW call to get a handle to a window as a test (in my case, a window with the text "Calculator - Calculator", which matched an open calculator window) and use that handle in a ShowWindow function.
Code below:
var user32path = 'C:\\Windows\\System32\\user32.dll';
function TEXT(text){
return new Buffer(text, 'ucs2').toString('binary');
}
var user32 = new FFI.Library(user32path, {
'FindWindowW': ['int', ['string', 'string']],
'ShowWindow': ['int', ['int', 'int']],
'ShowWindowAsync': ['int', ['int', 'int']],
'FindWindowExW': ['int', ['int', 'int', 'string', 'string']],
'BringWindowToTop': ['int', ['int']],
'GetActiveWindow': ['int', ['int']]
var handle = user32.FindWindowW(null,TEXT("Calculator ‎- Calculator"));
user32.ShowWindow(
handle, 'SW_Hide');
//associatedWindowHandle is a manually-created variable with the Spy++ variable.
//The Spy++ doesn't match and I'm not sure why.
user32.ShowWindowAsync(activeHandle, 'SW_Hide');
var pruneLength = Object.keys(prunedData).length;
for (let i = 0; i < pruneLength-1; i++){
if (Object.entries(prunedData)[i][1] === hotkey){
for(let j = 1; j <= prunedData.assocWindows.length; j++){
let associatedWindow = Object.entries(prunedData)[i+1][j].toString();
let associatedWindowHandle = parseInt(associatedWindow);
user32.ShowWindowAsync(associatedWindowHandle, 'SW_Hide');
user32.BringWindowToTop(associatedWindowHandle[i+1][j]);
}
}
}
2 main issues:
When I try hiding and/or minimizing the open calculator window, I can't seem to show it again when clicking on it. the preview image disappers and I notice a "Process Broker" is thrown.
I can't seem to actually find the window handle given with tools like Spy++, which makes it somewhat hard to debug to see if I need to grab a different handle. The parent-level calculator window's handle doesn't seem to match, and I verified that it was the same tool.
I'd also like to be pointed to some decent resources to help self-educate on this so I can better troubleshoot this in the future.
Many thanks!
Firstly, I'd echo Hans Passant's remarks that you're probably better off not trying to so this with a UWP app like Calculator, but then again these apps are not going to go away so perhaps you might want to try anyway.
The shell doesn't appear to appreciate you trying to hide a UWP app (Win32 apps work fine though, go figure). As you have observed, it's icon remains visible in the toolbar but behaves strangely while the window is hidden. So, short version, don't do that.
Instead, try this:
PostMessage (hWnd, WM_SYSCOMMAND, SC_MINIMIZE, 0);
Then things work a lot better, although the user can still undo all your good work by reopening the window of course.
As for Spy++, I have no trouble locating the top-level window of a UWP app using the 'Finder tool' (Menu -> Search -> Find Window). You just have to walk a couple of levels up the window hierarchy afterwards until you get to the one you really want.
Spy++ seems not to be able to log messages being sent to such a window however, see (shameless plug): Why can't Spy++ see messages sent to UWP apps?. I plan to look into this a bit more when I have time.
Finally, what do you mean by 'a "Process Broker" is thrown' please? I don't understand that comment. There's something called RuntimeBroker, which shows up in Process Explorer and appears to be connected with UWP apps in some way, but I don't know if that's what you mean and and I don't know anything about it even if you did.

#material material-component-web Invalid tab component given as activeTab

firstly let my say that the mdc documentation is difficult for non-pros like me.
I'm using Elixir Phoenix and Brunch.
I import and everything is fine.
import {MDCTab, MDCTabFoundation} from '#material/tabs'; import
{MDCTabBar, MDCTabBarFoundation} from '#material/tabs'; import
{MDCTabBarScroller, MDCTabBarScrollerFoundation} from
'#material/tabs';
I manually instantiate the tab bar in a separate function that I export
export var Tabbable = {
run: function(MDCTabBar, el){
var myDynamicTabBar = window.myDynamicTabBar = new MDCTabBar(document.querySelector('#' + el));
Which is following the documentation like this
const tabBar = new MDCTabBar(document.querySelector('#my-mdc-tab-bar'));
but is slightly different to the documentation's use of the tab bar in their code snippet
var dynamicTabBar = window.dynamicTabBar = new mdc.tabs.MDCTabBar(document.querySelector('#dynamic-tab-bar'));
But, whenever I try to use mdc I get a 'not defined' error. Therefore, I'm not using it :-)
Now, when the user clicks the tab bar I capture that like this:
myDynamicTabBar.listen('MDCTabBar:change', function ({detail: tabs}) {
var nthChildIndex = tabs.activeTabIndex;
updatePanel(nthChildIndex);
});
The subtle difference is that my myDynamicTabBar is MDCTabBar but the documentation's dynamicTabBar is mdc.tabs.MDCTabBar
My tab control works, but it throws an error only visible in the console:
Uncaught Error: Invalid tab component given as activeTab: Tab not
found within this component's tab list
which is likely because I'm not using mdc.tabs? The documentation notes the change event happens on the MDCTabBar.
Therefore, how do I get rid of this annoying error in the console?
And why can I not access the global mdc? I have tried this in my Brunch file
globals: { mdc: "#material"}
But no good.
I'm right behind you on this! I'm frustrated with the docs too :(
You answered your own question in this Elixir thread which is very informative.
I found the real solution in this thread https://github.com/hyperapp/hyperapp/issues/546
MDCTabBar automatically initiates its children. So initiating tabs will result in that error.
The fix is to just initiate MDCTabBar

Cannot get QWindow::fromWinId to work properly

My Qt 5.9 program (on X11 Linux) launches other applications, using QProcess.
I would like to have control over windows these applications spawn, so I obtain their winId value and use QWindow::fromWinId to get a QWindow instance.
The problem is these instances are invalid and do not represent the window they are supposed to.
If I check the winId values using xwininfo, the correct information is returned, so I know they are good.
What am I doing wrong?
Edit: An example won't help much, but here goes:
QProcess *process=new QProcess(this);
...
process.open()
... // wait until window appears
WId winId=PidToWid(process->processId()); // this function returns the Window ID in decimal format. I test this with xwininfo, it's always correct
...
QWindow *appWindow=QWindow::fromWinId(winId);
... And that's basically it. appWindow is a valid QWindow instance, but it does not relate to the actual window in any way. For example, if I close() it, it returns true but the window does not close.
Even if I provide a wrong WId on purpose, the end result is the same.
This is not proper solution with explanation why it should work, however it may be helpful for somebody...
I had the same issue with my application when I switched from Qt4 QX11EmebeddedContainer to Qt5 implementation using QWindow. What I did to resolve / fix this issue was following:
Client application:
widget->show(); //Widget had to be shown
widget->createWinId();
sendWinId(widget->winId()); //Post window handle to master app where is constructed container
Master application:
QWindow* window = QWindow::fromWinId(clientWinId);
window->show(); //This show/hide toggle did trick in combination with show in client app
window->hide();
QWidget* container = QWidget::createWindowContainer(window, parentWindowWidget);
After this I was able to control window properly through QWidget container.

Add a footer to an existing OpenXML spreadsheet

EDIT: There's a further detail I left out with my original post. The program is using a template stream rather than a concrete template for the ".Open" command. The template stream gets initialized with this code block:
public void Initialize(Stream templateStream)
{
spreadsheet = SpreadsheetDocument.Open(templateStream, true);
}
I'm still researching this, but does anyone know the implications of using a stream for the HeaderFooter object in OpenXML?
I'm new to OpenXML and still in the process of reading and learning what I can of this massive SDK. I've inherited a C# MVC.NET program that uses OpenXML to display information on an Excel spreadsheet and all of that is working, but I now need to add a footer to the same spreadsheet and I'm hitting some brick walls in my OpenXML knowledge.
I put the footer information I wanted into the spreadsheet, opened it up with the Open XML SDK Productivity Tool and found this code under <.x:oddFooter(OddFooter):
// Creates an OddFooter instance and adds its children.
public OddFooter GenerateOddFooter()
{
OddFooter oddFooter1 = new OddFooter();
oddFooter1.Text = "&L&\"Times New Roman,Regular\"Page &P of &N&C&\"Times New Roman,Regular\"Generated On: <Date/Time> Central&R&\"Times New Roman,Regular\"Report";
return oddFooter1;
}
And this code one level up under <>x:headerFooter(OddFooter):
// Creates an HeaderFooter instance and adds its children.
public HeaderFooter GenerateHeaderFooter()
{
HeaderFooter headerFooter1 = new HeaderFooter();
OddFooter oddFooter1 = new OddFooter();
oddFooter1.Text = "&L&\"Times New Roman,Regular\"Page &P of &N&C&\"Times New Roman,Regular\"Generated On: <Date/Time> Central&R&\"Times New Roman,Regular\"Report";
headerFooter1.Append(oddFooter1);
return headerFooter1;
}
Now I of course need to append the footer info somewhere, and this is where I'm stuck. In <>x:worksheet(Worksheet) I see this line of code:
worksheet1.Append(headerFooter1);
This looked easy enough, but when I looked back at the application code I found no worksheet object to append to. I thought I was close with the following line of code:
spreadsheet.WorkbookPart.Workbook.Append(headerFooter1);
but this yielded nothing. In the application I see a SpreadsheetDocument object and references to OpenXMLParts... do I need to get a spreadsheet part to append to? Or do I need to take a different approach with a Spreadsheet versus a worksheet object? Do I need to materialize the current worksheet and then append?
I have a feeling this has an easy solution, but as I said I'm still learning the SDK.
Thank you!
For those who don't know about the productivity tool, it is included with the SDK and can be downloaded from https://www.microsoft.com/en-us/download/details.aspx?displaylang=en&id=5124
On my 64 bit machine, the install path to the tool was:
"C:\Program Files (x86)\Open XML SDK\V2.0\tool\OpenXmlSdkTool.exe"
You should be able to manually add a footer to a spreadsheet, and open it up with the tool and see the exact C# required to create the entire spreadsheet, including the footer. You can then remove the parts of the code that are unnecessary, like some of the styles that are automatically added.

Resources