QObject::findChildren() for QML object finding - qml

I have an QML form with QQuickApplicationWindow. I need to get QQuickItem pointers on BaseKey elements of QtVirtualKeyboard (it's implementation is placed in separate QML file and loads with Loader of layout when program executes), but it has dynamical (runtime) type like this BaseKey_QMLTYPE_XX, where "XX" is a changeable number.
I found QObject::findChildren() function http://doc.qt.io/qt-4.8/qobject.html#findChild, but i cant find out number "XX" in typename.
How can i find QQuickItem pointer on BaseKey from C++ code?

BaseKey_QMLTYPE_XX looks like what you'd get if you printed the object (print(myObject)). I think that comes from QMetaObject::className().
If the object doesn't have an objectName set, you won't be able to find it using findChild() (unless you have access to the C++ type and there's only one object of that type).
I have a hacky test helper function that does something similar to what you're after:
QObject *TestHelper::findPopupFromTypeName(const QString &typeName) const
{
QObject *popup = nullptr;
foreach (QQuickItem *child, overlay->childItems()) {
if (QString::fromLatin1(child->metaObject()->className()) == "QQuickPopupItem") {
if (QString::fromLatin1(child->parent()->metaObject()->className()).contains(typeName)) {
popup = child->parent();
break;
}
}
}
return popup;
}
You could adapt this to iterate over the children of an object that you pass in. There are a couple more changes than that to get it to work, but the general idea is there.

Related

Need help handling StaleElementReferenceException

Before we get started let me say I have done my research on this matter, and I have seen the solutions posted here: stale element solution one, and I even came up with my own solution here: My temporary solution the problem with my solutions is that it does not work for all cases (particularly when dealing with long chains of .children()
The problem I have with "stale element solution one" is that it is not a very robust solution at all. It only works if you can put your Navigator element instantiation inside of the try catch, but if you do not have that instantiation, then this solution does no good. let me give an example of what I mean.
Lets say i have a Page class that looks something like this:
package interfaces
import geb.Page
import geb.navigator.Navigator
import org.openqa.selenium.By
class TabledPage extends Page {
static content ={
table {$(By.xpath("//tbody"))}
headers {$(By.xpath("//thead"))}
}
Navigator getAllRows(){
return table.children()
}
Navigator getRow(int index){
return table.children()[index]
}
Navigator getRow(String name){
return table.children().find{it.text().matches(~/.*\b${name}\b.*/)}
}
Navigator getColumn(Navigator row, int column){
return row.children()[column]
}
}
lets say that I have a method in my script that does what "stale element solution one" does (more or less). That looks like this:
def staleWraper(Closure c, args = null, attempts = 5){
def working = false
def trys = 0
while(!working){
try{
if(args){
return c(args)
}
else{
return c()
}
}
catch(StaleElementReferenceException se){
println("I caught me a stale element this many times: ${trys}")
if(trys>attempts){
working = true
throw se
}
else{
trys++
}
}
}
}
The way you call the above method is like this (using TabledPage as an example: staleWrapper(TabledPage.&getRow, 5) //grabs the 4th row of the table
and this works fine reason being, and this is important, the getRowmethod references an element that is in static content. When an static content element is reference, the Navigator is re-defined at run time. this is why this solution works for the getRow method. (table is re-instantiated inside of the try catch)
my problem and gripe with "stale element solution one" is that this type of implementation does not work for methods like getColumn this is because getColumn does not reference the static content itself. The Tabled page I am testing has javascript running on it that refreshes the DOM multiple times per second. so even if I use the staleWraper method it will always throw a stale element no matter how many attempts are made.
One solution to this is to add the columns as static content for the page, but I want to avoid that because it just doesn't flow with the way I have my whole project setup (I have many Page objects that implement methods in a similar way to TabledPage) If it were up to me, there would be a way to suppress the staleElelementExcpetions, but that is not an option either.
I am wondering if anyone here has a creative solution to Robustly (key word here) handle the StaleElementException, because I think looping over a try catch is already kinda hacky.
Well, I am not sure if my solution will solve your purpose. In my case I am using Page Pattern to design the tests so each method of Page class uses PageFactory to return instance of Page class. For example,
public class GoogleSearchPage {
// The element is now looked up using the name attribute
#FindBy(how = How.NAME, using = "q")
private WebElement searchBox;
public SearchResultPage searchFor(String text) {
// We continue using the element just as before
searchBox.sendKeys(text);
searchBox.submit();
// Return page instance of SearchResultPage class
return PageFactory.initElements(driver, SearchResultPage.class);
}
public GoogleSearchPage lookForAutoSuggestions(String text) {
// Do something
// Return page instance of itself as this method does not change the page
return PageFactory.initElements(driver, GoogleSearchPage.class);
}
}
The lookForAutoSuggestions may throw StaleElementException which is taken care by the returning the page instance. So if you are having page classes then ideally each page method should return an instance of page where user is supposed to land.
I ended up implementing something similar to what this guy gives as the "3rd option": Look at the answer (option 3)
I need to test it out some more but I have yet to get stale element since I implemented it this way (the key was to make my own WebElement class and then override the Navigator classes but use the NeverStaleWebElement object instead of the WebElement class

Actionscript, can a class be accessed using a variable name?

I wish to access many classes and variables, I would like to do this by dynamically setting the class name and variable name. Currently I am using
MyClass["myVariable1"]
to dynamically access the variable name
MyClass.myVariable1
I want to also dynanmically acces the class name, something like
["MyClass"]["myVariable1"]
But this does not work.
The purpose is that I have shared object with many user settings, I want to iterate through the shared object and set all the user settings across all the classes. I think if I cant dynamically access the class I must have a statement for each and every class name/variable.
I advise against such a practice. Although technically possible, it is like welcoming a disaster into the app architecture:
You rely on something you have no apparent control of: on the way Flash names the classes.
You walk out of future possibility to protect your code with identifier renaming obfuscation because it will render your code invalid.
Compile time error checks is better than runtime, and you are leaving it to runtime. If it happens to fail in non-debug environment, you will never know.
The next developer to work with your code (might be you in a couple of years) will have hard time finding where the initial data coming from.
So, having all of above, I encourage you to switch to another model:
package
{
import flash.net.SharedObject;
public class SharedData
{
static private var SO:SharedObject;
static public function init():void
{
SO = SharedObject.getLocal("my_precious_shared_data", "/");
}
static public function read(key:String):*
{
// if (!SO) init();
return SO.data[key];
}
static public function write(key:String, value:*):void
{
// if (!SO) init();
SO.data[key] = value;
SO.flush();
}
// Returns stored data if any, or default value otherwise.
// A good practice of default application values that might
// change upon user activity, e.g. sound volume or level progress.
static public function readSafe(key:String, defaultValue:*):*
{
// if (!SO) init();
return SO.data.hasOwnProperty(key)? read(key): defaultValue;
}
}
}
In the main class you call
SharedData.init();
// So now your shared data are available.
// If you are not sure you can call it before other classes will read
// the shared data, just uncomment // if (!SO) init(); lines in SharedData methods.
Then each class that feeds on these data should have an initialization block:
// It's a good idea to keep keys as constants
// so you won't occasionally mistype them.
// Compile time > runtime again.
static private const SOMAXMANA:String = "maxmana";
static private const SOMAXHP:String = "maxhp";
private var firstTime:Boolean = true;
private var maxmana:int;
private var maxhp:int;
// ...
if (firstTime)
{
// Make sure it does not read them second time.
firstTime = false;
maxhp = SharedData.readSafe(SOMAXHP, 100);
maxmana = SharedData.readSafe(SOMAXMANA, 50);
}
Well, again. The code above:
does not employ weird practices and easy to understand
in each class anyone can clearly see where the data come from
will be checked for errors at compile time
can be obfuscated and protected
You can try getting the class into a variable and going from there:
var myClass:Class = getDefinitionByName("MyClass") as Class;
myClass["myVariable1"] = x;

qvariant_cast causing segfault

Within qt's item/view framework, I'm trying to save a QColorDialog as user data and then retrieve that dialog as the editor, as well as during paint, in a tableview.
In my class constructor I do
QStandardItem *item = new QStandardItem();
QColorDialog *colorDlg = new QColorDialog(QColor(0,0,255), this);
item->setData(QVariant::fromValue(colorDlg), ColorDialogRole);
mTableModel->setItem(0,2,item);
then, inside my delegate's paint function I have
void ReportFigureTableDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
QVariant vColorDlg= index.data(ReportFigure::ColorDialogRole);
if(vColorDlg.isValid())
{
////////////////////////////////////////////////
// Program segfaults on the next line ... why?
////////////////////////////////////////////////
QColorDialog *colorDlg = qvariant_cast<QColorDialog*>(vColorDlg);
if(colorDlg != NULL)
{
painter->save();
QStyleOptionViewItem opt = option;
initStyleOption(&opt, index);
painter->fillRect(opt.rect, colorDlg->selectedColor());
painter->restore();
}
else
QStyledItemDelegate::paint(painter, option, index);
}
else
QStyledItemDelegate::paint(painter, option, index);
}
During runtime, the table shows up the first time (although with the wrong color ... different issue I assume). I double click to edit the cell and it brings up the dialog as expected. When I close, though, it segfaults on the indicated line. I don't understand why since I think I'm doing all the necessary checks.
You set the data on a QStandardItem object. Meanwhile you are retrieving the data on a QModelIndex object. Now why is the variant valid is a mystery. Maybe because ReportFigure::ColorDialogRole is equal to a build-in Qt role while it should be at least Qt::UserRole.
Anyway In the paint() method you can access the previously set item using
QStandardItem *item = mTableModel->itemFromIndex(index);

Controller selection

In my title screen, i have a code saying that the first controller using A is the PlayerIndex.one.
Here is the code:
public override void HandleInput(InputState input)
{
for (int anyPlayer = 0; anyPlayer <4; anyPlayer++)
{
if (GamePad.GetState((PlayerIndex)anyPlayer).Buttons.A == ButtonState.Pressed)
{
FirstPlayer = (PlayerIndex)anyPlayer;
this.ExitScreen();
AddScreen(new Background());
}
}
}
My question is: How can i use the "FirstPlayer" in other classes? (without this, there is no interest in this code)
I tried the Get Set thing but i can't make it work. Does i need to put my code in another class? Do you use other code to make this?
Thanks.
You can make a static variable say : SelectedPlayer,
and assign first player to it!
then you can call the first player through this class,
for example
class GameManager
{
public static PlayerIndex SelectedPlayer{get;set;}
..
..
..
}
and right after the loop in your code, you can say:
GameManager.SelectedPlayer = FirstPlayer;
I hope this helps, if your code cold be clearer that would be easier to help :)
Ok, so to do this properly you're going to have to redesign a little.
First off, you should be checking for a new gamepad input (i.e. you should be exiting the screen only when 'A' has been newly pressed). To do this you should be storing previous and current gamepad states:
private GamePadState currentGamePadState;
private GamePadState lastGamePadState;
// in your constructor
currentGamePadState = new GamePadState();
lastGamePadState = new GamePadState();
// in your update
lastGamePadState = currentGamePadState;
currentGamePadState = GamePad.GetState(PlayerIndex.One);
Really what you need to do is modify your class that deals with input. The basic functionality from your HandleInput function should be moved into your input class. Input should have a collection of functions that test for new/current input. For example, for the case you posted:
public Bool IsNewButtonPress(Buttons buton)
{
return (currentGamePadState.IsButtonDown(button) && lastGamePadState.IsButtonUp(button));
}
Then you can write:
public override void HandleInput(InputState input)
{
if (input.IsNewButtonPress(Buttons.A)
{
this.ExitScreen();
AddScreen(new Background());
}
}
Note: this will only work for one controller. To extend the implementation, you'll need to do something like this:
private GamePadState[] currentGamePadStates;
private GamePadState[] lastGamePadStates;
// in your constructor
currentGamePadStates = new GamePadState[4];
currentGamePadStates[0] = new GamePadState(PlayerIndex.One);
currentGamePadStates[1] = new GamePadController(PlayerIndex.Two);
// etc.
lastGamePadStates[0] = new GamePadState(PlayerIndex.One);
// etc.
// in your update
foreach (GamePadState s in currentGamePadStates)
{
// update all of this as before...
}
// etc.
Now, you want to test every controller for input, so you'll need to generalise by writing a function that returns a Bool after checking each GamePadState in the arrays for a button press.
Check out the MSDN Game State Management Sample for a well developed implementation. I can't remember if it supports multiple controllers, but the structure is clear and can easily be adapted if not.

How to register component interface in wxwebconnect?

I'm doing an experiment with wxWebConnect test application, incorporating the xpcom tutorial at "http://nerdlife.net/building-a-c-xpcom-component-in-windows/"
I adapt MyComponent class as necessary to compile together with testapp.exe (not as separate dll), and on MyApp::OnInit I have the following lines:
ns_smartptr<nsIComponentRegistrar> comp_reg;
res = NS_GetComponentRegistrar(&comp_reg.p);
if (NS_FAILED(res))
return false;
ns_smartptr<nsIFactory> prompt_factory;
CreateMyComponentFactory(&prompt_factory.p);
nsCID prompt_cid = MYCOMPONENT_CID;
res = comp_reg->RegisterFactory(prompt_cid,
"MyComponent",
"#mozilla.org/mycomp;1",
prompt_factory);
Those lines are copied from GeckoEngine::Init(), using the same mechanism to register PromptService, etc. The code compiles well and testapp.exe is running as expected.
I put javascript test as below :
try {
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
const cid = "#mozilla.org/mycomp;1";
obj = Components.classes[cid].createInstance();
alert(typeof obj);
// bind the instance we just created to our interface
alert(Components.interfaces.nsIMyComponent);
obj = obj.QueryInterface(Components.interfaces.nsIMyComponent);
} catch (err) {
alert(err);
return;
}
and get the following exception:
Could not convert JavaScript argument arg 0 [nsISupport.QueryInterface]
The first alert says "object", so the line
Components.classes[cid].createInstance()
is returning the created instance.
The second alert says "undefined", so the interface nsIMyComponent is not recognized by XULRunner.
How to dynamically registering nsIMyComponent interface in wxWebConnect environment ?
Thx
I'm not sure what is happening here. The first thing I would check is that your component is scriptable (I assume it is, since the demo you copy from is). The next thing I would check is whether you can instantiate other, standard XULRunner components and get their interface (try something like "alert('Components.interfaces.nsIFile');" - at least in my version of wxWebConnect this shows an alert box with string "nsIFile".
Also, I think it would be worth checking the Error Console to make sure there are no errors or warnings reported. A magic string to do that (in Javascript) is:
window.open('chrome://global/content/console.xul', '', 'chrome,dialog=no,toolbar,resizable');