Squish/Robust Selection of View Items with Variable Text: Difference between revisions

From Qt Wiki
Jump to navigation Jump to search
No edit summary
m (style, fixed link)
 
(5 intermediate revisions by 3 users not shown)
Line 1: Line 1:
[[Category:Tools::Squish]]
[[Category:Squish]]


= Robust selection of items in a tree or list view with variable text =
This article is similiar to to [[Finding_List_Items_that_End_in_a_Given_String|Finding list items that ends in a given string]] however goes into a bit more detail and is also functional for treeviews as well as listviews. Dealing with a treeview requires careful handling of "."-separated paths.
 
This article is similiar to to [[Finding list items that ends in a given string]] however goes into a bit more detail and is also functional for treeviews as well as listviews. Dealing with a treeview requires careful handling of "."-separated paths.


Imagine you need to select some item in a treeview, but you don't know the exact string of the item as it may have changed. Alternatively maybe the parent item of the item you wish to select has changed, but the item is still the same. You need to find the item you want in the tree, and extract the correct Squish item identifier to interact with it later on.
Imagine you need to select some item in a treeview, but you don't know the exact string of the item as it may have changed. Alternatively maybe the parent item of the item you wish to select has changed, but the item is still the same. You need to find the item you want in the tree, and extract the correct Squish item identifier to interact with it later on.
Line 9: Line 7:
For example, assume you need to find a certain file in treeview, but the treeview has items with absolute paths that might be different on various machines. All you know is that the filename you are looking for will be the same, if it exists. You wish to iterate through the treeview, checking each level against some known valid path that you have. For this example, we will assume you have paths such as
For example, assume you need to find a certain file in treeview, but the treeview has items with absolute paths that might be different on various machines. All you know is that the filename you are looking for will be the same, if it exists. You wish to iterate through the treeview, checking each level against some known valid path that you have. For this example, we will assume you have paths such as


<code>server1\serverpath\some\path\test_data\test_project\filetest.db<br />server32\serverpath\some\path\test_data\test_project\filetest.db<br />C:_mirror\some\path\test_data\test_project\filetest.db</code>
<code>
server1\serverpath\some\path\test_data\test_project\filetest.db
server32\serverpath\some\path\test_data\test_project\filetest.db
C:\_mirror\some\path\test_data\test_project\filetest.db
</code>


and you can't be sure which path is in the treeview (potentially as a parent or child item). This means you might need to convert
and you can't be sure which path is in the treeview (potentially as a parent or child item). This means you might need to convert


<code>server1serverpathsomepathtestdatatestprojectfiletestb.subtem.subSubItem<code>
<code>server1\serverpath\some\path\test_data\test_project\filetestdb.subtem.subSubItem</code>


to
to


</code>C:localmirrorsomepathtestdatatestprojectfiletestb.subtem.subSubItem</code>
<code>C:\_mirror\some\path\test_data\test_project\filetestdb.subtem.subSubItem</code>
 
