Skip to content
Browse files

messy, broken tables.

  • Loading branch information...
1 parent 163fa6b commit 628efc2a50a333f71480479d9656df65908b5230 @griffy committed Aug 13, 2012
Showing with 180 additions and 58 deletions.
  1. +3 −0 TODO
  2. +1 −1 bin/sikwidgets
  3. +1 −1 bin/sikwidgets.bat
  4. 0 bin/{sw.py → sikwidgets_script.py}
  5. +1 −1 setup.py
  6. +13 −1 sikwidgets/util.py
  7. +161 −54 sikwidgets/widgets/table.py
View
3 TODO
@@ -17,5 +17,8 @@ In the case of wanting to find only the first matching cell, the current algorit
Migrate away from using the terminal for input (especially for the
capture screenshot section).. Use popups and inputs
+Can ScrollableWidget be abstracted enough so that child widgets don't have to worry about handling specific scrolling
+situations, such as with a Table? Probably not.
+
## Future Work
Don't require an application. Allow certain widgets to be used within scripts to automate certain tasks that may require using multiple apps?
View
2 bin/sikwidgets
@@ -9,4 +9,4 @@ if [ -z $SIKULI_HOME ]; then
fi
SCRIPT_PATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
-java -cp "$SIKULI_HOME/sikuli-script.jar" org.python.util.jython "$SCRIPT_PATH/sw.py" $@
+java -cp "$SIKULI_HOME/sikuli-script.jar" org.python.util.jython "$SCRIPT_PATH/sikwidgets_script.py" $@
View
2 bin/sikwidgets.bat
@@ -1,4 +1,4 @@
set JAVA_EXE="C:\Program Files\Java\jre6\bin\java.exe"
if defined PROGRAMFILES(X86) set JAVA_EXE="%PROGRAMFILES(X86)%\Java\jre6\bin\java.exe"
set SCRIPT_PATH=%~dp0
-%JAVA_EXE% -cp "%SIKULI_HOME%\sikuli-script.jar" org.python.util.jython "%SCRIPT_PATH%\sw.py" %*
+%JAVA_EXE% -cp "%SIKULI_HOME%\sikuli-script.jar" org.python.util.jython "%SCRIPT_PATH%\sikwidgets_script.py" %*
View
0 bin/sw.py → bin/sikwidgets_script.py
File renamed without changes.
View
2 setup.py
@@ -5,5 +5,5 @@
description='Adds GUI mockup functionality to Sikuli for easier, robust testing',
author='Joel Griffith',
packages=find_packages(),
- scripts=['bin/sw.py', 'bin/sikwidgets.bat', 'bin/sikwidgets']
+ scripts=['bin/sikwidgets_script.py', 'bin/sikwidgets.bat', 'bin/sikwidgets']
)
View
14 sikwidgets/util.py
@@ -75,4 +75,16 @@ def wrapped(*args, **kwargs):
move_mouse(original_location)
Settings.MoveMouseDelay = original_move_delay
return result
- return wrapped
+ return wrapped
+
+def clicks_per_widget(widget_size, pixels_per_scroll):
+ num_clicks = 1
+ if pixels_per_scroll != widget_size:
+ # FIXME
+ # very naively calculate how many clicks we need to
+ # do to scroll a widget at a time
+ if pixels_per_scroll < widget_size:
+ num_clicks *= widget_size / pixels_per_scroll
+ else:
+ num_clicks /= pixels_per_scroll / widget_size
+ return num_clicks
View
215 sikwidgets/widgets/table.py
@@ -7,6 +7,7 @@
from sikwidgets.widgets.button import Button
from sikwidgets.widgets.scrollable_widget import ScrollableWidget
from sikwidgets import settings
+from sikwidgets.util import clicks_per_widget
# TODO: create subfolders of table columns for each expected cell.
# This way, cells can have states just like other widgets.
@@ -31,38 +32,36 @@ class Table(ScrollableWidget):
def __init__(self, parent, name, columns,
row_height, rows_per_page, pixels_per_scroll=None):
ScrollableWidget.__init__(self, parent, name)
+ self.set_columns(columns)
+ self.rows = []
+ self.set_row_attributes(row_height, rows_per_page, pixels_per_scroll)
- self.columns = []
- self.column = {}
- for column_name in columns:
- column = TableColumn(self, column_name)
- # keep an ordered list so we know how the columns go
- # left-to-right
- self.columns.append(column)
- # keep a hash so we can conveniently access columns by name
- self.column[column_name] = column
-
+ def set_row_attributes(row_height, rows_per_page, pixels_per_scroll=None):
self.row_height = row_height
self.rows_per_page = rows_per_page
if not pixels_per_scroll:
# the sane default per scroll is exactly one row
self.pixels_per_scroll = row_height
+ self.clicks_per_row = clicks_per_widget(self.row_height, self.pixels_per_scroll)
- self.clicks_per_row = 1
- if self.pixels_per_scroll != self.row_height:
- # FIXME
- # very naively calculate how many clicks we need to
- # do to scroll a row at a time
- if self.pixels_per_scroll < self.row_height:
- self.clicks_per_row *= self.row_height / self.pixels_per_scroll
- else:
- self.clicks_per_row /= self.pixels_per_scroll / self.row_height
+ def add_column(self, column_name):
+ column = TableColumn(self, column_name)
+ # keep an ordered list so we know how the columns go
+ # left-to-right
+ self.columns.append(column)
+ # keep a hash so we can conveniently access columns by name
+ self.column[column_name] = column
+
+ def set_columns(self, column_names):
+ self.columns = []
+ self.column = {}
+ for column_name in column_names:
+ self.add_column(column_name)
def capture_screenshots(self):
for column in self.columns:
column.capture_screenshots()
- # capture screenshots of the scrollbars, followed
- # finally by 'expected' cell states in the table itself
+ # capture screenshots of the scrollbars
ScrollableWidget.capture_screenshots(self)
def scroll_up(self, num_rows=1):
@@ -77,6 +76,109 @@ def scroll_down(self, num_rows=1):
actual_rows = actual_clicks / self.clicks_per_row
return actual_rows
+ def row_scanner(self):
+ self.scroll_to_top()
+ # the index of a row in terms of the visible page
+ starting_page_row_index = 0
+ # the amount of times we've scrolled in terms of rows
+ total_rows_scrolled = 0
+ current_row_index = 0
+ scanned_page = False
+ while not self.scrollbar_at_bottom() or not scanned_page:
+ # loop through the visible page of rows
+ for page_row_index in range(starting_page_row_index, self.rows_per_page):
+ if len(self.rows) <= current_row_index:
+ # If the row hasn't been generated yet, do so.
+ # The row needs to know its index in the overall table
+ # as well as the amount of scrolls necessary to reach it
+ # from the top
+ self.rows.append(TableRow(self, current_row_index, total_rows_scrolled))
+ # return the row's index in the current page and the row itself
+ yield (page_row_index, self.rows[current_row_index])
+ current_row_index += 1
+ # we scanned the full page of visible rows
+ scanned_page = True
+ if not self.scrollbar_at_bottom():
+ # but there are more pages to scan
+ if settings.DEBUG:
+ print "Reached end of rows; scrolling down a page"
+ rows_scrolled = self.scroll_down(self.rows_per_page)
+ if not rows_scrolled:
+ break
+ total_rows_scrolled += rows_scrolled
+ starting_page_row_index = self.rows_per_page - rows_scrolled
+ scanned_page = False
+ yield None
+
+ def first_row_where(self, **kwargs):
+ return self.next_row_where(**kwargs).next()
+
+ def rows_where(self, **kwargs):
+ rows = []
+ row_gen = self.next_row_where(**kwargs)
+ row = row_gen.next()
+ while row:
+ rows.append(row)
+ row = row_gen.next()
+ return rows
+
+ # TODO: in the process of converting this to a generator
+ # method first_row_where and rows_where will call
+ def next_row_where(self, **kwargs):
+ row_scanner = self.row_scanner()
+ page_row_index, row = row_scanner.next()
+ while row:
+ for column_name, cell_value in kwargs.iteritems():
+ if not row.column_has(column_name, cell_value):
+ # one of the column values didn't match,
+ # so this row isn't a match
+ break
+
+ row = row_scanner.next()
+
+ cell_region = self.cell_region_at(cur_cell)
+ # hover over it as visual feedback
+ cell_region.hover(cell_region)
+ # look within the cell region for cell value
+ match = None
+ if use_text:
+ # search for the text in the cell region
+ match = self.do_find_in(cell_region, cell_value)
+ else:
+ # search within the cell region to find the expected cell
+ # (checking all its states)
+ match = self.expected_cell[cell_value].find_in(cell_region)
+
+ if match:
+ if settings.DEBUG:
+ print "Found a cell matching '%s'" % cell_value
+ row_index = cur_cell + total_rows_scrolled
+ yield TableCell(self, match, row_index)
+
+
+
+
+ row_cells = {}
+ for column_name, cell_value in kwargs.iteritems():
+ matching_cells = self.column[column_name].cells_with(cell_value)
+ if not matching_cells:
+ yield None
+ elif row_cells is None:
+ row_cells = {}
+ for cell in matching_cells:
+ row_cells[cell.row_index] = [cell]
+ else:
+ for row_index, cells in row_cells.iteritems():
+ cell = filter(lambda c: c.row_index == row_index, matching_cells)
+ if not cell:
+ del row_cells[row_index]
+ else:
+ row_cells[row_index].append(cell[0])
+ rows = []
+ for row_index, cells in row_cells.iteritems():
+ rows.append(TableRow(self, row_index, cells))
+ return rows
+
def cell_exists(self, column_name, cell_value):
if self.first_cell_where(**{column_name: cell_value}):
return True
@@ -90,13 +192,6 @@ def first_row_where(self, **kwargs):
return rows[0]
return None
- # TODO: in the process of converting this to a generator
- # method first_row_where and rows_where will call
- def next_row_where(self, **kwargs):
- row_cells = {}
- for column_name, cell_value in kwargs.iteritems():
- # search by column for all cells that match
- matching_cells = self.
def rows_where(self, **kwargs):
# TODO: improve search algorithm so that only the rows
@@ -141,9 +236,6 @@ def exists(self, force_check=False):
return False
-# A column has cells, and cells make up rows. We'll always be searching
-# by column to find rows, which entails looking up column cells and deducing
-# which row the cells belong to
class TableColumn(Widget):
def __init__(self, table, name):
Widget.__init__(self, table, name)
@@ -160,7 +252,7 @@ def load_expected_cells(self):
path = self.image_folder()
expected_cells = [cell for cell in os.listdir(path) if os.path.isdir(os.path.join(path, cell))]
for cell_name in expected_cells:
- self.expected_cell[cell_name] = Button(self, cell_name)
+ self.expected_cell[cell_name] = Widget(self, cell_name)
def exists(self, force_check=False):
if self.locate_header(force_check):
@@ -212,18 +304,7 @@ def cells_with(self, cell_value):
# TODO: store cells in cache
return cells
- def cell_region_at(row_index):
- """ Returns the region which will be searched in
- for the cell at the given row index in the table
- """
- self.locate_header()
- # find the cell's index on the current page of the table
- page_cell_index = row_index % self.table.rows_per_page
- y_offset = self.table.row_height + (page_cell_index * self.table.row_height)
- cell_region = self.header_region.offset(Location(0, y_offset))
- cell_region.setH(self.table.row_height)
- cell_region = cell_region.nearby(self.table.row_height / 2)
- return cell_region
+
# TODO: scrolling left or right to show columns :|
# It should be done like this:
@@ -279,15 +360,38 @@ class TableCell(Widget):
""" A generated, "virtual" widget. Its region is actually a Match from
a search performed in TableColumn.
"""
- def __init__(self, column, row_index, cell_match=None):
- Widget.__init__(self, column)
- if cell_match:
- self.region = cell_match # FIXME: convert to Region?
- self.found = True
- else:
+ def __init__(self, table, column, row):
+ Widget.__init__(self, table)
+ self.table = table
+ self.column = column
+ self.row = row
+ self.search_region = self.cell
self.column = column
self.table = column.table
- self.row_index = row_index
+ self.row = row
+
+ def cell_region_at(row_index):
+ """ Returns the region which will be searched in
+ for the cell at the given row index in the table
+ """
+ self.locate_header()
+ # find the cell's index on the current page of the table
+ page_cell_index = row_index % self.table.rows_per_page
+ y_offset = self.table.row_height + (page_cell_index * self.table.row_height)
+ cell_region = self.header_region.offset(Location(0, y_offset))
+ cell_region.setH(self.table.row_height)
+ cell_region = cell_region.nearby(self.table.row_height / 2)
+ return cell_region
+
+ # TODO: need to scroll right (if necessary) as well
+ def scroll_to(self):
+ self.table.scroll_to_left()
+ self.table.scroll_to_top()
+ num_scrolls_down = max(self.row_index+1 - self.table.rows_per_page, 0)
+ self.table.scroll_down(num_scrolls_down)
+ if self.num_scrolls_right is None:
+ while self.column.exists(force_check=True)
+ self.table.scroll_right(self.num_scrolls_right)
# FIXME: the methods below are too naive. They will not work
# if the page has been scrolled. A cell needs to know
@@ -305,12 +409,15 @@ def __init__(self, column, row_index, cell_match=None):
class TableRow(Widget):
""" A generated, "virtual" widget """
- # TODO: should TableColumn
- def __init__(self, table, index, cells=[]):
+ def __init__(self, table, index, scrolls_from_top):
Widget.__init__(self, table)
self.table = table
self.index = index
- self.cells = cells
+ self.scrolls_from_top = scrolls_from_top
+ self.cell = {}
+
+ def contains(column_name, cell_value):
+ region_contains
def cell_under(self, column_name):
# if we already had the cell stored, return it

0 comments on commit 628efc2

Please sign in to comment.
Something went wrong with that request. Please try again.