nltk 3.2__zip → 3.3__zip

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (406) hide show
  1. {nltk-3.2 → nltk-3.3}/INSTALL.txt +6 -6
  2. {nltk-3.2 → nltk-3.3}/LICENSE.txt +13 -13
  3. {nltk-3.2 → nltk-3.3}/MANIFEST.in +6 -6
  4. {nltk-3.2/nltk.egg-info → nltk-3.3}/PKG-INFO +13 -5
  5. nltk-3.2/README.txt → nltk-3.3/README.md +49 -48
  6. nltk-3.3/nltk/VERSION +1 -0
  7. {nltk-3.2 → nltk-3.3}/nltk/__init__.py +185 -185
  8. {nltk-3.2 → nltk-3.3}/nltk/app/__init__.py +52 -53
  9. {nltk-3.2 → nltk-3.3}/nltk/app/chartparser_app.py +2276 -2276
  10. {nltk-3.2 → nltk-3.3}/nltk/app/chunkparser_app.py +1262 -1264
  11. {nltk-3.2 → nltk-3.3}/nltk/app/collocations_app.py +347 -350
  12. {nltk-3.2 → nltk-3.3}/nltk/app/concordance_app.py +568 -570
  13. {nltk-3.2 → nltk-3.3}/nltk/app/nemo_app.py +156 -155
  14. {nltk-3.2 → nltk-3.3}/nltk/app/rdparser_app.py +892 -894
  15. {nltk-3.2 → nltk-3.3}/nltk/app/srparser_app.py +809 -809
  16. {nltk-3.2 → nltk-3.3}/nltk/app/wordfreq_app.py +32 -32
  17. {nltk-3.2 → nltk-3.3}/nltk/app/wordnet_app.py +968 -968
  18. {nltk-3.2 → nltk-3.3}/nltk/book.py +91 -90
  19. {nltk-3.2 → nltk-3.3}/nltk/ccg/__init__.py +22 -22
  20. {nltk-3.2 → nltk-3.3}/nltk/ccg/api.py +359 -335
  21. {nltk-3.2 → nltk-3.3}/nltk/ccg/chart.py +394 -361
  22. {nltk-3.2 → nltk-3.3}/nltk/ccg/combinator.py +343 -312
  23. {nltk-3.2 → nltk-3.3}/nltk/ccg/lexicon.py +328 -290
  24. nltk-3.3/nltk/ccg/logic.py +46 -0
  25. {nltk-3.2 → nltk-3.3}/nltk/chat/__init__.py +49 -49
  26. {nltk-3.2 → nltk-3.3}/nltk/chat/eliza.py +244 -244
  27. {nltk-3.2 → nltk-3.3}/nltk/chat/iesha.py +140 -140
  28. {nltk-3.2 → nltk-3.3}/nltk/chat/rude.py +92 -92
  29. {nltk-3.2 → nltk-3.3}/nltk/chat/suntsu.py +117 -117
  30. {nltk-3.2 → nltk-3.3}/nltk/chat/util.py +122 -120
  31. {nltk-3.2 → nltk-3.3}/nltk/chat/zen.py +282 -282
  32. {nltk-3.2 → nltk-3.3}/nltk/chunk/__init__.py +190 -190
  33. {nltk-3.2 → nltk-3.3}/nltk/chunk/api.py +51 -51
  34. {nltk-3.2 → nltk-3.3}/nltk/chunk/named_entity.py +331 -331
  35. {nltk-3.2 → nltk-3.3}/nltk/chunk/regexp.py +1390 -1384
  36. {nltk-3.2 → nltk-3.3}/nltk/chunk/util.py +600 -600
  37. {nltk-3.2 → nltk-3.3}/nltk/classify/__init__.py +98 -98
  38. {nltk-3.2 → nltk-3.3}/nltk/classify/api.py +193 -193
  39. {nltk-3.2 → nltk-3.3}/nltk/classify/decisiontree.py +295 -295
  40. {nltk-3.2 → nltk-3.3}/nltk/classify/maxent.py +1510 -1492
  41. {nltk-3.2 → nltk-3.3}/nltk/classify/megam.py +179 -178
  42. {nltk-3.2 → nltk-3.3}/nltk/classify/naivebayes.py +245 -242
  43. {nltk-3.2 → nltk-3.3}/nltk/classify/positivenaivebayes.py +170 -170
  44. {nltk-3.2 → nltk-3.3}/nltk/classify/rte_classify.py +160 -191
  45. {nltk-3.2 → nltk-3.3}/nltk/classify/scikitlearn.py +153 -151
  46. {nltk-3.2 → nltk-3.3}/nltk/classify/senna.py +183 -181
  47. {nltk-3.2 → nltk-3.3}/nltk/classify/svm.py +15 -15
  48. {nltk-3.2 → nltk-3.3}/nltk/classify/tadm.py +113 -112
  49. {nltk-3.2 → nltk-3.3}/nltk/classify/textcat.py +193 -193
  50. {nltk-3.2 → nltk-3.3}/nltk/classify/util.py +324 -312
  51. {nltk-3.2 → nltk-3.3}/nltk/classify/weka.py +346 -345
  52. {nltk-3.2 → nltk-3.3}/nltk/cluster/__init__.py +86 -86
  53. {nltk-3.2 → nltk-3.3}/nltk/cluster/api.py +74 -70
  54. {nltk-3.2 → nltk-3.3}/nltk/cluster/em.py +247 -247
  55. {nltk-3.2 → nltk-3.3}/nltk/cluster/gaac.py +168 -168
  56. {nltk-3.2 → nltk-3.3}/nltk/cluster/kmeans.py +221 -221
  57. {nltk-3.2 → nltk-3.3}/nltk/cluster/util.py +304 -289
  58. nltk-3.3/nltk/collections.py +640 -0
  59. {nltk-3.2 → nltk-3.3}/nltk/collocations.py +388 -388
  60. nltk-3.3/nltk/compat.py +365 -0
  61. {nltk-3.2 → nltk-3.3}/nltk/corpus/__init__.py +341 -320
  62. {nltk-3.2 → nltk-3.3}/nltk/corpus/europarl_raw.py +44 -44
  63. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/__init__.py +147 -145
  64. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/aligned.py +115 -114
  65. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/api.py +448 -446
  66. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/bnc.py +252 -252
  67. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/bracket_parse.py +227 -227
  68. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/categorized_sents.py +179 -178
  69. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/chasen.py +140 -139
  70. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/childes.py +522 -494
  71. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/chunked.py +212 -212
  72. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/cmudict.py +97 -95
  73. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/comparative_sents.py +280 -278
  74. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/conll.py +528 -521
  75. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/crubadan.py +116 -116
  76. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/dependency.py +101 -101
  77. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/framenet.py +3059 -2093
  78. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/ieer.py +112 -111
  79. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/indian.py +85 -86
  80. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/ipipan.py +332 -331
  81. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/knbc.py +187 -187
  82. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/lin.py +156 -156
  83. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/mte.py +311 -310
  84. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/nkjp.py +429 -428
  85. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/nombank.py +421 -421
  86. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/nps_chat.py +73 -73
  87. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/opinion_lexicon.py +115 -115
  88. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/panlex_lite.py +165 -165
  89. nltk-3.3/nltk/corpus/reader/pl196x.py +292 -0
  90. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/plaintext.py +232 -233
  91. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/ppattach.py +96 -95
  92. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/propbank.py +479 -481
  93. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/pros_cons.py +128 -126
  94. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/reviews.py +330 -328
  95. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/rte.py +144 -146
  96. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/semcor.py +256 -256
  97. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/senseval.py +203 -201
  98. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/sentiwordnet.py +137 -137
  99. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/sinica_treebank.py +75 -75
  100. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/string_category.py +60 -59
  101. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/switchboard.py +119 -119
  102. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/tagged.py +295 -294
  103. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/timit.py +452 -450
  104. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/toolbox.py +68 -68
  105. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/twitter.py +155 -157
  106. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/udhr.py +80 -80
  107. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/util.py +804 -800
  108. nltk-3.3/nltk/corpus/reader/verbnet.py +595 -0
  109. nltk-3.3/nltk/corpus/reader/wordlist.py +134 -0
  110. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/wordnet.py +2108 -2036
  111. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/xmldocs.py +392 -387
  112. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/ycoe.py +243 -242
  113. {nltk-3.2 → nltk-3.3}/nltk/corpus/util.py +144 -127
  114. {nltk-3.2 → nltk-3.3}/nltk/data.py +1487 -1451
  115. {nltk-3.2 → nltk-3.3}/nltk/decorators.py +219 -215
  116. {nltk-3.2 → nltk-3.3}/nltk/downloader.py +2278 -2274
  117. {nltk-3.2 → nltk-3.3}/nltk/draw/__init__.py +27 -27
  118. {nltk-3.2 → nltk-3.3}/nltk/draw/cfg.py +774 -777
  119. {nltk-3.2 → nltk-3.3}/nltk/draw/dispersion.py +58 -58
  120. {nltk-3.2 → nltk-3.3}/nltk/draw/table.py +1098 -1098
  121. {nltk-3.2 → nltk-3.3}/nltk/draw/tree.py +963 -966
  122. {nltk-3.2 → nltk-3.3}/nltk/draw/util.py +2356 -2356
  123. {nltk-3.2 → nltk-3.3}/nltk/featstruct.py +2617 -2500
  124. {nltk-3.2 → nltk-3.3}/nltk/grammar.py +1559 -1516
  125. {nltk-3.2 → nltk-3.3}/nltk/help.py +56 -56
  126. {nltk-3.2 → nltk-3.3}/nltk/inference/__init__.py +20 -20
  127. {nltk-3.2 → nltk-3.3}/nltk/inference/api.py +597 -589
  128. {nltk-3.2 → nltk-3.3}/nltk/inference/discourse.py +612 -608
  129. {nltk-3.2 → nltk-3.3}/nltk/inference/mace.py +311 -311
  130. {nltk-3.2 → nltk-3.3}/nltk/inference/nonmonotonic.py +509 -509
  131. {nltk-3.2 → nltk-3.3}/nltk/inference/prover9.py +431 -431
  132. {nltk-3.2 → nltk-3.3}/nltk/inference/resolution.py +687 -687
  133. {nltk-3.2 → nltk-3.3}/nltk/inference/tableau.py +607 -607
  134. {nltk-3.2 → nltk-3.3}/nltk/internals.py +982 -982
  135. {nltk-3.2 → nltk-3.3}/nltk/jsontags.py +63 -63
  136. {nltk-3.2 → nltk-3.3}/nltk/lazyimport.py +142 -142
  137. {nltk-3.2 → nltk-3.3}/nltk/metrics/__init__.py +30 -29
  138. {nltk-3.2 → nltk-3.3}/nltk/metrics/agreement.py +421 -401
  139. nltk-3.3/nltk/metrics/aline.py +607 -0
  140. {nltk-3.2 → nltk-3.3}/nltk/metrics/association.py +414 -410
  141. {nltk-3.2 → nltk-3.3}/nltk/metrics/confusionmatrix.py +206 -206
  142. {nltk-3.2 → nltk-3.3}/nltk/metrics/distance.py +207 -202
  143. {nltk-3.2 → nltk-3.3}/nltk/metrics/paice.py +381 -381
  144. {nltk-3.2 → nltk-3.3}/nltk/metrics/scores.py +228 -228
  145. {nltk-3.2 → nltk-3.3}/nltk/metrics/segmentation.py +231 -232
  146. {nltk-3.2 → nltk-3.3}/nltk/metrics/spearman.py +69 -69
  147. {nltk-3.2 → nltk-3.3}/nltk/misc/__init__.py +11 -11
  148. {nltk-3.2 → nltk-3.3}/nltk/misc/babelfish.py +10 -10
  149. {nltk-3.2 → nltk-3.3}/nltk/misc/chomsky.py +135 -133
  150. {nltk-3.2 → nltk-3.3}/nltk/misc/minimalset.py +83 -83
  151. {nltk-3.2 → nltk-3.3}/nltk/misc/sort.py +157 -157
  152. {nltk-3.2 → nltk-3.3}/nltk/misc/wordfinder.py +129 -129
  153. {nltk-3.2 → nltk-3.3}/nltk/parse/__init__.py +81 -80
  154. {nltk-3.2 → nltk-3.3}/nltk/parse/api.py +66 -66
  155. {nltk-3.2 → nltk-3.3}/nltk/parse/bllip.py +282 -282
  156. {nltk-3.2 → nltk-3.3}/nltk/parse/chart.py +1682 -1681
  157. nltk-3.3/nltk/parse/corenlp.py +780 -0
  158. {nltk-3.2 → nltk-3.3}/nltk/parse/dependencygraph.py +760 -758
  159. {nltk-3.2 → nltk-3.3}/nltk/parse/earleychart.py +452 -451
  160. {nltk-3.2 → nltk-3.3}/nltk/parse/evaluate.py +129 -129
  161. {nltk-3.2 → nltk-3.3}/nltk/parse/featurechart.py +580 -578
  162. {nltk-3.2 → nltk-3.3}/nltk/parse/generate.py +87 -76
  163. {nltk-3.2 → nltk-3.3}/nltk/parse/malt.py +356 -356
  164. {nltk-3.2 → nltk-3.3}/nltk/parse/nonprojectivedependencyparser.py +783 -783
  165. {nltk-3.2 → nltk-3.3}/nltk/parse/pchart.py +522 -522
  166. {nltk-3.2 → nltk-3.3}/nltk/parse/projectivedependencyparser.py +579 -578
  167. {nltk-3.2 → nltk-3.3}/nltk/parse/recursivedescent.py +655 -656
  168. {nltk-3.2 → nltk-3.3}/nltk/parse/shiftreduce.py +458 -459
  169. {nltk-3.2 → nltk-3.3}/nltk/parse/stanford.py +431 -406
  170. {nltk-3.2 → nltk-3.3}/nltk/parse/transitionparser.py +774 -772
  171. {nltk-3.2 → nltk-3.3}/nltk/parse/util.py +231 -231
  172. {nltk-3.2 → nltk-3.3}/nltk/parse/viterbi.py +401 -401
  173. {nltk-3.2 → nltk-3.3}/nltk/probability.py +2410 -2355
  174. {nltk-3.2 → nltk-3.3}/nltk/sem/__init__.py +61 -61
  175. {nltk-3.2 → nltk-3.3}/nltk/sem/boxer.py +1261 -1261
  176. {nltk-3.2 → nltk-3.3}/nltk/sem/chat80.py +782 -782
  177. {nltk-3.2 → nltk-3.3}/nltk/sem/cooper_storage.py +118 -118
  178. {nltk-3.2 → nltk-3.3}/nltk/sem/drt.py +1258 -1257
  179. {nltk-3.2 → nltk-3.3}/nltk/sem/drt_glue_demo.py +485 -489
  180. {nltk-3.2 → nltk-3.3}/nltk/sem/evaluate.py +790 -784
  181. {nltk-3.2 → nltk-3.3}/nltk/sem/glue.py +696 -694
  182. {nltk-3.2 → nltk-3.3}/nltk/sem/hole.py +386 -384
  183. {nltk-3.2 → nltk-3.3}/nltk/sem/lfg.py +210 -208
  184. {nltk-3.2 → nltk-3.3}/nltk/sem/linearlogic.py +451 -449
  185. {nltk-3.2 → nltk-3.3}/nltk/sem/logic.py +1910 -1910
  186. {nltk-3.2 → nltk-3.3}/nltk/sem/relextract.py +476 -483
  187. {nltk-3.2 → nltk-3.3}/nltk/sem/skolemize.py +101 -101
  188. {nltk-3.2 → nltk-3.3}/nltk/sem/util.py +249 -249
  189. {nltk-3.2 → nltk-3.3}/nltk/sentiment/__init__.py +14 -14
  190. {nltk-3.2 → nltk-3.3}/nltk/sentiment/sentiment_analyzer.py +229 -229
  191. {nltk-3.2 → nltk-3.3}/nltk/sentiment/util.py +762 -752
  192. {nltk-3.2 → nltk-3.3}/nltk/sentiment/vader.py +443 -445
  193. {nltk-3.2 → nltk-3.3}/nltk/stem/__init__.py +31 -33
  194. {nltk-3.2 → nltk-3.3}/nltk/stem/api.py +28 -25
  195. nltk-3.3/nltk/stem/arlstem.py +369 -0
  196. {nltk-3.2 → nltk-3.3}/nltk/stem/isri.py +345 -345
  197. {nltk-3.2 → nltk-3.3}/nltk/stem/lancaster.py +337 -310
  198. nltk-3.3/nltk/stem/porter.py +710 -0
  199. {nltk-3.2 → nltk-3.3}/nltk/stem/regexp.py +61 -61
  200. {nltk-3.2 → nltk-3.3}/nltk/stem/rslp.py +142 -142
  201. {nltk-3.2 → nltk-3.3}/nltk/stem/snowball.py +4236 -3712
  202. {nltk-3.2 → nltk-3.3}/nltk/stem/util.py +22 -12
  203. {nltk-3.2 → nltk-3.3}/nltk/stem/wordnet.py +51 -51
  204. {nltk-3.2 → nltk-3.3}/nltk/tag/__init__.py +152 -127
  205. {nltk-3.2 → nltk-3.3}/nltk/tag/api.py +86 -81
  206. {nltk-3.2 → nltk-3.3}/nltk/tag/brill.py +424 -426
  207. {nltk-3.2 → nltk-3.3}/nltk/tag/brill_trainer.py +608 -608
  208. {nltk-3.2 → nltk-3.3}/nltk/tag/crf.py +204 -200
  209. {nltk-3.2 → nltk-3.3}/nltk/tag/hmm.py +1277 -1277
  210. {nltk-3.2 → nltk-3.3}/nltk/tag/hunpos.py +132 -132
  211. {nltk-3.2 → nltk-3.3}/nltk/tag/mapping.py +101 -101
  212. {nltk-3.2 → nltk-3.3}/nltk/tag/perceptron.py +329 -314
  213. {nltk-3.2 → nltk-3.3}/nltk/tag/senna.py +147 -145
  214. {nltk-3.2 → nltk-3.3}/nltk/tag/sequential.py +740 -736
  215. {nltk-3.2 → nltk-3.3}/nltk/tag/stanford.py +219 -196
  216. {nltk-3.2 → nltk-3.3}/nltk/tag/tnt.py +595 -604
  217. {nltk-3.2 → nltk-3.3}/nltk/tag/util.py +72 -72
  218. {nltk-3.2 → nltk-3.3}/nltk/tbl/__init__.py +28 -28
  219. {nltk-3.2 → nltk-3.3}/nltk/tbl/api.py +1 -1
  220. {nltk-3.2 → nltk-3.3}/nltk/tbl/demo.py +366 -366
  221. {nltk-3.2 → nltk-3.3}/nltk/tbl/erroranalysis.py +39 -39
  222. {nltk-3.2 → nltk-3.3}/nltk/tbl/feature.py +259 -261
  223. {nltk-3.2 → nltk-3.3}/nltk/tbl/rule.py +321 -321
  224. {nltk-3.2 → nltk-3.3}/nltk/tbl/template.py +303 -308
  225. {nltk-3.2 → nltk-3.3}/nltk/test/__init__.py +18 -18
  226. {nltk-3.2 → nltk-3.3}/nltk/test/all.py +24 -23
  227. {nltk-3.2 → nltk-3.3}/nltk/test/bleu.doctest +14 -14
  228. {nltk-3.2 → nltk-3.3}/nltk/test/bnc.doctest +60 -60
  229. {nltk-3.2 → nltk-3.3}/nltk/test/ccg.doctest +376 -395
  230. nltk-3.3/nltk/test/ccg_semantics.doctest +553 -0
  231. {nltk-3.2 → nltk-3.3}/nltk/test/chat80.doctest +234 -234
  232. {nltk-3.2 → nltk-3.3}/nltk/test/childes.doctest +184 -184
  233. {nltk-3.2 → nltk-3.3}/nltk/test/childes_fixt.py +14 -13
  234. {nltk-3.2 → nltk-3.3}/nltk/test/chunk.doctest +373 -373
  235. {nltk-3.2 → nltk-3.3}/nltk/test/classify.doctest +185 -185
  236. {nltk-3.2 → nltk-3.3}/nltk/test/classify_fixt.py +11 -10
  237. nltk-3.3/nltk/test/collections.doctest +20 -0
  238. {nltk-3.2 → nltk-3.3}/nltk/test/collocations.doctest +276 -276
  239. {nltk-3.2 → nltk-3.3}/nltk/test/compat.doctest +134 -138
  240. {nltk-3.2 → nltk-3.3}/nltk/test/compat_fixt.py +9 -8
  241. nltk-3.3/nltk/test/concordance.doctest +55 -0
  242. {nltk-3.2 → nltk-3.3}/nltk/test/corpus.doctest +2200 -2212
  243. {nltk-3.2 → nltk-3.3}/nltk/test/corpus_fixt.py +3 -3
  244. {nltk-3.2 → nltk-3.3}/nltk/test/crubadan.doctest +65 -65
  245. {nltk-3.2 → nltk-3.3}/nltk/test/data.doctest +379 -379
  246. {nltk-3.2 → nltk-3.3}/nltk/test/dependency.doctest +241 -241
  247. {nltk-3.2 → nltk-3.3}/nltk/test/discourse.doctest +546 -546
  248. {nltk-3.2 → nltk-3.3}/nltk/test/discourse_fixt.py +14 -13
  249. {nltk-3.2 → nltk-3.3}/nltk/test/doctest_nose_plugin.py +159 -156
  250. {nltk-3.2 → nltk-3.3}/nltk/test/drt.doctest +517 -517
  251. {nltk-3.2 → nltk-3.3}/nltk/test/featgram.doctest +607 -607
  252. {nltk-3.2 → nltk-3.3}/nltk/test/featstruct.doctest +1229 -1230
  253. {nltk-3.2 → nltk-3.3}/nltk/test/framenet.doctest +288 -239
  254. {nltk-3.2 → nltk-3.3}/nltk/test/generate.doctest +67 -67
  255. {nltk-3.2 → nltk-3.3}/nltk/test/gensim.doctest +140 -141
  256. {nltk-3.2 → nltk-3.3}/nltk/test/gensim_fixt.py +10 -9
  257. {nltk-3.2 → nltk-3.3}/nltk/test/gluesemantics.doctest +384 -386
  258. {nltk-3.2 → nltk-3.3}/nltk/test/gluesemantics_malt.doctest +68 -68
  259. {nltk-3.2 → nltk-3.3}/nltk/test/gluesemantics_malt_fixt.py +12 -11
  260. {nltk-3.2 → nltk-3.3}/nltk/test/grammar.doctest +48 -48
  261. {nltk-3.2 → nltk-3.3}/nltk/test/grammartestsuites.doctest +109 -109
  262. {nltk-3.2 → nltk-3.3}/nltk/test/index.doctest +100 -100
  263. {nltk-3.2 → nltk-3.3}/nltk/test/inference.doctest +534 -534
  264. {nltk-3.2 → nltk-3.3}/nltk/test/inference_fixt.py +12 -11
  265. {nltk-3.2 → nltk-3.3}/nltk/test/internals.doctest +140 -140
  266. {nltk-3.2 → nltk-3.3}/nltk/test/japanese.doctest +48 -48
  267. {nltk-3.2 → nltk-3.3}/nltk/test/logic.doctest +1098 -1098
  268. {nltk-3.2 → nltk-3.3}/nltk/test/metrics.doctest +270 -270
  269. {nltk-3.2 → nltk-3.3}/nltk/test/misc.doctest +118 -118
  270. {nltk-3.2 → nltk-3.3}/nltk/test/nonmonotonic.doctest +286 -286
  271. {nltk-3.2 → nltk-3.3}/nltk/test/nonmonotonic_fixt.py +12 -11
  272. {nltk-3.2 → nltk-3.3}/nltk/test/paice.doctest +35 -35
  273. {nltk-3.2 → nltk-3.3}/nltk/test/parse.doctest +884 -884
  274. {nltk-3.2 → nltk-3.3}/nltk/test/portuguese_en.doctest +565 -565
  275. {nltk-3.2 → nltk-3.3}/nltk/test/portuguese_en_fixt.py +16 -16
  276. {nltk-3.2 → nltk-3.3}/nltk/test/probability.doctest +304 -304
  277. {nltk-3.2 → nltk-3.3}/nltk/test/probability_fixt.py +13 -12
  278. {nltk-3.2 → nltk-3.3}/nltk/test/propbank.doctest +176 -176
  279. {nltk-3.2 → nltk-3.3}/nltk/test/relextract.doctest +263 -263
  280. {nltk-3.2 → nltk-3.3}/nltk/test/resolution.doctest +221 -221
  281. {nltk-3.2 → nltk-3.3}/nltk/test/runtests.py +86 -64
  282. {nltk-3.2 → nltk-3.3}/nltk/test/segmentation_fixt.py +10 -10
  283. {nltk-3.2 → nltk-3.3}/nltk/test/semantics.doctest +665 -665
  284. {nltk-3.2 → nltk-3.3}/nltk/test/semantics_fixt.py +7 -7
  285. {nltk-3.2 → nltk-3.3}/nltk/test/sentiment.doctest +233 -233
  286. {nltk-3.2 → nltk-3.3}/nltk/test/sentiwordnet.doctest +41 -41
  287. {nltk-3.2 → nltk-3.3}/nltk/test/simple.doctest +85 -85
  288. {nltk-3.2 → nltk-3.3}/nltk/test/stem.doctest +78 -78
  289. {nltk-3.2 → nltk-3.3}/nltk/test/tag.doctest +33 -33
  290. {nltk-3.2 → nltk-3.3}/nltk/test/tokenize.doctest +273 -166
  291. {nltk-3.2 → nltk-3.3}/nltk/test/toolbox.doctest +307 -307
  292. {nltk-3.2 → nltk-3.3}/nltk/test/translate.doctest +242 -242
  293. {nltk-3.2 → nltk-3.3}/nltk/test/translate_fixt.py +3 -3
  294. {nltk-3.2 → nltk-3.3}/nltk/test/tree.doctest +1101 -1101
  295. {nltk-3.2 → nltk-3.3}/nltk/test/treeprettyprinter.doctest +127 -127
  296. {nltk-3.2 → nltk-3.3}/nltk/test/treetransforms.doctest +156 -156
  297. nltk-3.3/nltk/test/unit/test_2x_compat.py +63 -0
  298. nltk-3.3/nltk/test/unit/test_aline.py +52 -0
  299. nltk-3.3/nltk/test/unit/test_chunk.py +56 -0
  300. {nltk-3.2 → nltk-3.3}/nltk/test/unit/test_classify.py +55 -53
  301. {nltk-3.2 → nltk-3.3}/nltk/test/unit/test_collocations.py +88 -79
  302. nltk-3.3/nltk/test/unit/test_concordance.py +98 -0
  303. nltk-3.3/nltk/test/unit/test_corenlp.py +1000 -0
  304. {nltk-3.2 → nltk-3.3}/nltk/test/unit/test_corpora.py +200 -183
  305. {nltk-3.2 → nltk-3.3}/nltk/test/unit/test_corpus_views.py +45 -45
  306. {nltk-3.2 → nltk-3.3}/nltk/test/unit/test_hmm.py +88 -88
  307. {nltk-3.2 → nltk-3.3}/nltk/test/unit/test_json2csv_corpus.py +188 -187
  308. {nltk-3.2 → nltk-3.3}/nltk/test/unit/test_naivebayes.py +26 -26
  309. nltk-3.3/nltk/test/unit/test_rte_classify.py +86 -0
  310. {nltk-3.2 → nltk-3.3}/nltk/test/unit/test_seekable_unicode_stream_reader.py +124 -124
  311. nltk-3.3/nltk/test/unit/test_senna.py +74 -0
  312. nltk-3.3/nltk/test/unit/test_stem.py +121 -0
  313. {nltk-3.2 → nltk-3.3}/nltk/test/unit/test_tag.py +20 -20
  314. {nltk-3.2 → nltk-3.3}/nltk/test/unit/test_tgrep.py +628 -626
  315. nltk-3.3/nltk/test/unit/test_tokenize.py +168 -0
  316. {nltk-3.2 → nltk-3.3}/nltk/test/unit/test_twitter_auth.py +185 -185
  317. nltk-3.3/nltk/test/unit/test_wordnet.py +134 -0
  318. nltk-3.3/nltk/test/unit/translate/test_bleu.py +244 -0
  319. nltk-3.3/nltk/test/unit/translate/test_gdfa.py +57 -0
  320. {nltk-3.2 → nltk-3.3}/nltk/test/unit/translate/test_ibm1.py +75 -75
  321. {nltk-3.2 → nltk-3.3}/nltk/test/unit/translate/test_ibm2.py +87 -87
  322. {nltk-3.2 → nltk-3.3}/nltk/test/unit/translate/test_ibm3.py +105 -105
  323. {nltk-3.2 → nltk-3.3}/nltk/test/unit/translate/test_ibm4.py +126 -126
  324. {nltk-3.2 → nltk-3.3}/nltk/test/unit/translate/test_ibm5.py +166 -166
  325. {nltk-3.2 → nltk-3.3}/nltk/test/unit/translate/test_ibm_model.py +270 -270
  326. nltk-3.3/nltk/test/unit/translate/test_nist.py +37 -0
  327. {nltk-3.2 → nltk-3.3}/nltk/test/unit/translate/test_stack_decoder.py +300 -300
  328. {nltk-3.2 → nltk-3.3}/nltk/test/unit/utils.py +41 -41
  329. {nltk-3.2 → nltk-3.3}/nltk/test/util.doctest +48 -48
  330. {nltk-3.2 → nltk-3.3}/nltk/test/wordnet.doctest +595 -581
  331. {nltk-3.2 → nltk-3.3}/nltk/test/wordnet_fixt.py +7 -6
  332. {nltk-3.2 → nltk-3.3}/nltk/test/wordnet_lch.doctest +53 -53
  333. {nltk-3.2 → nltk-3.3}/nltk/test/wsd.doctest +68 -68
  334. {nltk-3.2 → nltk-3.3}/nltk/text.py +663 -610
  335. {nltk-3.2 → nltk-3.3}/nltk/tgrep.py +938 -937
  336. {nltk-3.2 → nltk-3.3}/nltk/tokenize/__init__.py +130 -107
  337. {nltk-3.2 → nltk-3.3}/nltk/tokenize/api.py +77 -78
  338. {nltk-3.2 → nltk-3.3}/nltk/tokenize/casual.py +343 -342
  339. {nltk-3.2 → nltk-3.3}/nltk/tokenize/mwe.py +120 -120
  340. nltk-3.3/nltk/tokenize/nist.py +173 -0
  341. {nltk-3.2 → nltk-3.3}/nltk/tokenize/punkt.py +1620 -1601
  342. {nltk-3.2 → nltk-3.3}/nltk/tokenize/regexp.py +208 -202
  343. nltk-3.3/nltk/tokenize/repp.py +151 -0
  344. {nltk-3.2 → nltk-3.3}/nltk/tokenize/sexpr.py +140 -142
  345. {nltk-3.2 → nltk-3.3}/nltk/tokenize/simple.py +138 -136
  346. {nltk-3.2 → nltk-3.3}/nltk/tokenize/stanford.py +117 -113
  347. nltk-3.3/nltk/tokenize/stanford_segmenter.py +246 -0
  348. {nltk-3.2 → nltk-3.3}/nltk/tokenize/texttiling.py +458 -460
  349. nltk-3.3/nltk/tokenize/toktok.py +155 -0
  350. nltk-3.3/nltk/tokenize/treebank.py +363 -0
  351. nltk-3.3/nltk/tokenize/util.py +261 -0
  352. {nltk-3.2 → nltk-3.3}/nltk/toolbox.py +499 -497
  353. {nltk-3.2 → nltk-3.3}/nltk/translate/__init__.py +24 -24
  354. {nltk-3.2 → nltk-3.3}/nltk/translate/api.py +331 -321
  355. {nltk-3.2 → nltk-3.3}/nltk/translate/bleu_score.py +615 -556
  356. nltk-3.3/nltk/translate/chrf_score.py +137 -0
  357. {nltk-3.2 → nltk-3.3}/nltk/translate/gale_church.py +248 -235
  358. {nltk-3.2 → nltk-3.3}/nltk/translate/gdfa.py +135 -131
  359. nltk-3.3/nltk/translate/gleu_score.py +193 -0
  360. {nltk-3.2 → nltk-3.3}/nltk/translate/ibm1.py +250 -241
  361. {nltk-3.2 → nltk-3.3}/nltk/translate/ibm2.py +308 -308
  362. {nltk-3.2 → nltk-3.3}/nltk/translate/ibm3.py +337 -337
  363. {nltk-3.2 → nltk-3.3}/nltk/translate/ibm4.py +467 -467
  364. {nltk-3.2 → nltk-3.3}/nltk/translate/ibm5.py +639 -639
  365. {nltk-3.2 → nltk-3.3}/nltk/translate/ibm_model.py +536 -533
  366. {nltk-3.2 → nltk-3.3}/nltk/translate/metrics.py +40 -40
  367. nltk-3.3/nltk/translate/nist_score.py +178 -0
  368. {nltk-3.2 → nltk-3.3}/nltk/translate/phrase_based.py +175 -175
  369. {nltk-3.2 → nltk-3.3}/nltk/translate/ribes_score.py +325 -325
  370. {nltk-3.2 → nltk-3.3}/nltk/translate/stack_decoder.py +499 -495
  371. {nltk-3.2 → nltk-3.3}/nltk/tree.py +1605 -1603
  372. {nltk-3.2 → nltk-3.3}/nltk/treeprettyprinter.py +564 -564
  373. {nltk-3.2 → nltk-3.3}/nltk/treetransforms.py +309 -309
  374. {nltk-3.2 → nltk-3.3}/nltk/twitter/__init__.py +28 -28
  375. {nltk-3.2 → nltk-3.3}/nltk/twitter/api.py +141 -136
  376. {nltk-3.2 → nltk-3.3}/nltk/twitter/common.py +264 -258
  377. {nltk-3.2 → nltk-3.3}/nltk/twitter/twitter_demo.py +288 -288
  378. {nltk-3.2 → nltk-3.3}/nltk/twitter/twitterclient.py +530 -530
  379. {nltk-3.2 → nltk-3.3}/nltk/twitter/util.py +147 -146
  380. nltk-3.3/nltk/util.py +751 -0
  381. {nltk-3.2 → nltk-3.3}/nltk/wsd.py +53 -53
  382. {nltk-3.2 → nltk-3.3/nltk.egg-info}/PKG-INFO +13 -5
  383. {nltk-3.2 → nltk-3.3}/nltk.egg-info/SOURCES.txt +26 -3
  384. nltk-3.3/nltk.egg-info/requires.txt +31 -0
  385. {nltk-3.2 → nltk-3.3}/setup.cfg +0 -1
  386. {nltk-3.2 → nltk-3.3}/setup.py +102 -75
  387. nltk-3.2/nltk/VERSION +0 -1
  388. nltk-3.2/nltk/compat.py +0 -680
  389. nltk-3.2/nltk/corpus/reader/pl196x.py +0 -278
  390. nltk-3.2/nltk/corpus/reader/verbnet.py +0 -408
  391. nltk-3.2/nltk/corpus/reader/wordlist.py +0 -37
  392. nltk-3.2/nltk/sentiment/vader_lexicon.txt +0 -7517
  393. nltk-3.2/nltk/six.py +0 -868
  394. nltk-3.2/nltk/stem/porter.py +0 -692
  395. nltk-3.2/nltk/test/unit/test_2x_compat.py +0 -26
  396. nltk-3.2/nltk/test/unit/test_stem.py +0 -36
  397. nltk-3.2/nltk/test/unit/test_tokenize.py +0 -23
  398. nltk-3.2/nltk/test/unit/translate/test_bleu.py +0 -84
  399. nltk-3.2/nltk/tokenize/treebank.py +0 -124
  400. nltk-3.2/nltk/tokenize/util.py +0 -90
  401. nltk-3.2/nltk/util.py +0 -1364
  402. {nltk-3.2 → nltk-3.3}/nltk/test/unit/__init__.py +0 -0
  403. {nltk-3.2 → nltk-3.3}/nltk/test/unit/translate/__init__.py +0 -0
  404. {nltk-3.2 → nltk-3.3}/nltk.egg-info/dependency_links.txt +0 -0
  405. {nltk-3.2 → nltk-3.3}/nltk.egg-info/not-zip-safe +0 -0
  406. {nltk-3.2 → nltk-3.3}/nltk.egg-info/top_level.txt +0 -0
@@ -1,1098 +1,1098 @@
1
- # Natural Language Toolkit: Table widget
2
- #
3
- # Copyright (C) 2001-2015 NLTK Project
4
- # Author: Edward Loper <edloper@gmail.com>
5
- # URL: <http://nltk.org/>
6
- # For license information, see LICENSE.TXT
7
-
8
- """
9
- Tkinter widgets for displaying multi-column listboxes and tables.
10
- """
11
-
12
- from __future__ import division
13
-
14
- import nltk.compat
15
- import operator
16
-
17
- from tkinter import (Frame, Label, Listbox, Scrollbar, Tk)
18
-
19
-
20
- ######################################################################
21
- # Multi-Column Listbox
22
- ######################################################################
23
-
24
- class MultiListbox(Frame):
25
- """
26
- A multi-column listbox, where the current selection applies to an
27
- entire row. Based on the MultiListbox Tkinter widget
28
- recipe from the Python Cookbook (http://code.activestate.com/recipes/52266/)
29
-
30
- For the most part, ``MultiListbox`` methods delegate to its
31
- contained listboxes. For any methods that do not have docstrings,
32
- see ``Tkinter.Listbox`` for a description of what that method does.
33
- """
34
- #/////////////////////////////////////////////////////////////////
35
- # Configuration
36
- #/////////////////////////////////////////////////////////////////
37
-
38
- #: Default configuration values for the frame.
39
- FRAME_CONFIG = dict(background='#888',
40
- takefocus=True,
41
- highlightthickness=1)
42
-
43
- #: Default configurations for the column labels.
44
- LABEL_CONFIG = dict(borderwidth=1, relief='raised',
45
- font='helvetica -16 bold',
46
- background='#444', foreground='white')
47
-
48
- #: Default configuration for the column listboxes.
49
- LISTBOX_CONFIG = dict(borderwidth=1,
50
- selectborderwidth=0,
51
- highlightthickness=0,
52
- exportselection=False,
53
- selectbackground='#888',
54
- activestyle='none',
55
- takefocus=False)
56
-
57
- #/////////////////////////////////////////////////////////////////
58
- # Constructor
59
- #/////////////////////////////////////////////////////////////////
60
-
61
- def __init__(self, master, columns, column_weights=None, cnf={}, **kw):
62
- """
63
- Construct a new multi-column listbox widget.
64
-
65
- :param master: The widget that should contain the new
66
- multi-column listbox.
67
-
68
- :param columns: Specifies what columns should be included in
69
- the new multi-column listbox. If ``columns`` is an integer,
70
- the it is the number of columns to include. If it is
71
- a list, then its length indicates the number of columns
72
- to include; and each element of the list will be used as
73
- a label for the corresponding column.
74
-
75
- :param cnf, kw: Configuration parameters for this widget.
76
- Use ``label_*`` to configure all labels; and ``listbox_*``
77
- to configure all listboxes. E.g.:
78
-
79
- >>> mlb = MultiListbox(master, 5, label_foreground='red')
80
- """
81
- # If columns was specified as an int, convert it to a list.
82
- if isinstance(columns, int):
83
- columns = list(range(columns))
84
- include_labels = False
85
- else:
86
- include_labels = True
87
-
88
- if len(columns) == 0:
89
- raise ValueError("Expected at least one column")
90
-
91
- # Instance variables
92
- self._column_names = tuple(columns)
93
- self._listboxes = []
94
- self._labels = []
95
-
96
- # Pick a default value for column_weights, if none was specified.
97
- if column_weights is None:
98
- column_weights = [1] * len(columns)
99
- elif len(column_weights) != len(columns):
100
- raise ValueError('Expected one column_weight for each column')
101
- self._column_weights = column_weights
102
-
103
- # Configure our widgets.
104
- Frame.__init__(self, master, **self.FRAME_CONFIG)
105
- self.grid_rowconfigure(1, weight=1)
106
- for i, label in enumerate(self._column_names):
107
- self.grid_columnconfigure(i, weight=column_weights[i])
108
-
109
- # Create a label for the column
110
- if include_labels:
111
- l = Label(self, text=label, **self.LABEL_CONFIG)
112
- self._labels.append(l)
113
- l.grid(column=i, row=0, sticky='news', padx=0, pady=0)
114
- l.column_index = i
115
-
116
- # Create a listbox for the column
117
- lb = Listbox(self, **self.LISTBOX_CONFIG)
118
- self._listboxes.append(lb)
119
- lb.grid(column=i, row=1, sticky='news', padx=0, pady=0)
120
- lb.column_index = i
121
-
122
- # Clicking or dragging selects:
123
- lb.bind('<Button-1>', self._select)
124
- lb.bind('<B1-Motion>', self._select)
125
- # Scroll whell scrolls:
126
- lb.bind('<Button-4>', lambda e: self._scroll(-1))
127
- lb.bind('<Button-5>', lambda e: self._scroll(+1))
128
- lb.bind('<MouseWheel>', lambda e: self._scroll(e.delta))
129
- # Button 2 can be used to scan:
130
- lb.bind('<Button-2>', lambda e: self.scan_mark(e.x, e.y))
131
- lb.bind('<B2-Motion>', lambda e: self.scan_dragto(e.x, e.y))
132
- # Dragging outside the window has no effect (diable
133
- # the default listbox behavior, which scrolls):
134
- lb.bind('<B1-Leave>', lambda e: 'break')
135
- # Columns can be resized by dragging them:
136
- l.bind('<Button-1>', self._resize_column)
137
-
138
- # Columns can be resized by dragging them. (This binding is
139
- # used if they click on the grid between columns:)
140
- self.bind('<Button-1>', self._resize_column)
141
-
142
- # Set up key bindings for the widget:
143
- self.bind('<Up>', lambda e: self.select(delta=-1))
144
- self.bind('<Down>', lambda e: self.select(delta=1))
145
- self.bind('<Prior>', lambda e: self.select(delta=-self._pagesize()))
146
- self.bind('<Next>', lambda e: self.select(delta=self._pagesize()))
147
-
148
- # Configuration customizations
149
- self.configure(cnf, **kw)
150
-
151
- #/////////////////////////////////////////////////////////////////
152
- # Column Resizing
153
- #/////////////////////////////////////////////////////////////////
154
-
155
- def _resize_column(self, event):
156
- """
157
- Callback used to resize a column of the table. Return ``True``
158
- if the column is actually getting resized (if the user clicked
159
- on the far left or far right 5 pixels of a label); and
160
- ``False`` otherwies.
161
- """
162
- # If we're already waiting for a button release, then ignore
163
- # the new button press.
164
- if event.widget.bind('<ButtonRelease>'):
165
- return False
166
-
167
- # Decide which column (if any) to resize.
168
- self._resize_column_index = None
169
- if event.widget is self:
170
- for i, lb in enumerate(self._listboxes):
171
- if abs(event.x-(lb.winfo_x()+lb.winfo_width())) < 10:
172
- self._resize_column_index = i
173
- elif event.x > (event.widget.winfo_width()-5):
174
- self._resize_column_index = event.widget.column_index
175
- elif event.x < 5 and event.widget.column_index != 0:
176
- self._resize_column_index = event.widget.column_index-1
177
-
178
- # Bind callbacks that are used to resize it.
179
- if self._resize_column_index is not None:
180
- event.widget.bind('<Motion>', self._resize_column_motion_cb)
181
- event.widget.bind('<ButtonRelease-%d>' % event.num,
182
- self._resize_column_buttonrelease_cb)
183
- return True
184
- else:
185
- return False
186
-
187
- def _resize_column_motion_cb(self, event):
188
- lb = self._listboxes[self._resize_column_index]
189
- charwidth = lb.winfo_width() / lb['width']
190
-
191
- x1 = event.x + event.widget.winfo_x()
192
- x2 = lb.winfo_x() + lb.winfo_width()
193
-
194
- lb['width'] = max(3, lb['width'] + (x1-x2) // charwidth)
195
-
196
- def _resize_column_buttonrelease_cb(self, event):
197
- event.widget.unbind('<ButtonRelease-%d>' % event.num)
198
- event.widget.unbind('<Motion>')
199
-
200
- #/////////////////////////////////////////////////////////////////
201
- # Properties
202
- #/////////////////////////////////////////////////////////////////
203
-
204
- @property
205
- def column_names(self):
206
- """
207
- A tuple containing the names of the columns used by this
208
- multi-column listbox.
209
- """
210
- return self._column_names
211
-
212
- @property
213
- def column_labels(self):
214
- """
215
- A tuple containing the ``Tkinter.Label`` widgets used to
216
- display the label of each column. If this multi-column
217
- listbox was created without labels, then this will be an empty
218
- tuple. These widgets will all be augmented with a
219
- ``column_index`` attribute, which can be used to determine
220
- which column they correspond to. This can be convenient,
221
- e.g., when defining callbacks for bound events.
222
- """
223
- return tuple(self._labels)
224
-
225
- @property
226
- def listboxes(self):
227
- """
228
- A tuple containing the ``Tkinter.Listbox`` widgets used to
229
- display individual columns. These widgets will all be
230
- augmented with a ``column_index`` attribute, which can be used
231
- to determine which column they correspond to. This can be
232
- convenient, e.g., when defining callbacks for bound events.
233
- """
234
- return tuple(self._listboxes)
235
-
236
- #/////////////////////////////////////////////////////////////////
237
- # Mouse & Keyboard Callback Functions
238
- #/////////////////////////////////////////////////////////////////
239
-
240
- def _select(self, e):
241
- i = e.widget.nearest(e.y)
242
- self.selection_clear(0, 'end')
243
- self.selection_set(i)
244
- self.activate(i)
245
- self.focus()
246
-
247
- def _scroll(self, delta):
248
- for lb in self._listboxes:
249
- lb.yview_scroll(delta, 'unit')
250
- return 'break'
251
-
252
- def _pagesize(self):
253
- """:return: The number of rows that makes up one page"""
254
- return int(self.index('@0,1000000')) - int(self.index('@0,0'))
255
-
256
- #/////////////////////////////////////////////////////////////////
257
- # Row selection
258
- #/////////////////////////////////////////////////////////////////
259
-
260
- def select(self, index=None, delta=None, see=True):
261
- """
262
- Set the selected row. If ``index`` is specified, then select
263
- row ``index``. Otherwise, if ``delta`` is specified, then move
264
- the current selection by ``delta`` (negative numbers for up,
265
- positive numbers for down). This will not move the selection
266
- past the top or the bottom of the list.
267
-
268
- :param see: If true, then call ``self.see()`` with the newly
269
- selected index, to ensure that it is visible.
270
- """
271
- if (index is not None) and (delta is not None):
272
- raise ValueError('specify index or delta, but not both')
273
-
274
- # If delta was given, then calculate index.
275
- if delta is not None:
276
- if len(self.curselection()) == 0:
277
- index = -1 + delta
278
- else:
279
- index = int(self.curselection()[0]) + delta
280
-
281
- # Clear all selected rows.
282
- self.selection_clear(0, 'end')
283
-
284
- # Select the specified index
285
- if index is not None:
286
- index = min(max(index, 0), self.size()-1)
287
- #self.activate(index)
288
- self.selection_set(index)
289
- if see: self.see(index)
290
-
291
- #/////////////////////////////////////////////////////////////////
292
- # Configuration
293
- #/////////////////////////////////////////////////////////////////
294
-
295
- def configure(self, cnf={}, **kw):
296
- """
297
- Configure this widget. Use ``label_*`` to configure all
298
- labels; and ``listbox_*`` to configure all listboxes. E.g.:
299
-
300
- >>> mlb = MultiListbox(master, 5)
301
- >>> mlb.configure(label_foreground='red')
302
- >>> mlb.configure(listbox_foreground='red')
303
- """
304
- cnf = dict(list(cnf.items()) + list(kw.items()))
305
- for (key, val) in list(cnf.items()):
306
- if key.startswith('label_') or key.startswith('label-'):
307
- for label in self._labels:
308
- label.configure({key[6:]: val})
309
- elif key.startswith('listbox_') or key.startswith('listbox-'):
310
- for listbox in self._listboxes:
311
- listbox.configure({key[8:]: val})
312
- else:
313
- Frame.configure(self, {key:val})
314
-
315
- def __setitem__(self, key, val):
316
- """
317
- Configure this widget. This is equivalent to
318
- ``self.configure({key,val``)}. See ``configure()``.
319
- """
320
- self.configure({key:val})
321
-
322
- def rowconfigure(self, row_index, cnf={}, **kw):
323
- """
324
- Configure all table cells in the given row. Valid keyword
325
- arguments are: ``background``, ``bg``, ``foreground``, ``fg``,
326
- ``selectbackground``, ``selectforeground``.
327
- """
328
- for lb in self._listboxes: lb.itemconfigure(row_index, cnf, **kw)
329
-
330
- def columnconfigure(self, col_index, cnf={}, **kw):
331
- """
332
- Configure all table cells in the given column. Valid keyword
333
- arguments are: ``background``, ``bg``, ``foreground``, ``fg``,
334
- ``selectbackground``, ``selectforeground``.
335
- """
336
- lb = self._listboxes[col_index]
337
-
338
- cnf = dict(list(cnf.items()) + list(kw.items()))
339
- for (key, val) in list(cnf.items()):
340
- if key in ('background', 'bg', 'foreground', 'fg',
341
- 'selectbackground', 'selectforeground'):
342
- for i in range(lb.size()): lb.itemconfigure(i, {key:val})
343
- else:
344
- lb.configure({key:val})
345
-
346
- def itemconfigure(self, row_index, col_index, cnf=None, **kw):
347
- """
348
- Configure the table cell at the given row and column. Valid
349
- keyword arguments are: ``background``, ``bg``, ``foreground``,
350
- ``fg``, ``selectbackground``, ``selectforeground``.
351
- """
352
- lb = self._listboxes[col_index]
353
- return lb.itemconfigure(row_index, cnf, **kw)
354
-
355
- #/////////////////////////////////////////////////////////////////
356
- # Value Access
357
- #/////////////////////////////////////////////////////////////////
358
-
359
- def insert(self, index, *rows):
360
- """
361
- Insert the given row or rows into the table, at the given
362
- index. Each row value should be a tuple of cell values, one
363
- for each column in the row. Index may be an integer or any of
364
- the special strings (such as ``'end'``) accepted by
365
- ``Tkinter.Listbox``.
366
- """
367
- for elt in rows:
368
- if len(elt) != len(self._column_names):
369
- raise ValueError('rows should be tuples whose length '
370
- 'is equal to the number of columns')
371
- for (lb,elts) in zip(self._listboxes, list(zip(*rows))):
372
- lb.insert(index, *elts)
373
-
374
- def get(self, first, last=None):
375
- """
376
- Return the value(s) of the specified row(s). If ``last`` is
377
- not specified, then return a single row value; otherwise,
378
- return a list of row values. Each row value is a tuple of
379
- cell values, one for each column in the row.
380
- """
381
- values = [lb.get(first, last) for lb in self._listboxes]
382
- if last:
383
- return [tuple(row) for row in zip(*values)]
384
- else:
385
- return tuple(values)
386
-
387
- def bbox(self, row, col):
388
- """
389
- Return the bounding box for the given table cell, relative to
390
- this widget's top-left corner. The bounding box is a tuple
391
- of integers ``(left, top, width, height)``.
392
- """
393
- dx, dy, _, _ = self.grid_bbox(row=0, column=col)
394
- x, y, w, h = self._listboxes[col].bbox(row)
395
- return int(x)+int(dx), int(y)+int(dy), int(w), int(h)
396
-
397
- #/////////////////////////////////////////////////////////////////
398
- # Hide/Show Columns
399
- #/////////////////////////////////////////////////////////////////
400
-
401
- def hide_column(self, col_index):
402
- """
403
- Hide the given column. The column's state is still
404
- maintained: its values will still be returned by ``get()``, and
405
- you must supply its values when calling ``insert()``. It is
406
- safe to call this on a column that is already hidden.
407
-
408
- :see: ``show_column()``
409
- """
410
- if self._labels:
411
- self._labels[col_index].grid_forget()
412
- self.listboxes[col_index].grid_forget()
413
- self.grid_columnconfigure(col_index, weight=0)
414
-
415
- def show_column(self, col_index):
416
- """
417
- Display a column that has been hidden using ``hide_column()``.
418
- It is safe to call this on a column that is not hidden.
419
- """
420
- weight = self._column_weights[col_index]
421
- if self._labels:
422
- self._labels[col_index].grid(column=col_index, row=0,
423
- sticky='news', padx=0, pady=0)
424
- self._listboxes[col_index].grid(column=col_index, row=1,
425
- sticky='news', padx=0, pady=0)
426
- self.grid_columnconfigure(col_index, weight=weight)
427
-
428
- #/////////////////////////////////////////////////////////////////
429
- # Binding Methods
430
- #/////////////////////////////////////////////////////////////////
431
-
432
- def bind_to_labels(self, sequence=None, func=None, add=None):
433
- """
434
- Add a binding to each ``Tkinter.Label`` widget in this
435
- mult-column listbox that will call ``func`` in response to the
436
- event sequence.
437
-
438
- :return: A list of the identifiers of replaced binding
439
- functions (if any), allowing for their deletion (to
440
- prevent a memory leak).
441
- """
442
- return [label.bind(sequence, func, add)
443
- for label in self.column_labels]
444
-
445
- def bind_to_listboxes(self, sequence=None, func=None, add=None):
446
- """
447
- Add a binding to each ``Tkinter.Listbox`` widget in this
448
- mult-column listbox that will call ``func`` in response to the
449
- event sequence.
450
-
451
- :return: A list of the identifiers of replaced binding
452
- functions (if any), allowing for their deletion (to
453
- prevent a memory leak).
454
- """
455
- for listbox in self.listboxes:
456
- listbox.bind(sequence, func, add)
457
-
458
- def bind_to_columns(self, sequence=None, func=None, add=None):
459
- """
460
- Add a binding to each ``Tkinter.Label`` and ``Tkinter.Listbox``
461
- widget in this mult-column listbox that will call ``func`` in
462
- response to the event sequence.
463
-
464
- :return: A list of the identifiers of replaced binding
465
- functions (if any), allowing for their deletion (to
466
- prevent a memory leak).
467
- """
468
- return (self.bind_to_labels(sequence, func, add) +
469
- self.bind_to_listboxes(sequence, func, add))
470
-
471
- #/////////////////////////////////////////////////////////////////
472
- # Simple Delegation
473
- #/////////////////////////////////////////////////////////////////
474
-
475
- # These methods delegate to the first listbox:
476
- def curselection(self, *args, **kwargs):
477
- return self._listboxes[0].curselection(*args, **kwargs)
478
- def selection_includes(self, *args, **kwargs):
479
- return self._listboxes[0].selection_includes(*args, **kwargs)
480
- def itemcget(self, *args, **kwargs):
481
- return self._listboxes[0].itemcget(*args, **kwargs)
482
- def size(self, *args, **kwargs):
483
- return self._listboxes[0].size(*args, **kwargs)
484
- def index(self, *args, **kwargs):
485
- return self._listboxes[0].index(*args, **kwargs)
486
- def nearest(self, *args, **kwargs):
487
- return self._listboxes[0].nearest(*args, **kwargs)
488
-
489
- # These methods delegate to each listbox (and return None):
490
- def activate(self, *args, **kwargs):
491
- for lb in self._listboxes: lb.activate(*args, **kwargs)
492
- def delete(self, *args, **kwargs):
493
- for lb in self._listboxes: lb.delete(*args, **kwargs)
494
- def scan_mark(self, *args, **kwargs):
495
- for lb in self._listboxes: lb.scan_mark(*args, **kwargs)
496
- def scan_dragto(self, *args, **kwargs):
497
- for lb in self._listboxes: lb.scan_dragto(*args, **kwargs)
498
- def see(self, *args, **kwargs):
499
- for lb in self._listboxes: lb.see(*args, **kwargs)
500
- def selection_anchor(self, *args, **kwargs):
501
- for lb in self._listboxes: lb.selection_anchor(*args, **kwargs)
502
- def selection_clear(self, *args, **kwargs):
503
- for lb in self._listboxes: lb.selection_clear(*args, **kwargs)
504
- def selection_set(self, *args, **kwargs):
505
- for lb in self._listboxes: lb.selection_set(*args, **kwargs)
506
- def yview(self, *args, **kwargs):
507
- for lb in self._listboxes: v = lb.yview(*args, **kwargs)
508
- return v # if called with no arguments
509
- def yview_moveto(self, *args, **kwargs):
510
- for lb in self._listboxes: lb.yview_moveto(*args, **kwargs)
511
- def yview_scroll(self, *args, **kwargs):
512
- for lb in self._listboxes: lb.yview_scroll(*args, **kwargs)
513
-
514
- #/////////////////////////////////////////////////////////////////
515
- # Aliases
516
- #/////////////////////////////////////////////////////////////////
517
-
518
- itemconfig = itemconfigure
519
- rowconfig = rowconfigure
520
- columnconfig = columnconfigure
521
- select_anchor = selection_anchor
522
- select_clear = selection_clear
523
- select_includes = selection_includes
524
- select_set = selection_set
525
-
526
- #/////////////////////////////////////////////////////////////////
527
- # These listbox methods are not defined for multi-listbox
528
- #/////////////////////////////////////////////////////////////////
529
- # def xview(self, *what): pass
530
- # def xview_moveto(self, fraction): pass
531
- # def xview_scroll(self, number, what): pass
532
-
533
- ######################################################################
534
- # Table
535
- ######################################################################
536
-
537
- class Table(object):
538
- """
539
- A display widget for a table of values, based on a ``MultiListbox``
540
- widget. For many purposes, ``Table`` can be treated as a
541
- list-of-lists. E.g., table[i] is a list of the values for row i;
542
- and table.append(row) adds a new row with the given lits of
543
- values. Individual cells can be accessed using table[i,j], which
544
- refers to the j-th column of the i-th row. This can be used to
545
- both read and write values from the table. E.g.:
546
-
547
- >>> table[i,j] = 'hello'
548
-
549
- The column (j) can be given either as an index number, or as a
550
- column name. E.g., the following prints the value in the 3rd row
551
- for the 'First Name' column:
552
-
553
- >>> print(table[3, 'First Name'])
554
- John
555
-
556
- You can configure the colors for individual rows, columns, or
557
- cells using ``rowconfig()``, ``columnconfig()``, and ``itemconfig()``.
558
- The color configuration for each row will be preserved if the
559
- table is modified; however, when new rows are added, any color
560
- configurations that have been made for *columns* will not be
561
- applied to the new row.
562
-
563
- Note: Although ``Table`` acts like a widget in some ways (e.g., it
564
- defines ``grid()``, ``pack()``, and ``bind()``), it is not itself a
565
- widget; it just contains one. This is because widgets need to
566
- define ``__getitem__()``, ``__setitem__()``, and ``__nonzero__()`` in
567
- a way that's incompatible with the fact that ``Table`` behaves as a
568
- list-of-lists.
569
-
570
- :ivar _mlb: The multi-column listbox used to display this table's data.
571
- :ivar _rows: A list-of-lists used to hold the cell values of this
572
- table. Each element of _rows is a row value, i.e., a list of
573
- cell values, one for each column in the row.
574
- """
575
- def __init__(self, master, column_names, rows=None,
576
- column_weights=None,
577
- scrollbar=True, click_to_sort=True,
578
- reprfunc=None, cnf={}, **kw):
579
- """
580
- Construct a new Table widget.
581
-
582
- :type master: Tkinter.Widget
583
- :param master: The widget that should contain the new table.
584
- :type column_names: list(str)
585
- :param column_names: A list of names for the columns; these
586
- names will be used to create labels for each column;
587
- and can be used as an index when reading or writing
588
- cell values from the table.
589
- :type rows: list(list)
590
- :param rows: A list of row values used to initialze the table.
591
- Each row value should be a tuple of cell values, one for
592
- each column in the row.
593
- :type scrollbar: bool
594
- :param scrollbar: If true, then create a scrollbar for the
595
- new table widget.
596
- :type click_to_sort: bool
597
- :param click_to_sort: If true, then create bindings that will
598
- sort the table's rows by a given column's values if the
599
- user clicks on that colum's label.
600
- :type reprfunc: function
601
- :param reprfunc: If specified, then use this function to
602
- convert each table cell value to a string suitable for
603
- display. ``reprfunc`` has the following signature:
604
- reprfunc(row_index, col_index, cell_value) -> str
605
- (Note that the column is specified by index, not by name.)
606
- :param cnf, kw: Configuration parameters for this widget's
607
- contained ``MultiListbox``. See ``MultiListbox.__init__()``
608
- for details.
609
- """
610
- self._num_columns = len(column_names)
611
- self._reprfunc = reprfunc
612
- self._frame = Frame(master)
613
-
614
- self._column_name_to_index = dict((c,i) for (i,c) in
615
- enumerate(column_names))
616
-
617
- # Make a copy of the rows & check that it's valid.
618
- if rows is None: self._rows = []
619
- else: self._rows = [[v for v in row] for row in rows]
620
- for row in self._rows: self._checkrow(row)
621
-
622
- # Create our multi-list box.
623
- self._mlb = MultiListbox(self._frame, column_names,
624
- column_weights, cnf, **kw)
625
- self._mlb.pack(side='left', expand=True, fill='both')
626
-
627
- # Optional scrollbar
628
- if scrollbar:
629
- sb = Scrollbar(self._frame, orient='vertical',
630
- command=self._mlb.yview)
631
- self._mlb.listboxes[0]['yscrollcommand'] = sb.set
632
- #for listbox in self._mlb.listboxes:
633
- # listbox['yscrollcommand'] = sb.set
634
- sb.pack(side='right', fill='y')
635
- self._scrollbar = sb
636
-
637
- # Set up sorting
638
- self._sortkey = None
639
- if click_to_sort:
640
- for i, l in enumerate(self._mlb.column_labels):
641
- l.bind('<Button-1>', self._sort)
642
-
643
- # Fill in our multi-list box.
644
- self._fill_table()
645
-
646
- #/////////////////////////////////////////////////////////////////
647
- #{ Widget-like Methods
648
- #/////////////////////////////////////////////////////////////////
649
- # These all just delegate to either our frame or our MLB.
650
-
651
- def pack(self, *args, **kwargs):
652
- """Position this table's main frame widget in its parent
653
- widget. See ``Tkinter.Frame.pack()`` for more info."""
654
- self._frame.pack(*args, **kwargs)
655
-
656
- def grid(self, *args, **kwargs):
657
- """Position this table's main frame widget in its parent
658
- widget. See ``Tkinter.Frame.grid()`` for more info."""
659
- self._frame.grid(*args, **kwargs)
660
-
661
- def focus(self):
662
- """Direct (keyboard) input foxus to this widget."""
663
- self._mlb.focus()
664
-
665
- def bind(self, sequence=None, func=None, add=None):
666
- """Add a binding to this table's main frame that will call
667
- ``func`` in response to the event sequence."""
668
- self._mlb.bind(sequence, func, add)
669
-
670
- def rowconfigure(self, row_index, cnf={}, **kw):
671
- """:see: ``MultiListbox.rowconfigure()``"""
672
- self._mlb.rowconfigure(row_index, cnf, **kw)
673
-
674
- def columnconfigure(self, col_index, cnf={}, **kw):
675
- """:see: ``MultiListbox.columnconfigure()``"""
676
- col_index = self.column_index(col_index)
677
- self._mlb.columnconfigure(col_index, cnf, **kw)
678
-
679
- def itemconfigure(self, row_index, col_index, cnf=None, **kw):
680
- """:see: ``MultiListbox.itemconfigure()``"""
681
- col_index = self.column_index(col_index)
682
- return self._mlb.itemconfigure(row_index, col_index, cnf, **kw)
683
-
684
- def bind_to_labels(self, sequence=None, func=None, add=None):
685
- """:see: ``MultiListbox.bind_to_labels()``"""
686
- return self._mlb.bind_to_labels(sequence, func, add)
687
-
688
- def bind_to_listboxes(self, sequence=None, func=None, add=None):
689
- """:see: ``MultiListbox.bind_to_listboxes()``"""
690
- return self._mlb.bind_to_listboxes(sequence, func, add)
691
-
692
- def bind_to_columns(self, sequence=None, func=None, add=None):
693
- """:see: ``MultiListbox.bind_to_columns()``"""
694
- return self._mlb.bind_to_columns(sequence, func, add)
695
-
696
- rowconfig = rowconfigure
697
- columnconfig = columnconfigure
698
- itemconfig = itemconfigure
699
-
700
- #/////////////////////////////////////////////////////////////////
701
- #{ Table as list-of-lists
702
- #/////////////////////////////////////////////////////////////////
703
-
704
- def insert(self, row_index, rowvalue):
705
- """
706
- Insert a new row into the table, so that its row index will be
707
- ``row_index``. If the table contains any rows whose row index
708
- is greater than or equal to ``row_index``, then they will be
709
- shifted down.
710
-
711
- :param rowvalue: A tuple of cell values, one for each column
712
- in the new row.
713
- """
714
- self._checkrow(rowvalue)
715
- self._rows.insert(row_index, rowvalue)
716
- if self._reprfunc is not None:
717
- rowvalue = [self._reprfunc(row_index,j,v)
718
- for (j,v) in enumerate(rowvalue)]
719
- self._mlb.insert(row_index, rowvalue)
720
- if self._DEBUG: self._check_table_vs_mlb()
721
-
722
- def extend(self, rowvalues):
723
- """
724
- Add new rows at the end of the table.
725
-
726
- :param rowvalues: A list of row values used to initialze the
727
- table. Each row value should be a tuple of cell values,
728
- one for each column in the row.
729
- """
730
- for rowvalue in rowvalues: self.append(rowvalue)
731
- if self._DEBUG: self._check_table_vs_mlb()
732
-
733
- def append(self, rowvalue):
734
- """
735
- Add a new row to the end of the table.
736
-
737
- :param rowvalue: A tuple of cell values, one for each column
738
- in the new row.
739
- """
740
- self.insert(len(self._rows), rowvalue)
741
- if self._DEBUG: self._check_table_vs_mlb()
742
-
743
- def clear(self):
744
- """
745
- Delete all rows in this table.
746
- """
747
- self._rows = []
748
- self._mlb.delete(0, 'end')
749
- if self._DEBUG: self._check_table_vs_mlb()
750
-
751
- def __getitem__(self, index):
752
- """
753
- Return the value of a row or a cell in this table. If
754
- ``index`` is an integer, then the row value for the ``index``th
755
- row. This row value consists of a tuple of cell values, one
756
- for each column in the row. If ``index`` is a tuple of two
757
- integers, ``(i,j)``, then return the value of the cell in the
758
- ``i``th row and the ``j``th column.
759
- """
760
- if isinstance(index, slice):
761
- raise ValueError('Slicing not supported')
762
- elif isinstance(index, tuple) and len(index)==2:
763
- return self._rows[index[0]][self.column_index(index[1])]
764
- else:
765
- return tuple(self._rows[index])
766
-
767
- def __setitem__(self, index, val):
768
- """
769
- Replace the value of a row or a cell in this table with
770
- ``val``.
771
-
772
- If ``index`` is an integer, then ``val`` should be a row value
773
- (i.e., a tuple of cell values, one for each column). In this
774
- case, the values of the ``index``th row of the table will be
775
- replaced with the values in ``val``.
776
-
777
- If ``index`` is a tuple of integers, ``(i,j)``, then replace the
778
- value of the cell in the ``i``th row and ``j``th column with
779
- ``val``.
780
- """
781
- if isinstance(index, slice):
782
- raise ValueError('Slicing not supported')
783
-
784
-
785
- # table[i,j] = val
786
- elif isinstance(index, tuple) and len(index)==2:
787
- i, j = index[0], self.column_index(index[1])
788
- config_cookie = self._save_config_info([i])
789
- self._rows[i][j] = val
790
- if self._reprfunc is not None:
791
- val = self._reprfunc(i, j, val)
792
- self._mlb.listboxes[j].insert(i, val)
793
- self._mlb.listboxes[j].delete(i+1)
794
- self._restore_config_info(config_cookie)
795
-
796
- # table[i] = val
797
- else:
798
- config_cookie = self._save_config_info([index])
799
- self._checkrow(val)
800
- self._rows[index] = list(val)
801
- if self._reprfunc is not None:
802
- val = [self._reprfunc(index,j,v) for (j,v) in enumerate(val)]
803
- self._mlb.insert(index, val)
804
- self._mlb.delete(index+1)
805
- self._restore_config_info(config_cookie)
806
-
807
- def __delitem__(self, row_index):
808
- """
809
- Delete the ``row_index``th row from this table.
810
- """
811
- if isinstance(index, slice):
812
- raise ValueError('Slicing not supported')
813
- if isinstance(row_index, tuple) and len(row_index)==2:
814
- raise ValueError('Cannot delete a single cell!')
815
- del self._rows[row_index]
816
- self._mlb.delete(row_index)
817
- if self._DEBUG: self._check_table_vs_mlb()
818
-
819
- def __len__(self):
820
- """
821
- :return: the number of rows in this table.
822
- """
823
- return len(self._rows)
824
-
825
- def _checkrow(self, rowvalue):
826
- """
827
- Helper function: check that a given row value has the correct
828
- number of elements; and if not, raise an exception.
829
- """
830
- if len(rowvalue) != self._num_columns:
831
- raise ValueError('Row %r has %d columns; expected %d' %
832
- (rowvalue, len(rowvalue), self._num_columns))
833
-
834
- #/////////////////////////////////////////////////////////////////
835
- # Columns
836
- #/////////////////////////////////////////////////////////////////
837
-
838
- @property
839
- def column_names(self):
840
- """A list of the names of the columns in this table."""
841
- return self._mlb.column_names
842
-
843
- def column_index(self, i):
844
- """
845
- If ``i`` is a valid column index integer, then return it as is.
846
- Otherwise, check if ``i`` is used as the name for any column;
847
- if so, return that column's index. Otherwise, raise a
848
- ``KeyError`` exception.
849
- """
850
- if isinstance(i, int) and 0 <= i < self._num_columns:
851
- return i
852
- else:
853
- # This raises a key error if the column is not found.
854
- return self._column_name_to_index[i]
855
-
856
- def hide_column(self, column_index):
857
- """:see: ``MultiListbox.hide_column()``"""
858
- self._mlb.hide_column(self.column_index(column_index))
859
-
860
- def show_column(self, column_index):
861
- """:see: ``MultiListbox.show_column()``"""
862
- self._mlb.show_column(self.column_index(column_index))
863
-
864
- #/////////////////////////////////////////////////////////////////
865
- # Selection
866
- #/////////////////////////////////////////////////////////////////
867
-
868
- def selected_row(self):
869
- """
870
- Return the index of the currently selected row, or None if
871
- no row is selected. To get the row value itself, use
872
- ``table[table.selected_row()]``.
873
- """
874
- sel = self._mlb.curselection()
875
- if sel: return int(sel[0])
876
- else: return None
877
-
878
- def select(self, index=None, delta=None, see=True):
879
- """:see: ``MultiListbox.select()``"""
880
- self._mlb.select(index, delta, see)
881
-
882
- #/////////////////////////////////////////////////////////////////
883
- # Sorting
884
- #/////////////////////////////////////////////////////////////////
885
-
886
- def sort_by(self, column_index, order='toggle'):
887
- """
888
- Sort the rows in this table, using the specified column's
889
- values as a sort key.
890
-
891
- :param column_index: Specifies which column to sort, using
892
- either a column index (int) or a column's label name
893
- (str).
894
-
895
- :param order: Specifies whether to sort the values in
896
- ascending or descending order:
897
-
898
- - ``'ascending'``: Sort from least to greatest.
899
- - ``'descending'``: Sort from greatest to least.
900
- - ``'toggle'``: If the most recent call to ``sort_by()``
901
- sorted the table by the same column (``column_index``),
902
- then reverse the rows; otherwise sort in ascending
903
- order.
904
- """
905
- if order not in ('ascending', 'descending', 'toggle'):
906
- raise ValueError('sort_by(): order should be "ascending", '
907
- '"descending", or "toggle".')
908
- column_index = self.column_index(column_index)
909
- config_cookie = self._save_config_info(index_by_id=True)
910
-
911
- # Sort the rows.
912
- if order == 'toggle' and column_index == self._sortkey:
913
- self._rows.reverse()
914
- else:
915
- self._rows.sort(key=operator.itemgetter(column_index),
916
- reverse=(order=='descending'))
917
- self._sortkey = column_index
918
-
919
- # Redraw the table.
920
- self._fill_table()
921
- self._restore_config_info(config_cookie, index_by_id=True, see=True)
922
- if self._DEBUG: self._check_table_vs_mlb()
923
-
924
- def _sort(self, event):
925
- """Event handler for clicking on a column label -- sort by
926
- that column."""
927
- column_index = event.widget.column_index
928
-
929
- # If they click on the far-left of far-right of a column's
930
- # label, then resize rather than sorting.
931
- if self._mlb._resize_column(event):
932
- return 'continue'
933
-
934
- # Otherwise, sort.
935
- else:
936
- self.sort_by(column_index)
937
- return 'continue'
938
-
939
- #/////////////////////////////////////////////////////////////////
940
- #{ Table Drawing Helpers
941
- #/////////////////////////////////////////////////////////////////
942
-
943
- def _fill_table(self, save_config=True):
944
- """
945
- Re-draw the table from scratch, by clearing out the table's
946
- multi-column listbox; and then filling it in with values from
947
- ``self._rows``. Note that any cell-, row-, or column-specific
948
- color configuration that has been done will be lost. The
949
- selection will also be lost -- i.e., no row will be selected
950
- after this call completes.
951
- """
952
- self._mlb.delete(0, 'end')
953
- for i, row in enumerate(self._rows):
954
- if self._reprfunc is not None:
955
- row = [self._reprfunc(i,j,v) for (j,v) in enumerate(row)]
956
- self._mlb.insert('end', row)
957
-
958
- def _get_itemconfig(self, r, c):
959
- return dict( (k, self._mlb.itemconfig(r, c, k)[-1])
960
- for k in ('foreground', 'selectforeground',
961
- 'background', 'selectbackground') )
962
-
963
- def _save_config_info(self, row_indices=None, index_by_id=False):
964
- """
965
- Return a 'cookie' containing information about which row is
966
- selected, and what color configurations have been applied.
967
- this information can the be re-applied to the table (after
968
- making modifications) using ``_restore_config_info()``. Color
969
- configuration information will be saved for any rows in
970
- ``row_indices``, or in the entire table, if
971
- ``row_indices=None``. If ``index_by_id=True``, the the cookie
972
- will associate rows with their configuration information based
973
- on the rows' python id. This is useful when performing
974
- operations that re-arrange the rows (e.g. ``sort``). If
975
- ``index_by_id=False``, then it is assumed that all rows will be
976
- in the same order when ``_restore_config_info()`` is called.
977
- """
978
- # Default value for row_indices is all rows.
979
- if row_indices is None:
980
- row_indices = list(range(len(self._rows)))
981
-
982
- # Look up our current selection.
983
- selection = self.selected_row()
984
- if index_by_id and selection is not None:
985
- selection = id(self._rows[selection])
986
-
987
- # Look up the color configuration info for each row.
988
- if index_by_id:
989
- config = dict((id(self._rows[r]), [self._get_itemconfig(r, c)
990
- for c in range(self._num_columns)])
991
- for r in row_indices)
992
- else:
993
- config = dict((r, [self._get_itemconfig(r, c)
994
- for c in range(self._num_columns)])
995
- for r in row_indices)
996
-
997
-
998
- return selection, config
999
-
1000
- def _restore_config_info(self, cookie, index_by_id=False, see=False):
1001
- """
1002
- Restore selection & color configuration information that was
1003
- saved using ``_save_config_info``.
1004
- """
1005
- selection, config = cookie
1006
-
1007
- # Clear the selection.
1008
- if selection is None:
1009
- self._mlb.selection_clear(0, 'end')
1010
-
1011
- # Restore selection & color config
1012
- if index_by_id:
1013
- for r, row in enumerate(self._rows):
1014
- if id(row) in config:
1015
- for c in range(self._num_columns):
1016
- self._mlb.itemconfigure(r, c, config[id(row)][c])
1017
- if id(row) == selection:
1018
- self._mlb.select(r, see=see)
1019
- else:
1020
- if selection is not None:
1021
- self._mlb.select(selection, see=see)
1022
- for r in config:
1023
- for c in range(self._num_columns):
1024
- self._mlb.itemconfigure(r, c, config[r][c])
1025
-
1026
- #/////////////////////////////////////////////////////////////////
1027
- # Debugging (Invariant Checker)
1028
- #/////////////////////////////////////////////////////////////////
1029
-
1030
- _DEBUG = False
1031
- """If true, then run ``_check_table_vs_mlb()`` after any operation
1032
- that modifies the table."""
1033
-
1034
- def _check_table_vs_mlb(self):
1035
- """
1036
- Verify that the contents of the table's ``_rows`` variable match
1037
- the contents of its multi-listbox (``_mlb``). This is just
1038
- included for debugging purposes, to make sure that the
1039
- list-modifying operations are working correctly.
1040
- """
1041
- for col in self._mlb.listboxes:
1042
- assert len(self) == col.size()
1043
- for row in self:
1044
- assert len(row) == self._num_columns
1045
- assert self._num_columns == len(self._mlb.column_names)
1046
- #assert self._column_names == self._mlb.column_names
1047
- for i, row in enumerate(self):
1048
- for j, cell in enumerate(row):
1049
- if self._reprfunc is not None:
1050
- cell = self._reprfunc(i, j, cell)
1051
- assert self._mlb.get(i)[j] == cell
1052
-
1053
- ######################################################################
1054
- # Demo/Test Function
1055
- ######################################################################
1056
-
1057
- # update this to use new WordNet API
1058
- def demo():
1059
- root = Tk()
1060
- root.bind('<Control-q>', lambda e: root.destroy())
1061
-
1062
- table = Table(root, 'Word Synset Hypernym Hyponym'.split(),
1063
- column_weights=[0, 1, 1, 1],
1064
- reprfunc=(lambda i,j,s: ' %s' % s))
1065
- table.pack(expand=True, fill='both')
1066
-
1067
- from nltk.corpus import wordnet
1068
- from nltk.corpus import brown
1069
- for word, pos in sorted(set(brown.tagged_words()[:500])):
1070
- if pos[0] != 'N': continue
1071
- word = word.lower()
1072
- for synset in wordnet.synsets(word):
1073
- try:
1074
- hyper_def = synset.hypernyms()[0].definition()
1075
- except:
1076
- hyper_def = '*none*'
1077
- try:
1078
- hypo_def = synset.hypernyms()[0].definition()
1079
- except:
1080
- hypo_def = '*none*'
1081
- table.append([word,
1082
- synset.definition(),
1083
- hyper_def,
1084
- hypo_def])
1085
-
1086
- table.columnconfig('Word', background='#afa')
1087
- table.columnconfig('Synset', background='#efe')
1088
- table.columnconfig('Hypernym', background='#fee')
1089
- table.columnconfig('Hyponym', background='#ffe')
1090
- for row in range(len(table)):
1091
- for column in ('Hypernym', 'Hyponym'):
1092
- if table[row, column] == '*none*':
1093
- table.itemconfig(row, column, foreground='#666',
1094
- selectforeground='#666')
1095
- root.mainloop()
1096
-
1097
- if __name__ == '__main__':
1098
- demo()
1
+ # Natural Language Toolkit: Table widget
2
+ #
3
+ # Copyright (C) 2001-2018 NLTK Project
4
+ # Author: Edward Loper <edloper@gmail.com>
5
+ # URL: <http://nltk.org/>
6
+ # For license information, see LICENSE.TXT
7
+
8
+ """
9
+ Tkinter widgets for displaying multi-column listboxes and tables.
10
+ """
11
+
12
+ from __future__ import division
13
+
14
+
15
+ import operator
16
+
17
+ from six.moves.tkinter import (Frame, Label, Listbox, Scrollbar, Tk)
18
+
19
+
20
+ ######################################################################
21
+ # Multi-Column Listbox
22
+ ######################################################################
23
+
24
+ class MultiListbox(Frame):
25
+ """
26
+ A multi-column listbox, where the current selection applies to an
27
+ entire row. Based on the MultiListbox Tkinter widget
28
+ recipe from the Python Cookbook (http://code.activestate.com/recipes/52266/)
29
+
30
+ For the most part, ``MultiListbox`` methods delegate to its
31
+ contained listboxes. For any methods that do not have docstrings,
32
+ see ``Tkinter.Listbox`` for a description of what that method does.
33
+ """
34
+ #/////////////////////////////////////////////////////////////////
35
+ # Configuration
36
+ #/////////////////////////////////////////////////////////////////
37
+
38
+ #: Default configuration values for the frame.
39
+ FRAME_CONFIG = dict(background='#888',
40
+ takefocus=True,
41
+ highlightthickness=1)
42
+
43
+ #: Default configurations for the column labels.
44
+ LABEL_CONFIG = dict(borderwidth=1, relief='raised',
45
+ font='helvetica -16 bold',
46
+ background='#444', foreground='white')
47
+
48
+ #: Default configuration for the column listboxes.
49
+ LISTBOX_CONFIG = dict(borderwidth=1,
50
+ selectborderwidth=0,
51
+ highlightthickness=0,
52
+ exportselection=False,
53
+ selectbackground='#888',
54
+ activestyle='none',
55
+ takefocus=False)
56
+
57
+ #/////////////////////////////////////////////////////////////////
58
+ # Constructor
59
+ #/////////////////////////////////////////////////////////////////
60
+
61
+ def __init__(self, master, columns, column_weights=None, cnf={}, **kw):
62
+ """
63
+ Construct a new multi-column listbox widget.
64
+
65
+ :param master: The widget that should contain the new
66
+ multi-column listbox.
67
+
68
+ :param columns: Specifies what columns should be included in
69
+ the new multi-column listbox. If ``columns`` is an integer,
70
+ the it is the number of columns to include. If it is
71
+ a list, then its length indicates the number of columns
72
+ to include; and each element of the list will be used as
73
+ a label for the corresponding column.
74
+
75
+ :param cnf, kw: Configuration parameters for this widget.
76
+ Use ``label_*`` to configure all labels; and ``listbox_*``
77
+ to configure all listboxes. E.g.:
78
+
79
+ >>> mlb = MultiListbox(master, 5, label_foreground='red')
80
+ """
81
+ # If columns was specified as an int, convert it to a list.
82
+ if isinstance(columns, int):
83
+ columns = list(range(columns))
84
+ include_labels = False
85
+ else:
86
+ include_labels = True
87
+
88
+ if len(columns) == 0:
89
+ raise ValueError("Expected at least one column")
90
+
91
+ # Instance variables
92
+ self._column_names = tuple(columns)
93
+ self._listboxes = []
94
+ self._labels = []
95
+
96
+ # Pick a default value for column_weights, if none was specified.
97
+ if column_weights is None:
98
+ column_weights = [1] * len(columns)
99
+ elif len(column_weights) != len(columns):
100
+ raise ValueError('Expected one column_weight for each column')
101
+ self._column_weights = column_weights
102
+
103
+ # Configure our widgets.
104
+ Frame.__init__(self, master, **self.FRAME_CONFIG)
105
+ self.grid_rowconfigure(1, weight=1)
106
+ for i, label in enumerate(self._column_names):
107
+ self.grid_columnconfigure(i, weight=column_weights[i])
108
+
109
+ # Create a label for the column
110
+ if include_labels:
111
+ l = Label(self, text=label, **self.LABEL_CONFIG)
112
+ self._labels.append(l)
113
+ l.grid(column=i, row=0, sticky='news', padx=0, pady=0)
114
+ l.column_index = i
115
+
116
+ # Create a listbox for the column
117
+ lb = Listbox(self, **self.LISTBOX_CONFIG)
118
+ self._listboxes.append(lb)
119
+ lb.grid(column=i, row=1, sticky='news', padx=0, pady=0)
120
+ lb.column_index = i
121
+
122
+ # Clicking or dragging selects:
123
+ lb.bind('<Button-1>', self._select)
124
+ lb.bind('<B1-Motion>', self._select)
125
+ # Scroll whell scrolls:
126
+ lb.bind('<Button-4>', lambda e: self._scroll(-1))
127
+ lb.bind('<Button-5>', lambda e: self._scroll(+1))
128
+ lb.bind('<MouseWheel>', lambda e: self._scroll(e.delta))
129
+ # Button 2 can be used to scan:
130
+ lb.bind('<Button-2>', lambda e: self.scan_mark(e.x, e.y))
131
+ lb.bind('<B2-Motion>', lambda e: self.scan_dragto(e.x, e.y))
132
+ # Dragging outside the window has no effect (diable
133
+ # the default listbox behavior, which scrolls):
134
+ lb.bind('<B1-Leave>', lambda e: 'break')
135
+ # Columns can be resized by dragging them:
136
+ l.bind('<Button-1>', self._resize_column)
137
+
138
+ # Columns can be resized by dragging them. (This binding is
139
+ # used if they click on the grid between columns:)
140
+ self.bind('<Button-1>', self._resize_column)
141
+
142
+ # Set up key bindings for the widget:
143
+ self.bind('<Up>', lambda e: self.select(delta=-1))
144
+ self.bind('<Down>', lambda e: self.select(delta=1))
145
+ self.bind('<Prior>', lambda e: self.select(delta=-self._pagesize()))
146
+ self.bind('<Next>', lambda e: self.select(delta=self._pagesize()))
147
+
148
+ # Configuration customizations
149
+ self.configure(cnf, **kw)
150
+
151
+ #/////////////////////////////////////////////////////////////////
152
+ # Column Resizing
153
+ #/////////////////////////////////////////////////////////////////
154
+
155
+ def _resize_column(self, event):
156
+ """
157
+ Callback used to resize a column of the table. Return ``True``
158
+ if the column is actually getting resized (if the user clicked
159
+ on the far left or far right 5 pixels of a label); and
160
+ ``False`` otherwies.
161
+ """
162
+ # If we're already waiting for a button release, then ignore
163
+ # the new button press.
164
+ if event.widget.bind('<ButtonRelease>'):
165
+ return False
166
+
167
+ # Decide which column (if any) to resize.
168
+ self._resize_column_index = None
169
+ if event.widget is self:
170
+ for i, lb in enumerate(self._listboxes):
171
+ if abs(event.x-(lb.winfo_x()+lb.winfo_width())) < 10:
172
+ self._resize_column_index = i
173
+ elif event.x > (event.widget.winfo_width()-5):
174
+ self._resize_column_index = event.widget.column_index
175
+ elif event.x < 5 and event.widget.column_index != 0:
176
+ self._resize_column_index = event.widget.column_index-1
177
+
178
+ # Bind callbacks that are used to resize it.
179
+ if self._resize_column_index is not None:
180
+ event.widget.bind('<Motion>', self._resize_column_motion_cb)
181
+ event.widget.bind('<ButtonRelease-%d>' % event.num,
182
+ self._resize_column_buttonrelease_cb)
183
+ return True
184
+ else:
185
+ return False
186
+
187
+ def _resize_column_motion_cb(self, event):
188
+ lb = self._listboxes[self._resize_column_index]
189
+ charwidth = lb.winfo_width() / lb['width']
190
+
191
+ x1 = event.x + event.widget.winfo_x()
192
+ x2 = lb.winfo_x() + lb.winfo_width()
193
+
194
+ lb['width'] = max(3, lb['width'] + (x1-x2) // charwidth)
195
+
196
+ def _resize_column_buttonrelease_cb(self, event):
197
+ event.widget.unbind('<ButtonRelease-%d>' % event.num)
198
+ event.widget.unbind('<Motion>')
199
+
200
+ #/////////////////////////////////////////////////////////////////
201
+ # Properties
202
+ #/////////////////////////////////////////////////////////////////
203
+
204
+ @property
205
+ def column_names(self):
206
+ """
207
+ A tuple containing the names of the columns used by this
208
+ multi-column listbox.
209
+ """
210
+ return self._column_names
211
+
212
+ @property
213
+ def column_labels(self):
214
+ """
215
+ A tuple containing the ``Tkinter.Label`` widgets used to
216
+ display the label of each column. If this multi-column
217
+ listbox was created without labels, then this will be an empty
218
+ tuple. These widgets will all be augmented with a
219
+ ``column_index`` attribute, which can be used to determine
220
+ which column they correspond to. This can be convenient,
221
+ e.g., when defining callbacks for bound events.
222
+ """
223
+ return tuple(self._labels)
224
+
225
+ @property
226
+ def listboxes(self):
227
+ """
228
+ A tuple containing the ``Tkinter.Listbox`` widgets used to
229
+ display individual columns. These widgets will all be
230
+ augmented with a ``column_index`` attribute, which can be used
231
+ to determine which column they correspond to. This can be
232
+ convenient, e.g., when defining callbacks for bound events.
233
+ """
234
+ return tuple(self._listboxes)
235
+
236
+ #/////////////////////////////////////////////////////////////////
237
+ # Mouse & Keyboard Callback Functions
238
+ #/////////////////////////////////////////////////////////////////
239
+
240
+ def _select(self, e):
241
+ i = e.widget.nearest(e.y)
242
+ self.selection_clear(0, 'end')
243
+ self.selection_set(i)
244
+ self.activate(i)
245
+ self.focus()
246
+
247
+ def _scroll(self, delta):
248
+ for lb in self._listboxes:
249
+ lb.yview_scroll(delta, 'unit')
250
+ return 'break'
251
+
252
+ def _pagesize(self):
253
+ """:return: The number of rows that makes up one page"""
254
+ return int(self.index('@0,1000000')) - int(self.index('@0,0'))
255
+
256
+ #/////////////////////////////////////////////////////////////////
257
+ # Row selection
258
+ #/////////////////////////////////////////////////////////////////
259
+
260
+ def select(self, index=None, delta=None, see=True):
261
+ """
262
+ Set the selected row. If ``index`` is specified, then select
263
+ row ``index``. Otherwise, if ``delta`` is specified, then move
264
+ the current selection by ``delta`` (negative numbers for up,
265
+ positive numbers for down). This will not move the selection
266
+ past the top or the bottom of the list.
267
+
268
+ :param see: If true, then call ``self.see()`` with the newly
269
+ selected index, to ensure that it is visible.
270
+ """
271
+ if (index is not None) and (delta is not None):
272
+ raise ValueError('specify index or delta, but not both')
273
+
274
+ # If delta was given, then calculate index.
275
+ if delta is not None:
276
+ if len(self.curselection()) == 0:
277
+ index = -1 + delta
278
+ else:
279
+ index = int(self.curselection()[0]) + delta
280
+
281
+ # Clear all selected rows.
282
+ self.selection_clear(0, 'end')
283
+
284
+ # Select the specified index
285
+ if index is not None:
286
+ index = min(max(index, 0), self.size()-1)
287
+ #self.activate(index)
288
+ self.selection_set(index)
289
+ if see: self.see(index)
290
+
291
+ #/////////////////////////////////////////////////////////////////
292
+ # Configuration
293
+ #/////////////////////////////////////////////////////////////////
294
+
295
+ def configure(self, cnf={}, **kw):
296
+ """
297
+ Configure this widget. Use ``label_*`` to configure all
298
+ labels; and ``listbox_*`` to configure all listboxes. E.g.:
299
+
300
+ >>> mlb = MultiListbox(master, 5)
301
+ >>> mlb.configure(label_foreground='red')
302
+ >>> mlb.configure(listbox_foreground='red')
303
+ """
304
+ cnf = dict(list(cnf.items()) + list(kw.items()))
305
+ for (key, val) in list(cnf.items()):
306
+ if key.startswith('label_') or key.startswith('label-'):
307
+ for label in self._labels:
308
+ label.configure({key[6:]: val})
309
+ elif key.startswith('listbox_') or key.startswith('listbox-'):
310
+ for listbox in self._listboxes:
311
+ listbox.configure({key[8:]: val})
312
+ else:
313
+ Frame.configure(self, {key:val})
314
+
315
+ def __setitem__(self, key, val):
316
+ """
317
+ Configure this widget. This is equivalent to
318
+ ``self.configure({key,val``)}. See ``configure()``.
319
+ """
320
+ self.configure({key:val})
321
+
322
+ def rowconfigure(self, row_index, cnf={}, **kw):
323
+ """
324
+ Configure all table cells in the given row. Valid keyword
325
+ arguments are: ``background``, ``bg``, ``foreground``, ``fg``,
326
+ ``selectbackground``, ``selectforeground``.
327
+ """
328
+ for lb in self._listboxes: lb.itemconfigure(row_index, cnf, **kw)
329
+
330
+ def columnconfigure(self, col_index, cnf={}, **kw):
331
+ """
332
+ Configure all table cells in the given column. Valid keyword
333
+ arguments are: ``background``, ``bg``, ``foreground``, ``fg``,
334
+ ``selectbackground``, ``selectforeground``.
335
+ """
336
+ lb = self._listboxes[col_index]
337
+
338
+ cnf = dict(list(cnf.items()) + list(kw.items()))
339
+ for (key, val) in list(cnf.items()):
340
+ if key in ('background', 'bg', 'foreground', 'fg',
341
+ 'selectbackground', 'selectforeground'):
342
+ for i in range(lb.size()): lb.itemconfigure(i, {key:val})
343
+ else:
344
+ lb.configure({key:val})
345
+
346
+ def itemconfigure(self, row_index, col_index, cnf=None, **kw):
347
+ """
348
+ Configure the table cell at the given row and column. Valid
349
+ keyword arguments are: ``background``, ``bg``, ``foreground``,
350
+ ``fg``, ``selectbackground``, ``selectforeground``.
351
+ """
352
+ lb = self._listboxes[col_index]
353
+ return lb.itemconfigure(row_index, cnf, **kw)
354
+
355
+ #/////////////////////////////////////////////////////////////////
356
+ # Value Access
357
+ #/////////////////////////////////////////////////////////////////
358
+
359
+ def insert(self, index, *rows):
360
+ """
361
+ Insert the given row or rows into the table, at the given
362
+ index. Each row value should be a tuple of cell values, one
363
+ for each column in the row. Index may be an integer or any of
364
+ the special strings (such as ``'end'``) accepted by
365
+ ``Tkinter.Listbox``.
366
+ """
367
+ for elt in rows:
368
+ if len(elt) != len(self._column_names):
369
+ raise ValueError('rows should be tuples whose length '
370
+ 'is equal to the number of columns')
371
+ for (lb,elts) in zip(self._listboxes, list(zip(*rows))):
372
+ lb.insert(index, *elts)
373
+
374
+ def get(self, first, last=None):
375
+ """
376
+ Return the value(s) of the specified row(s). If ``last`` is
377
+ not specified, then return a single row value; otherwise,
378
+ return a list of row values. Each row value is a tuple of
379
+ cell values, one for each column in the row.
380
+ """
381
+ values = [lb.get(first, last) for lb in self._listboxes]
382
+ if last:
383
+ return [tuple(row) for row in zip(*values)]
384
+ else:
385
+ return tuple(values)
386
+
387
+ def bbox(self, row, col):
388
+ """
389
+ Return the bounding box for the given table cell, relative to
390
+ this widget's top-left corner. The bounding box is a tuple
391
+ of integers ``(left, top, width, height)``.
392
+ """
393
+ dx, dy, _, _ = self.grid_bbox(row=0, column=col)
394
+ x, y, w, h = self._listboxes[col].bbox(row)
395
+ return int(x)+int(dx), int(y)+int(dy), int(w), int(h)
396
+
397
+ #/////////////////////////////////////////////////////////////////
398
+ # Hide/Show Columns
399
+ #/////////////////////////////////////////////////////////////////
400
+
401
+ def hide_column(self, col_index):
402
+ """
403
+ Hide the given column. The column's state is still
404
+ maintained: its values will still be returned by ``get()``, and
405
+ you must supply its values when calling ``insert()``. It is
406
+ safe to call this on a column that is already hidden.
407
+
408
+ :see: ``show_column()``
409
+ """
410
+ if self._labels:
411
+ self._labels[col_index].grid_forget()
412
+ self.listboxes[col_index].grid_forget()
413
+ self.grid_columnconfigure(col_index, weight=0)
414
+
415
+ def show_column(self, col_index):
416
+ """
417
+ Display a column that has been hidden using ``hide_column()``.
418
+ It is safe to call this on a column that is not hidden.
419
+ """
420
+ weight = self._column_weights[col_index]
421
+ if self._labels:
422
+ self._labels[col_index].grid(column=col_index, row=0,
423
+ sticky='news', padx=0, pady=0)
424
+ self._listboxes[col_index].grid(column=col_index, row=1,
425
+ sticky='news', padx=0, pady=0)
426
+ self.grid_columnconfigure(col_index, weight=weight)
427
+
428
+ #/////////////////////////////////////////////////////////////////
429
+ # Binding Methods
430
+ #/////////////////////////////////////////////////////////////////
431
+
432
+ def bind_to_labels(self, sequence=None, func=None, add=None):
433
+ """
434
+ Add a binding to each ``Tkinter.Label`` widget in this
435
+ mult-column listbox that will call ``func`` in response to the
436
+ event sequence.
437
+
438
+ :return: A list of the identifiers of replaced binding
439
+ functions (if any), allowing for their deletion (to
440
+ prevent a memory leak).
441
+ """
442
+ return [label.bind(sequence, func, add)
443
+ for label in self.column_labels]
444
+
445
+ def bind_to_listboxes(self, sequence=None, func=None, add=None):
446
+ """
447
+ Add a binding to each ``Tkinter.Listbox`` widget in this
448
+ mult-column listbox that will call ``func`` in response to the
449
+ event sequence.
450
+
451
+ :return: A list of the identifiers of replaced binding
452
+ functions (if any), allowing for their deletion (to
453
+ prevent a memory leak).
454
+ """
455
+ for listbox in self.listboxes:
456
+ listbox.bind(sequence, func, add)
457
+
458
+ def bind_to_columns(self, sequence=None, func=None, add=None):
459
+ """
460
+ Add a binding to each ``Tkinter.Label`` and ``Tkinter.Listbox``
461
+ widget in this mult-column listbox that will call ``func`` in
462
+ response to the event sequence.
463
+
464
+ :return: A list of the identifiers of replaced binding
465
+ functions (if any), allowing for their deletion (to
466
+ prevent a memory leak).
467
+ """
468
+ return (self.bind_to_labels(sequence, func, add) +
469
+ self.bind_to_listboxes(sequence, func, add))
470
+
471
+ #/////////////////////////////////////////////////////////////////
472
+ # Simple Delegation
473
+ #/////////////////////////////////////////////////////////////////
474
+
475
+ # These methods delegate to the first listbox:
476
+ def curselection(self, *args, **kwargs):
477
+ return self._listboxes[0].curselection(*args, **kwargs)
478
+ def selection_includes(self, *args, **kwargs):
479
+ return self._listboxes[0].selection_includes(*args, **kwargs)
480
+ def itemcget(self, *args, **kwargs):
481
+ return self._listboxes[0].itemcget(*args, **kwargs)
482
+ def size(self, *args, **kwargs):
483
+ return self._listboxes[0].size(*args, **kwargs)
484
+ def index(self, *args, **kwargs):
485
+ return self._listboxes[0].index(*args, **kwargs)
486
+ def nearest(self, *args, **kwargs):
487
+ return self._listboxes[0].nearest(*args, **kwargs)
488
+
489
+ # These methods delegate to each listbox (and return None):
490
+ def activate(self, *args, **kwargs):
491
+ for lb in self._listboxes: lb.activate(*args, **kwargs)
492
+ def delete(self, *args, **kwargs):
493
+ for lb in self._listboxes: lb.delete(*args, **kwargs)
494
+ def scan_mark(self, *args, **kwargs):
495
+ for lb in self._listboxes: lb.scan_mark(*args, **kwargs)
496
+ def scan_dragto(self, *args, **kwargs):
497
+ for lb in self._listboxes: lb.scan_dragto(*args, **kwargs)
498
+ def see(self, *args, **kwargs):
499
+ for lb in self._listboxes: lb.see(*args, **kwargs)
500
+ def selection_anchor(self, *args, **kwargs):
501
+ for lb in self._listboxes: lb.selection_anchor(*args, **kwargs)
502
+ def selection_clear(self, *args, **kwargs):
503
+ for lb in self._listboxes: lb.selection_clear(*args, **kwargs)
504
+ def selection_set(self, *args, **kwargs):
505
+ for lb in self._listboxes: lb.selection_set(*args, **kwargs)
506
+ def yview(self, *args, **kwargs):
507
+ for lb in self._listboxes: v = lb.yview(*args, **kwargs)
508
+ return v # if called with no arguments
509
+ def yview_moveto(self, *args, **kwargs):
510
+ for lb in self._listboxes: lb.yview_moveto(*args, **kwargs)
511
+ def yview_scroll(self, *args, **kwargs):
512
+ for lb in self._listboxes: lb.yview_scroll(*args, **kwargs)
513
+
514
+ #/////////////////////////////////////////////////////////////////
515
+ # Aliases
516
+ #/////////////////////////////////////////////////////////////////
517
+
518
+ itemconfig = itemconfigure
519
+ rowconfig = rowconfigure
520
+ columnconfig = columnconfigure
521
+ select_anchor = selection_anchor
522
+ select_clear = selection_clear
523
+ select_includes = selection_includes
524
+ select_set = selection_set
525
+
526
+ #/////////////////////////////////////////////////////////////////
527
+ # These listbox methods are not defined for multi-listbox
528
+ #/////////////////////////////////////////////////////////////////
529
+ # def xview(self, *what): pass
530
+ # def xview_moveto(self, fraction): pass
531
+ # def xview_scroll(self, number, what): pass
532
+
533
+ ######################################################################
534
+ # Table
535
+ ######################################################################
536
+
537
+ class Table(object):
538
+ """
539
+ A display widget for a table of values, based on a ``MultiListbox``
540
+ widget. For many purposes, ``Table`` can be treated as a
541
+ list-of-lists. E.g., table[i] is a list of the values for row i;
542
+ and table.append(row) adds a new row with the given lits of
543
+ values. Individual cells can be accessed using table[i,j], which
544
+ refers to the j-th column of the i-th row. This can be used to
545
+ both read and write values from the table. E.g.:
546
+
547
+ >>> table[i,j] = 'hello'
548
+
549
+ The column (j) can be given either as an index number, or as a
550
+ column name. E.g., the following prints the value in the 3rd row
551
+ for the 'First Name' column:
552
+
553
+ >>> print(table[3, 'First Name'])
554
+ John
555
+
556
+ You can configure the colors for individual rows, columns, or
557
+ cells using ``rowconfig()``, ``columnconfig()``, and ``itemconfig()``.
558
+ The color configuration for each row will be preserved if the
559
+ table is modified; however, when new rows are added, any color
560
+ configurations that have been made for *columns* will not be
561
+ applied to the new row.
562
+
563
+ Note: Although ``Table`` acts like a widget in some ways (e.g., it
564
+ defines ``grid()``, ``pack()``, and ``bind()``), it is not itself a
565
+ widget; it just contains one. This is because widgets need to
566
+ define ``__getitem__()``, ``__setitem__()``, and ``__nonzero__()`` in
567
+ a way that's incompatible with the fact that ``Table`` behaves as a
568
+ list-of-lists.
569
+
570
+ :ivar _mlb: The multi-column listbox used to display this table's data.
571
+ :ivar _rows: A list-of-lists used to hold the cell values of this
572
+ table. Each element of _rows is a row value, i.e., a list of
573
+ cell values, one for each column in the row.
574
+ """
575
+ def __init__(self, master, column_names, rows=None,
576
+ column_weights=None,
577
+ scrollbar=True, click_to_sort=True,
578
+ reprfunc=None, cnf={}, **kw):
579
+ """
580
+ Construct a new Table widget.
581
+
582
+ :type master: Tkinter.Widget
583
+ :param master: The widget that should contain the new table.
584
+ :type column_names: list(str)
585
+ :param column_names: A list of names for the columns; these
586
+ names will be used to create labels for each column;
587
+ and can be used as an index when reading or writing
588
+ cell values from the table.
589
+ :type rows: list(list)
590
+ :param rows: A list of row values used to initialze the table.
591
+ Each row value should be a tuple of cell values, one for
592
+ each column in the row.
593
+ :type scrollbar: bool
594
+ :param scrollbar: If true, then create a scrollbar for the
595
+ new table widget.
596
+ :type click_to_sort: bool
597
+ :param click_to_sort: If true, then create bindings that will
598
+ sort the table's rows by a given column's values if the
599
+ user clicks on that colum's label.
600
+ :type reprfunc: function
601
+ :param reprfunc: If specified, then use this function to
602
+ convert each table cell value to a string suitable for
603
+ display. ``reprfunc`` has the following signature:
604
+ reprfunc(row_index, col_index, cell_value) -> str
605
+ (Note that the column is specified by index, not by name.)
606
+ :param cnf, kw: Configuration parameters for this widget's
607
+ contained ``MultiListbox``. See ``MultiListbox.__init__()``
608
+ for details.
609
+ """
610
+ self._num_columns = len(column_names)
611
+ self._reprfunc = reprfunc
612
+ self._frame = Frame(master)
613
+
614
+ self._column_name_to_index = dict((c,i) for (i,c) in
615
+ enumerate(column_names))
616
+
617
+ # Make a copy of the rows & check that it's valid.
618
+ if rows is None: self._rows = []
619
+ else: self._rows = [[v for v in row] for row in rows]
620
+ for row in self._rows: self._checkrow(row)
621
+
622
+ # Create our multi-list box.
623
+ self._mlb = MultiListbox(self._frame, column_names,
624
+ column_weights, cnf, **kw)
625
+ self._mlb.pack(side='left', expand=True, fill='both')
626
+
627
+ # Optional scrollbar
628
+ if scrollbar:
629
+ sb = Scrollbar(self._frame, orient='vertical',
630
+ command=self._mlb.yview)
631
+ self._mlb.listboxes[0]['yscrollcommand'] = sb.set
632
+ #for listbox in self._mlb.listboxes:
633
+ # listbox['yscrollcommand'] = sb.set
634
+ sb.pack(side='right', fill='y')
635
+ self._scrollbar = sb
636
+
637
+ # Set up sorting
638
+ self._sortkey = None
639
+ if click_to_sort:
640
+ for i, l in enumerate(self._mlb.column_labels):
641
+ l.bind('<Button-1>', self._sort)
642
+
643
+ # Fill in our multi-list box.
644
+ self._fill_table()
645
+
646
+ #/////////////////////////////////////////////////////////////////
647
+ #{ Widget-like Methods
648
+ #/////////////////////////////////////////////////////////////////
649
+ # These all just delegate to either our frame or our MLB.
650
+
651
+ def pack(self, *args, **kwargs):
652
+ """Position this table's main frame widget in its parent
653
+ widget. See ``Tkinter.Frame.pack()`` for more info."""
654
+ self._frame.pack(*args, **kwargs)
655
+
656
+ def grid(self, *args, **kwargs):
657
+ """Position this table's main frame widget in its parent
658
+ widget. See ``Tkinter.Frame.grid()`` for more info."""
659
+ self._frame.grid(*args, **kwargs)
660
+
661
+ def focus(self):
662
+ """Direct (keyboard) input foxus to this widget."""
663
+ self._mlb.focus()
664
+
665
+ def bind(self, sequence=None, func=None, add=None):
666
+ """Add a binding to this table's main frame that will call
667
+ ``func`` in response to the event sequence."""
668
+ self._mlb.bind(sequence, func, add)
669
+
670
+ def rowconfigure(self, row_index, cnf={}, **kw):
671
+ """:see: ``MultiListbox.rowconfigure()``"""
672
+ self._mlb.rowconfigure(row_index, cnf, **kw)
673
+
674
+ def columnconfigure(self, col_index, cnf={}, **kw):
675
+ """:see: ``MultiListbox.columnconfigure()``"""
676
+ col_index = self.column_index(col_index)
677
+ self._mlb.columnconfigure(col_index, cnf, **kw)
678
+
679
+ def itemconfigure(self, row_index, col_index, cnf=None, **kw):
680
+ """:see: ``MultiListbox.itemconfigure()``"""
681
+ col_index = self.column_index(col_index)
682
+ return self._mlb.itemconfigure(row_index, col_index, cnf, **kw)
683
+
684
+ def bind_to_labels(self, sequence=None, func=None, add=None):
685
+ """:see: ``MultiListbox.bind_to_labels()``"""
686
+ return self._mlb.bind_to_labels(sequence, func, add)
687
+
688
+ def bind_to_listboxes(self, sequence=None, func=None, add=None):
689
+ """:see: ``MultiListbox.bind_to_listboxes()``"""
690
+ return self._mlb.bind_to_listboxes(sequence, func, add)
691
+
692
+ def bind_to_columns(self, sequence=None, func=None, add=None):
693
+ """:see: ``MultiListbox.bind_to_columns()``"""
694
+ return self._mlb.bind_to_columns(sequence, func, add)
695
+
696
+ rowconfig = rowconfigure
697
+ columnconfig = columnconfigure
698
+ itemconfig = itemconfigure
699
+
700
+ #/////////////////////////////////////////////////////////////////
701
+ #{ Table as list-of-lists
702
+ #/////////////////////////////////////////////////////////////////
703
+
704
+ def insert(self, row_index, rowvalue):
705
+ """
706
+ Insert a new row into the table, so that its row index will be
707
+ ``row_index``. If the table contains any rows whose row index
708
+ is greater than or equal to ``row_index``, then they will be
709
+ shifted down.
710
+
711
+ :param rowvalue: A tuple of cell values, one for each column
712
+ in the new row.
713
+ """
714
+ self._checkrow(rowvalue)
715
+ self._rows.insert(row_index, rowvalue)
716
+ if self._reprfunc is not None:
717
+ rowvalue = [self._reprfunc(row_index,j,v)
718
+ for (j,v) in enumerate(rowvalue)]
719
+ self._mlb.insert(row_index, rowvalue)
720
+ if self._DEBUG: self._check_table_vs_mlb()
721
+
722
+ def extend(self, rowvalues):
723
+ """
724
+ Add new rows at the end of the table.
725
+
726
+ :param rowvalues: A list of row values used to initialze the
727
+ table. Each row value should be a tuple of cell values,
728
+ one for each column in the row.
729
+ """
730
+ for rowvalue in rowvalues: self.append(rowvalue)
731
+ if self._DEBUG: self._check_table_vs_mlb()
732
+
733
+ def append(self, rowvalue):
734
+ """
735
+ Add a new row to the end of the table.
736
+
737
+ :param rowvalue: A tuple of cell values, one for each column
738
+ in the new row.
739
+ """
740
+ self.insert(len(self._rows), rowvalue)
741
+ if self._DEBUG: self._check_table_vs_mlb()
742
+
743
+ def clear(self):
744
+ """
745
+ Delete all rows in this table.
746
+ """
747
+ self._rows = []
748
+ self._mlb.delete(0, 'end')
749
+ if self._DEBUG: self._check_table_vs_mlb()
750
+
751
+ def __getitem__(self, index):
752
+ """
753
+ Return the value of a row or a cell in this table. If
754
+ ``index`` is an integer, then the row value for the ``index``th
755
+ row. This row value consists of a tuple of cell values, one
756
+ for each column in the row. If ``index`` is a tuple of two
757
+ integers, ``(i,j)``, then return the value of the cell in the
758
+ ``i``th row and the ``j``th column.
759
+ """
760
+ if isinstance(index, slice):
761
+ raise ValueError('Slicing not supported')
762
+ elif isinstance(index, tuple) and len(index)==2:
763
+ return self._rows[index[0]][self.column_index(index[1])]
764
+ else:
765
+ return tuple(self._rows[index])
766
+
767
+ def __setitem__(self, index, val):
768
+ """
769
+ Replace the value of a row or a cell in this table with
770
+ ``val``.
771
+
772
+ If ``index`` is an integer, then ``val`` should be a row value
773
+ (i.e., a tuple of cell values, one for each column). In this
774
+ case, the values of the ``index``th row of the table will be
775
+ replaced with the values in ``val``.
776
+
777
+ If ``index`` is a tuple of integers, ``(i,j)``, then replace the
778
+ value of the cell in the ``i``th row and ``j``th column with
779
+ ``val``.
780
+ """
781
+ if isinstance(index, slice):
782
+ raise ValueError('Slicing not supported')
783
+
784
+
785
+ # table[i,j] = val
786
+ elif isinstance(index, tuple) and len(index)==2:
787
+ i, j = index[0], self.column_index(index[1])
788
+ config_cookie = self._save_config_info([i])
789
+ self._rows[i][j] = val
790
+ if self._reprfunc is not None:
791
+ val = self._reprfunc(i, j, val)
792
+ self._mlb.listboxes[j].insert(i, val)
793
+ self._mlb.listboxes[j].delete(i+1)
794
+ self._restore_config_info(config_cookie)
795
+
796
+ # table[i] = val
797
+ else:
798
+ config_cookie = self._save_config_info([index])
799
+ self._checkrow(val)
800
+ self._rows[index] = list(val)
801
+ if self._reprfunc is not None:
802
+ val = [self._reprfunc(index,j,v) for (j,v) in enumerate(val)]
803
+ self._mlb.insert(index, val)
804
+ self._mlb.delete(index+1)
805
+ self._restore_config_info(config_cookie)
806
+
807
+ def __delitem__(self, row_index):
808
+ """
809
+ Delete the ``row_index``th row from this table.
810
+ """
811
+ if isinstance(row_index, slice):
812
+ raise ValueError('Slicing not supported')
813
+ if isinstance(row_index, tuple) and len(row_index)==2:
814
+ raise ValueError('Cannot delete a single cell!')
815
+ del self._rows[row_index]
816
+ self._mlb.delete(row_index)
817
+ if self._DEBUG: self._check_table_vs_mlb()
818
+
819
+ def __len__(self):
820
+ """
821
+ :return: the number of rows in this table.
822
+ """
823
+ return len(self._rows)
824
+
825
+ def _checkrow(self, rowvalue):
826
+ """
827
+ Helper function: check that a given row value has the correct
828
+ number of elements; and if not, raise an exception.
829
+ """
830
+ if len(rowvalue) != self._num_columns:
831
+ raise ValueError('Row %r has %d columns; expected %d' %
832
+ (rowvalue, len(rowvalue), self._num_columns))
833
+
834
+ #/////////////////////////////////////////////////////////////////
835
+ # Columns
836
+ #/////////////////////////////////////////////////////////////////
837
+
838
+ @property
839
+ def column_names(self):
840
+ """A list of the names of the columns in this table."""
841
+ return self._mlb.column_names
842
+
843
+ def column_index(self, i):
844
+ """
845
+ If ``i`` is a valid column index integer, then return it as is.
846
+ Otherwise, check if ``i`` is used as the name for any column;
847
+ if so, return that column's index. Otherwise, raise a
848
+ ``KeyError`` exception.
849
+ """
850
+ if isinstance(i, int) and 0 <= i < self._num_columns:
851
+ return i
852
+ else:
853
+ # This raises a key error if the column is not found.
854
+ return self._column_name_to_index[i]
855
+
856
+ def hide_column(self, column_index):
857
+ """:see: ``MultiListbox.hide_column()``"""
858
+ self._mlb.hide_column(self.column_index(column_index))
859
+
860
+ def show_column(self, column_index):
861
+ """:see: ``MultiListbox.show_column()``"""
862
+ self._mlb.show_column(self.column_index(column_index))
863
+
864
+ #/////////////////////////////////////////////////////////////////
865
+ # Selection
866
+ #/////////////////////////////////////////////////////////////////
867
+
868
+ def selected_row(self):
869
+ """
870
+ Return the index of the currently selected row, or None if
871
+ no row is selected. To get the row value itself, use
872
+ ``table[table.selected_row()]``.
873
+ """
874
+ sel = self._mlb.curselection()
875
+ if sel: return int(sel[0])
876
+ else: return None
877
+
878
+ def select(self, index=None, delta=None, see=True):
879
+ """:see: ``MultiListbox.select()``"""
880
+ self._mlb.select(index, delta, see)
881
+
882
+ #/////////////////////////////////////////////////////////////////
883
+ # Sorting
884
+ #/////////////////////////////////////////////////////////////////
885
+
886
+ def sort_by(self, column_index, order='toggle'):
887
+ """
888
+ Sort the rows in this table, using the specified column's
889
+ values as a sort key.
890
+
891
+ :param column_index: Specifies which column to sort, using
892
+ either a column index (int) or a column's label name
893
+ (str).
894
+
895
+ :param order: Specifies whether to sort the values in
896
+ ascending or descending order:
897
+
898
+ - ``'ascending'``: Sort from least to greatest.
899
+ - ``'descending'``: Sort from greatest to least.
900
+ - ``'toggle'``: If the most recent call to ``sort_by()``
901
+ sorted the table by the same column (``column_index``),
902
+ then reverse the rows; otherwise sort in ascending
903
+ order.
904
+ """
905
+ if order not in ('ascending', 'descending', 'toggle'):
906
+ raise ValueError('sort_by(): order should be "ascending", '
907
+ '"descending", or "toggle".')
908
+ column_index = self.column_index(column_index)
909
+ config_cookie = self._save_config_info(index_by_id=True)
910
+
911
+ # Sort the rows.
912
+ if order == 'toggle' and column_index == self._sortkey:
913
+ self._rows.reverse()
914
+ else:
915
+ self._rows.sort(key=operator.itemgetter(column_index),
916
+ reverse=(order=='descending'))
917
+ self._sortkey = column_index
918
+
919
+ # Redraw the table.
920
+ self._fill_table()
921
+ self._restore_config_info(config_cookie, index_by_id=True, see=True)
922
+ if self._DEBUG: self._check_table_vs_mlb()
923
+
924
+ def _sort(self, event):
925
+ """Event handler for clicking on a column label -- sort by
926
+ that column."""
927
+ column_index = event.widget.column_index
928
+
929
+ # If they click on the far-left of far-right of a column's
930
+ # label, then resize rather than sorting.
931
+ if self._mlb._resize_column(event):
932
+ return 'continue'
933
+
934
+ # Otherwise, sort.
935
+ else:
936
+ self.sort_by(column_index)
937
+ return 'continue'
938
+
939
+ #/////////////////////////////////////////////////////////////////
940
+ #{ Table Drawing Helpers
941
+ #/////////////////////////////////////////////////////////////////
942
+
943
+ def _fill_table(self, save_config=True):
944
+ """
945
+ Re-draw the table from scratch, by clearing out the table's
946
+ multi-column listbox; and then filling it in with values from
947
+ ``self._rows``. Note that any cell-, row-, or column-specific
948
+ color configuration that has been done will be lost. The
949
+ selection will also be lost -- i.e., no row will be selected
950
+ after this call completes.
951
+ """
952
+ self._mlb.delete(0, 'end')
953
+ for i, row in enumerate(self._rows):
954
+ if self._reprfunc is not None:
955
+ row = [self._reprfunc(i,j,v) for (j,v) in enumerate(row)]
956
+ self._mlb.insert('end', row)
957
+
958
+ def _get_itemconfig(self, r, c):
959
+ return dict( (k, self._mlb.itemconfig(r, c, k)[-1])
960
+ for k in ('foreground', 'selectforeground',
961
+ 'background', 'selectbackground') )
962
+
963
+ def _save_config_info(self, row_indices=None, index_by_id=False):
964
+ """
965
+ Return a 'cookie' containing information about which row is
966
+ selected, and what color configurations have been applied.
967
+ this information can the be re-applied to the table (after
968
+ making modifications) using ``_restore_config_info()``. Color
969
+ configuration information will be saved for any rows in
970
+ ``row_indices``, or in the entire table, if
971
+ ``row_indices=None``. If ``index_by_id=True``, the the cookie
972
+ will associate rows with their configuration information based
973
+ on the rows' python id. This is useful when performing
974
+ operations that re-arrange the rows (e.g. ``sort``). If
975
+ ``index_by_id=False``, then it is assumed that all rows will be
976
+ in the same order when ``_restore_config_info()`` is called.
977
+ """
978
+ # Default value for row_indices is all rows.
979
+ if row_indices is None:
980
+ row_indices = list(range(len(self._rows)))
981
+
982
+ # Look up our current selection.
983
+ selection = self.selected_row()
984
+ if index_by_id and selection is not None:
985
+ selection = id(self._rows[selection])
986
+
987
+ # Look up the color configuration info for each row.
988
+ if index_by_id:
989
+ config = dict((id(self._rows[r]), [self._get_itemconfig(r, c)
990
+ for c in range(self._num_columns)])
991
+ for r in row_indices)
992
+ else:
993
+ config = dict((r, [self._get_itemconfig(r, c)
994
+ for c in range(self._num_columns)])
995
+ for r in row_indices)
996
+
997
+
998
+ return selection, config
999
+
1000
+ def _restore_config_info(self, cookie, index_by_id=False, see=False):
1001
+ """
1002
+ Restore selection & color configuration information that was
1003
+ saved using ``_save_config_info``.
1004
+ """
1005
+ selection, config = cookie
1006
+
1007
+ # Clear the selection.
1008
+ if selection is None:
1009
+ self._mlb.selection_clear(0, 'end')
1010
+
1011
+ # Restore selection & color config
1012
+ if index_by_id:
1013
+ for r, row in enumerate(self._rows):
1014
+ if id(row) in config:
1015
+ for c in range(self._num_columns):
1016
+ self._mlb.itemconfigure(r, c, config[id(row)][c])
1017
+ if id(row) == selection:
1018
+ self._mlb.select(r, see=see)
1019
+ else:
1020
+ if selection is not None:
1021
+ self._mlb.select(selection, see=see)
1022
+ for r in config:
1023
+ for c in range(self._num_columns):
1024
+ self._mlb.itemconfigure(r, c, config[r][c])
1025
+
1026
+ #/////////////////////////////////////////////////////////////////
1027
+ # Debugging (Invariant Checker)
1028
+ #/////////////////////////////////////////////////////////////////
1029
+
1030
+ _DEBUG = False
1031
+ """If true, then run ``_check_table_vs_mlb()`` after any operation
1032
+ that modifies the table."""
1033
+
1034
+ def _check_table_vs_mlb(self):
1035
+ """
1036
+ Verify that the contents of the table's ``_rows`` variable match
1037
+ the contents of its multi-listbox (``_mlb``). This is just
1038
+ included for debugging purposes, to make sure that the
1039
+ list-modifying operations are working correctly.
1040
+ """
1041
+ for col in self._mlb.listboxes:
1042
+ assert len(self) == col.size()
1043
+ for row in self:
1044
+ assert len(row) == self._num_columns
1045
+ assert self._num_columns == len(self._mlb.column_names)
1046
+ #assert self._column_names == self._mlb.column_names
1047
+ for i, row in enumerate(self):
1048
+ for j, cell in enumerate(row):
1049
+ if self._reprfunc is not None:
1050
+ cell = self._reprfunc(i, j, cell)
1051
+ assert self._mlb.get(i)[j] == cell
1052
+
1053
+ ######################################################################
1054
+ # Demo/Test Function
1055
+ ######################################################################
1056
+
1057
+ # update this to use new WordNet API
1058
+ def demo():
1059
+ root = Tk()
1060
+ root.bind('<Control-q>', lambda e: root.destroy())
1061
+
1062
+ table = Table(root, 'Word Synset Hypernym Hyponym'.split(),
1063
+ column_weights=[0, 1, 1, 1],
1064
+ reprfunc=(lambda i,j,s: ' %s' % s))
1065
+ table.pack(expand=True, fill='both')
1066
+
1067
+ from nltk.corpus import wordnet
1068
+ from nltk.corpus import brown
1069
+ for word, pos in sorted(set(brown.tagged_words()[:500])):
1070
+ if pos[0] != 'N': continue
1071
+ word = word.lower()
1072
+ for synset in wordnet.synsets(word):
1073
+ try:
1074
+ hyper_def = synset.hypernyms()[0].definition()
1075
+ except:
1076
+ hyper_def = '*none*'
1077
+ try:
1078
+ hypo_def = synset.hypernyms()[0].definition()
1079
+ except:
1080
+ hypo_def = '*none*'
1081
+ table.append([word,
1082
+ synset.definition(),
1083
+ hyper_def,
1084
+ hypo_def])
1085
+
1086
+ table.columnconfig('Word', background='#afa')
1087
+ table.columnconfig('Synset', background='#efe')
1088
+ table.columnconfig('Hypernym', background='#fee')
1089
+ table.columnconfig('Hyponym', background='#ffe')
1090
+ for row in range(len(table)):
1091
+ for column in ('Hypernym', 'Hyponym'):
1092
+ if table[row, column] == '*none*':
1093
+ table.itemconfig(row, column, foreground='#666',
1094
+ selectforeground='#666')
1095
+ root.mainloop()
1096
+
1097
+ if __name__ == '__main__':
1098
+ demo()