(Note that these paths are escaped so that Squish's itemview functions properly find the paths). As you can see, all three of the paths are the same from the "test_data" folder onwards, so we will use that to split between the "known good" parts of the path we wish to match, and the variable part we wish to replace.


(Note that these paths are escaped so that Squish's itemview functions properly find the paths). As you can see, all three of the paths are the same from the &quot;test_data&amp;quot; folder onwards, so we will use that to split between the &quot;known good&amp;quot; parts of the path we wish to match, and the variable part we wish to replace.
<code>
def findRealPath( treeviewname, origpath ):
import re


<code>def findRealPath( treeviewname, origpath ):<br /> import re
print "Original path: ", origpath
treeView = waitForObject( treeviewname )
model = treeView.model()


print &quot;Original path: &quot;, origpath<br /> treeView = waitForObject( treeviewname )<br /> model = treeView.model()
# we are looking for this
originalParts = origpath.split( "testdata" )
pathOnly = originalParts[1]
# Remove the items separated by "." (but not . which is an escaped dot in the folder path)
print "Now splitting items: s" re.split( "[<sup>].", pathOnly, 1 )
pieces = re.split( "[</sup>].", pathOnly, 1 ) # only split once
pathOnly = pieces[ 0 ]
childrenItems = ""
if len( pieces ) > 1: # If this has children
  childrenItems = pieces[ 1 ]


# we are looking for this<br /> originalParts = origpath.split( &quot;testdata&quot; )<br /> pathOnly = originalParts[1]<br /> # Remove the items separated by &quot;.&quot; (but not . which is an escaped dot in the folder path)<br /> print &quot;Now splitting items: s&amp;quot; re.split( &quot;[<sup>].&quot;, pathOnly, 1 )<br /> pieces = re.split( &quot;[</sup>].&quot;, pathOnly, 1 ) # only split once<br /> pathOnly = pieces[ 0 ]<br /> childrenItems = &quot;&quot;<br /> if len( pieces ) &gt; 1: # If this has children<br /> childrenItems = pieces[ 1 ]<br /> toplevel = model.rowCount( QModelIndex() )<br /> for row in range( toplevel ): # iterate through tree, looking for matching file<br /> # shortcut solution: check all top-level items in their first three columns. much more efficient if result is top-level<br /> for col in range( 3 ): # check first few columns<br /> idx = model.index( row, col, QModelIndex() )<br /> if checkModelItemReplacement(idx, pathOnly):<br /> return replaceModelIndex(utils.escape(str( idx.data( Qt.DisplayRole ).toString() )), childrenItems)
toplevel = model.rowCount( QModelIndex() )
for row in range( toplevel ): # iterate through tree, looking for matching file
  # shortcut solution: check all top-level items in their first three columns. much more efficient if result is top-level
  for col in range( 3 ): # check first few columns
    idx = model.index( row, col, QModelIndex() )
    if checkModelItemReplacement(idx, pathOnly):
      return replaceModelIndex(utils.escape(str( idx.data( Qt.DisplayRole ).toString() )), childrenItems)


# didn't find what we were looking for in the top level, do a search two more levels down<br /> # check all our children as well<br /> for col in range(20):<br /> idx = model.index( row, col, QModelIndex() )<br /> childCount = model.rowCount(idx)<br /> print &quot;Checking children of item %s, found s&amp;quot; (idx.data(Qt.DisplayRole).toString(), childCount)<br /> for childRow in range(childCount):<br /> child = model.index( 0, 0, idx )<br /> print &quot;checking child %s: s&amp;quot; (childRow, child.data(Qt.DisplayRole).toString())<br /> if checkModelItemReplacement(child, pathOnly):<br /> # we need to do the replacement differently, because we need to keep the parent in the path<br /> partpieces = re.split( &quot;([<sup>]).&quot;, origpath )<br /> # find the index that has the test_data/ path. all indexes before that are parents<br /> for i in range(len(partpieces)):<br /> if &quot;testdata&amp;quot; in partpieces[i]: # stop here<br /> # since we split on [</sup>]. and captured the split text, we need to condense each pair of items<br /> num = i/2<br /> parentItems = []<br /> for k in range(num):<br /> parentItems.append( partpieces[k] + partpieces[k+1] ) # rescue the char lost when splitting<br /> parentstr = &quot;.&quot;.join(parentItems)<br /> return replaceModelIndex(utils.escape(str( child.data( Qt.DisplayRole ).toString() )), childrenItems, parentstr)
  # didn't find what we were looking for in the top level, do a search two more levels down
  # check all our children as well
  for col in range(5):
    idx = model.index( row, col, QModelIndex() )
    childCount = model.rowCount(idx)
    print "Checking children of item %s, found s" (idx.data(Qt.DisplayRole).toString(), childCount)
    for childRow in range(childCount):
      child = model.index( 0, 0, idx )
      print "checking child %s: s" (childRow, child.data(Qt.DisplayRole).toString())
      if checkModelItemReplacement(child, pathOnly):
        # we need to do the replacement differently, because we need to keep the parent in the path
        partpieces = re.split( "([<sup>]).", origpath )
        # find the index that has the test_data/ path. all indexes before that are parents
        for i in range(len(partpieces)):
          if "testdata" in partpieces[i]: # stop here
            # since we split on [</sup>]. and captured the split text, we need to condense each pair of items
            num = i/2
            parentItems = []
            for k in range(num):
              parentItems.append( partpieces[k] + partpieces[k+1] ) # rescue the char lost when splitting
            parentstr = ".".join(parentItems)
            return replaceModelIndex(utils.escape(str( child.data( Qt.DisplayRole ).toString() )), childrenItems, parentstr)


# we failed<br /> print &quot;Failed to replace: s&amp;quot; origpath<br /> return origpath
# we failed
print "Failed to replace: s" origpath
return origpath


# used internally by above<br />def checkModelItemReplacement( idx, pathOnly ):<br /> contents = utils.escape(str( idx.data( Qt.DisplayRole ).toString() ))<br /> print &quot;Checking for %s in s&amp;quot; (pathOnly, contents)<br /> if pathOnly in contents: # this is the entry we are looking for<br /> return True<br /> else:<br /> return False
# used internally by above
def checkModelItemReplacement( idx, pathOnly ):
contents = utils.escape(str( idx.data( Qt.DisplayRole ).toString() ))
print "Checking for %s in s" (pathOnly, contents)
return pathOnly in contents: # this is the entry we are looking for


# used internally by above<br />def replaceModelIndex( contents, children, parents = &quot;&quot; ):<br /> print &quot;Replacing with %s and %s with parent? s&amp;quot; (contents, children, parents)<br /> replaced = contents
# used internally by above
def replaceModelIndex( contents, children, parents = "" ):
print "Replacing with %s and %s with parent? s" (contents, children, parents)
replaced = contents


if children [[Image:= &quot;&quot;:
if children != "":
        replaced += &quot;.&quot; + children
  replaced += "." + children
        print &quot;added children to replaced: %s&quot; % replaced
  print "added children to replaced: %s" % replaced
    if parents |]]= &quot;&quot;:<br /> replaced = parents + &quot;.&quot; + replaced<br /> print &quot;added parents to replaced: s&amp;quot; replaced<br /> return replaced</code>
  if parents != "":
    replaced = parents + "." + replaced
  print "added parents to replaced: s" replaced
return replaced</code>


As is visible in the above code sample, this code only searches three-levels-deep for a match. If your tree is deeper than 3 levels, you'll need to either add another level of drilling down, or convert it into recursive function that works on any arbitrary level. The above code can be used like so:
As is visible in the above code sample, this code only searches three-levels-deep for a match. If your tree is deeper than 3 levels, you'll need to either add another level of drilling down, or convert it into recursive function that works on any arbitrary level. The above code can be used like so:


<code>treeViewName = &quot;:Your_treeview_here&amp;quot;<br />itemPath = &quot;server1serverpathsomepathtestdatatestprojectfiletestb.subtem.subSubItem&amp;quot;<br />realItemPath = findRealPath( treeViewName, itemPath )
<code>
treeViewName = ":Your_treeview_here"
itemPath = "server1serverpathsomepathtestdatatestprojectfiletestb.subtem.subSubItem"
realItemPath = findRealPath( treeViewName, itemPath )
</code>

Latest revision as of 13:25, 11 August 2020


This article is similiar to to Finding list items that ends in a given string however goes into a bit more detail and is also functional for treeviews as well as listviews. Dealing with a treeview requires careful handling of "."-separated paths.

Imagine you need to select some item in a treeview, but you don't know the exact string of the item as it may have changed. Alternatively maybe the parent item of the item you wish to select has changed, but the item is still the same. You need to find the item you want in the tree, and extract the correct Squish item identifier to interact with it later on.

For example, assume you need to find a certain file in treeview, but the treeview has items with absolute paths that might be different on various machines. All you know is that the filename you are looking for will be the same, if it exists. You wish to iterate through the treeview, checking each level against some known valid path that you have. For this example, we will assume you have paths such as

server1\serverpath\some\path\test_data\test_project\filetest.db
server32\serverpath\some\path\test_data\test_project\filetest.db
C:\_mirror\some\path\test_data\test_project\filetest.db

and you can't be sure which path is in the treeview (potentially as a parent or child item). This means you might need to convert

server1\serverpath\some\path\test_data\test_project\filetestdb.subtem.subSubItem

to

C:\_mirror\some\path\test_data\test_project\filetestdb.subtem.subSubItem

(Note that these paths are escaped so that Squish's itemview functions properly find the paths). As you can see, all three of the paths are the same from the "test_data" folder onwards, so we will use that to split between the "known good" parts of the path we wish to match, and the variable part we wish to replace.

def findRealPath( treeviewname, origpath ):
 import re

 print "Original path: ", origpath
 treeView = waitForObject( treeviewname )
 model = treeView.model()

# we are looking for this
 originalParts = origpath.split( "testdata" )
 pathOnly = originalParts[1]
 # Remove the items separated by "." (but not . which is an escaped dot in the folder path)
 print "Now splitting items: s" re.split( "[<sup>].", pathOnly, 1 )
 pieces = re.split( "[</sup>].", pathOnly, 1 ) # only split once
 pathOnly = pieces[ 0 ]
 childrenItems = ""
 if len( pieces ) > 1: # If this has children
   childrenItems = pieces[ 1 ]

 toplevel = model.rowCount( QModelIndex() )
 for row in range( toplevel ): # iterate through tree, looking for matching file
   # shortcut solution: check all top-level items in their first three columns. much more efficient if result is top-level
   for col in range( 3 ): # check first few columns
     idx = model.index( row, col, QModelIndex() )
     if checkModelItemReplacement(idx, pathOnly):
       return replaceModelIndex(utils.escape(str( idx.data( Qt.DisplayRole ).toString() )), childrenItems)

   # didn't find what we were looking for in the top level, do a search two more levels down
   # check all our children as well
   for col in range(5):
     idx = model.index( row, col, QModelIndex() )
     childCount = model.rowCount(idx)
     print "Checking children of item %s, found s" (idx.data(Qt.DisplayRole).toString(), childCount)
     for childRow in range(childCount):
       child = model.index( 0, 0, idx )
       print "checking child %s: s" (childRow, child.data(Qt.DisplayRole).toString())
       if checkModelItemReplacement(child, pathOnly):
         # we need to do the replacement differently, because we need to keep the parent in the path
         partpieces = re.split( "([<sup>]).", origpath )
         # find the index that has the test_data/ path. all indexes before that are parents
         for i in range(len(partpieces)):
           if "testdata" in partpieces[i]: # stop here
             # since we split on [</sup>]. and captured the split text, we need to condense each pair of items
             num = i/2
             parentItems = []
             for k in range(num):
               parentItems.append( partpieces[k] + partpieces[k+1] ) # rescue the char lost when splitting
             parentstr = ".".join(parentItems)
             return replaceModelIndex(utils.escape(str( child.data( Qt.DisplayRole ).toString() )), childrenItems, parentstr)

# we failed
 print "Failed to replace: s" origpath
 return origpath

# used internally by above
def checkModelItemReplacement( idx, pathOnly ):
 contents = utils.escape(str( idx.data( Qt.DisplayRole ).toString() ))
 print "Checking for %s in s" (pathOnly, contents)
 return pathOnly in contents: # this is the entry we are looking for

# used internally by above
def replaceModelIndex( contents, children, parents = "" ):
 print "Replacing with %s and %s with parent? s" (contents, children, parents)
 replaced = contents

 if children != "":
   replaced += "." + children
   print "added children to replaced: %s" % replaced
   if parents != "":
     replaced = parents + "." + replaced
   print "added parents to replaced: s" replaced
 return replaced

As is visible in the above code sample, this code only searches three-levels-deep for a match. If your tree is deeper than 3 levels, you'll need to either add another level of drilling down, or convert it into recursive function that works on any arbitrary level. The above code can be used like so:

treeViewName = ":Your_treeview_here"
itemPath = "server1serverpathsomepathtestdatatestprojectfiletestb.subtem.subSubItem"
realItemPath = findRealPath( treeViewName, itemPath )