Advertisement
fernly

table-test-proxy

Mar 16th, 2015
260
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 7.84 KB | None | 0 0
  1. import random
  2. import time
  3. from sortedcontainers import SortedDict
  4.  
  5. # Define the database. At this point it comprises a single SortedDict in
  6. # which the keys are rwt(8) "word" tokens, and the values are tuples
  7. # comprising ( word, int in 0-99, rft() flag string). Yes this is
  8. # redundant in having the word as both key and value but it simplifies
  9. # coding the data() method of the model.
  10.  
  11. DBM = SortedDict()
  12. DBV = None # valuesview
  13.  
  14. DBS = 10000 # how many rows
  15.  
  16. # These functions are used to populate the database with
  17. # values that are kinda-sorta like the ones in my app.
  18.  
  19. def xdi(B,N,M):
  20.     '''
  21.            eXponential Distribution of Integers, XDI
  22.    Return a list of length N of integers with max value M, from an exponential
  23.    distribution with beta B.
  24.    '''
  25.     M = int(M) # just in case
  26.     # pre-allocate the list rather than building it with .append()
  27.     r = [0]*N
  28.     for j in range(N) :
  29.         r[j] = int( random.expovariate(B) ) % M
  30.     return r
  31.  
  32. RWTALPH = 'aeiouåáïbcdfghjklmnpqrstvwxyz_BCDFGHJKLMNPQRSTVWZ'
  33. def rwt(N):
  34.     '''
  35.            Random Word-like Token, RWT
  36.    Return a word-like token comprising N Latin-1 letters with an
  37.    emphasis on vowels.
  38.    '''
  39.     ii = xdi( 0.1, N, len(RWTALPH)-1 )
  40.     return ''.join( [ RWTALPH[i] for i in ii ] )
  41.  
  42. def rft():
  43.     '''
  44.            Random Flag Token
  45.    Return an 8-char token composed mostly of dashes with a sprinkle of Xs,
  46.    e.g. "---X--X-" or "XX------".
  47.  
  48.    '''
  49.     ii = xdi( 2, 8, 2 )
  50.     return ''.join( ['-X'[i] for i in ii ] )
  51.  
  52. # Clear the database, called before Refresh
  53. def clear_the_db() :
  54.     global DBM, DBV
  55.     DBM.clear()
  56.     DBV = None
  57.  
  58. # Rebuild the database, called on a Refresh.
  59.  
  60. def rebuild_the_db():
  61.     global DBM, DBV, DBS
  62.     '''
  63.    [re]populate the fake database:
  64.    * Create a vector of random numbers which will constitute
  65.      Column 2 and also pace the creation loop
  66.    * Clear the SortedDict and load it with DBS rows of fake data
  67.    * Recreate the keysview and valuesview
  68.    '''
  69.     col2 = xdi( 0.05, DBS, 100 )
  70.     assert DBS == len(col2) # once in a blue moon...
  71.     clear_the_db()
  72.     for k in col2 :
  73.         w = rwt(8)
  74.         f = rft()
  75.         DBM[ w ] = ( w, k, f )
  76.     DBV = DBM.values()
  77.  
  78. # Define the Table Model, instrumented to count certain calls
  79.  
  80. from PyQt5.QtCore import Qt, QAbstractTableModel
  81.  
  82. class Model( QAbstractTableModel ):
  83.     def __init__ ( self, parent=None ) :
  84.         super().__init__( parent )
  85.         self.access_counts = [0, 0, 0]
  86.  
  87.     def rowCount( self, index ) :
  88.         global DBM
  89.         if index.isValid() : return 0
  90.         return len(DBM)
  91.  
  92.     def columnCount( self, index ) :
  93.         if index.isValid() : return 0
  94.         return 3
  95.  
  96.     def data(self, index, role ) :
  97.         global DBV
  98.         if role != Qt.DisplayRole :
  99.             return None
  100.         row = index.row()
  101.         col = index.column()
  102.         self.access_counts[col] += 1
  103.         return DBV[ row ][ col ]
  104.  
  105.     def clear_counts( self ) :
  106.         self.access_counts = [0, 0, 0]
  107.  
  108.     def counts( self ) :
  109.         return list( self.access_counts )
  110.  
  111. # Define the Table View, which at this point is quite minimal.
  112. # The View is instantiated from MainWindow, which also connects
  113. # the model to it.
  114.  
  115. from PyQt5.QtWidgets import QTableView
  116.  
  117. class View( QTableView ):
  118.     def __init__ ( self, parent=None ) :
  119.         super().__init__( parent )
  120.         self.setSortingEnabled( True )
  121.         self.sortByColumn( 0, Qt.AscendingOrder )
  122.  
  123. # Define the SortFilterProxy, which at this point is quite minimal.
  124. # It too is instantiated by the Mainwindow.
  125.  
  126. from PyQt5.QtCore import QSortFilterProxyModel
  127. class Proxy( QSortFilterProxyModel ) :
  128.     def __init__ ( self, parent=None ) :
  129.         super().__init__( parent )
  130.         self.setSortCaseSensitivity( True )
  131.         self.setSortLocaleAware( True )
  132.  
  133. # Define the main window which is the visual face of this app.
  134.  
  135. from PyQt5.QtWidgets import (
  136.     QLabel,
  137.     QMainWindow,
  138.     QPushButton,
  139.     QVBoxLayout,
  140.     QHBoxLayout,
  141.     QWidget
  142.     )
  143.  
  144. class Main( QMainWindow ) :
  145.     def __init__ ( self ) :
  146.         super().__init__( )
  147.         self.times = [0, 0, 0]
  148.         clear_the_db() # make sure to start with 0 rows
  149.         self._uic() # all the layout stuff out of line
  150.         self.refresh_button.clicked.connect( self.do_refresh )
  151.  
  152.     # Slot called when Refresh is clicked:
  153.     # * clear the counts
  154.     # * start model reset
  155.     # * rebuild the DBM, timing it
  156.     # * end the model reset, timing that
  157.     # * update the labels displaying call counts and times
  158.  
  159.     def do_refresh( self ) :
  160.         self.table_model.clear_counts()
  161.         self.table_model.beginResetModel()
  162.         self.times[0] = time.process_time()
  163.         rebuild_the_db()
  164.         self.times[1] = time.process_time()
  165.         self.table_model.endResetModel()
  166.         QApplication.processEvents()
  167.         self.times[2] = time.process_time()
  168.         self.update_labels()
  169.  
  170.     def update_labels(self) :
  171.         [c0, c1, c2] = self.table_model.counts()
  172.         [t0, t1, t2] = self.times
  173.         self.c0_label.setText( str(c0) )
  174.         self.c1_label.setText( str(c1) )
  175.         self.c2_label.setText( str(c2) )
  176.         self.td_label.setText( '{:02.5f}'.format(t1-t0) )
  177.         self.tr_label.setText( '{:02.5f}'.format(t2-t1) )
  178.  
  179.     def _make_label( self, text='0' ) :
  180.         # just make a right-aligned label out of line
  181.         L = QLabel(text)
  182.         L.setAlignment( Qt.AlignRight | Qt.AlignVCenter )
  183.         return L
  184.  
  185.     def _uic( self ) :
  186.         # create the table model, view and proxy
  187.         self.table_view = View( parent=self )
  188.         self.table_model = Model( parent=self )
  189.         self.table_proxy = Proxy( parent=self )
  190.         self.table_proxy.setSourceModel( self.table_model )
  191.         self.table_view.setModel( self.table_proxy )
  192.         # create the refresh button, put it in an hbox by itself, for now
  193.         self.refresh_button = QPushButton( "Refresh" )
  194.         hb0 = QHBoxLayout()
  195.         hb0.addWidget(self.refresh_button, 0)
  196.         hb0.addStretch(1)
  197.         # create a set of labels to display counts of
  198.         # entry to the model.data() method and times
  199.         self.c0_label = self._make_label() # display role calls to column 0
  200.         self.c1_label = self._make_label() # 1
  201.         self.c2_label = self._make_label() # 2
  202.         self.tr_label = self._make_label() # time to reset the model
  203.         self.td_label = self._make_label() # time to rebuild the db
  204.         # build the row of call numbers
  205.         hb1 = QHBoxLayout()
  206.         hb1.addStretch(1) # push this row to the right
  207.         hb1.addWidget( self._make_label( 'Display role calls col 0:' ) )
  208.         hb1.addWidget( self.c0_label )
  209.         hb1.addStretch(0)
  210.         hb1.addWidget( self._make_label( 'col 1:' ) )
  211.         hb1.addWidget( self.c1_label )
  212.         hb1.addStretch(0)
  213.         hb1.addWidget( self._make_label( 'col 2:' ) )
  214.         hb1.addWidget( self.c2_label )
  215.         # build the row of times
  216.         hb2 = QHBoxLayout()
  217.         hb2.addStretch(1)
  218.         hb2.addWidget( self._make_label( 'Seconds to build DB:' ) )
  219.         hb2.addWidget( self.td_label )
  220.         hb2.addStretch(0)
  221.         hb2.addWidget( self._make_label( 'to reset model:' ) )
  222.         hb2.addWidget( self.tr_label )
  223.         # stack up the central layout
  224.         vb = QVBoxLayout()
  225.         vb.addLayout( hb0, 0 )
  226.         vb.addWidget( self.table_view, 1 )
  227.         vb.addLayout( hb1, 0 )
  228.         vb.addLayout( hb2, 0 )
  229.         # put all that in a widget and make the widget our central layout
  230.         wij = QWidget()
  231.         wij.setLayout( vb )
  232.         wij.setMinimumSize( 500, 500 )
  233.         self.setCentralWidget( wij )
  234.  
  235.  
  236. if __name__ == '__main__' :
  237.     from PyQt5.QtWidgets import QApplication
  238.     the_app = QApplication([])
  239.     main_window = Main()
  240.     main_window.show()
  241.     the_app.exec_()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement