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,2356 +1,2356 @@
1
- # Natural Language Toolkit: Drawing utilities
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
- Tools for graphically displaying and interacting with the objects and
10
- processing classes defined by the Toolkit. These tools are primarily
11
- intended to help students visualize the objects that they create.
12
-
13
- The graphical tools are typically built using "canvas widgets", each
14
- of which encapsulates the graphical elements and bindings used to
15
- display a complex object on a Tkinter ``Canvas``. For example, NLTK
16
- defines canvas widgets for displaying trees and directed graphs, as
17
- well as a number of simpler widgets. These canvas widgets make it
18
- easier to build new graphical tools and demos. See the class
19
- documentation for ``CanvasWidget`` for more information.
20
-
21
- The ``nltk.draw`` module defines the abstract ``CanvasWidget`` base
22
- class, and a number of simple canvas widgets. The remaining canvas
23
- widgets are defined by submodules, such as ``nltk.draw.tree``.
24
-
25
- The ``nltk.draw`` module also defines ``CanvasFrame``, which
26
- encapsulates a ``Canvas`` and its scrollbars. It uses a
27
- ``ScrollWatcherWidget`` to ensure that all canvas widgets contained on
28
- its canvas are within the scroll region.
29
-
30
- Acknowledgements: Many of the ideas behind the canvas widget system
31
- are derived from ``CLIG``, a Tk-based grapher for linguistic data
32
- structures. For more information, see the CLIG
33
- homepage (http://www.ags.uni-sb.de/~konrad/clig.html).
34
-
35
- """
36
-
37
-
38
- import nltk.compat
39
- from tkinter import (Button, Canvas, Entry, Frame, Label, Menu, Menubutton,
40
- RAISED, Scrollbar, StringVar, Text, Tk, Toplevel, Widget)
41
-
42
- import tkinter.font, tkinter.messagebox, tkinter.filedialog
43
-
44
- from nltk.util import in_idle
45
-
46
- ##//////////////////////////////////////////////////////
47
- ## CanvasWidget
48
- ##//////////////////////////////////////////////////////
49
-
50
- class CanvasWidget(object):
51
- """
52
- A collection of graphical elements and bindings used to display a
53
- complex object on a Tkinter ``Canvas``. A canvas widget is
54
- responsible for managing the ``Canvas`` tags and callback bindings
55
- necessary to display and interact with the object. Canvas widgets
56
- are often organized into hierarchies, where parent canvas widgets
57
- control aspects of their child widgets.
58
-
59
- Each canvas widget is bound to a single ``Canvas``. This ``Canvas``
60
- is specified as the first argument to the ``CanvasWidget``'s
61
- constructor.
62
-
63
- Attributes. Each canvas widget can support a variety of
64
- "attributes", which control how the canvas widget is displayed.
65
- Some typical examples attributes are ``color``, ``font``, and
66
- ``radius``. Each attribute has a default value. This default
67
- value can be overridden in the constructor, using keyword
68
- arguments of the form ``attribute=value``:
69
-
70
- >>> from nltk.draw.util import TextWidget
71
- >>> cn = TextWidget(c, 'test', color='red')
72
-
73
- Attribute values can also be changed after a canvas widget has
74
- been constructed, using the ``__setitem__`` operator:
75
-
76
- >>> cn['font'] = 'times'
77
-
78
- The current value of an attribute value can be queried using the
79
- ``__getitem__`` operator:
80
-
81
- >>> cn['color']
82
- red
83
-
84
- For a list of the attributes supported by a type of canvas widget,
85
- see its class documentation.
86
-
87
- Interaction. The attribute ``'draggable'`` controls whether the
88
- user can drag a canvas widget around the canvas. By default,
89
- canvas widgets are not draggable.
90
-
91
- ``CanvasWidget`` provides callback support for two types of user
92
- interaction: clicking and dragging. The method ``bind_click``
93
- registers a callback function that is called whenever the canvas
94
- widget is clicked. The method ``bind_drag`` registers a callback
95
- function that is called after the canvas widget is dragged. If
96
- the user clicks or drags a canvas widget with no registered
97
- callback function, then the interaction event will propagate to
98
- its parent. For each canvas widget, only one callback function
99
- may be registered for an interaction event. Callback functions
100
- can be deregistered with the ``unbind_click`` and ``unbind_drag``
101
- methods.
102
-
103
- Subclassing. ``CanvasWidget`` is an abstract class. Subclasses
104
- are required to implement the following methods:
105
-
106
- - ``__init__``: Builds a new canvas widget. It must perform the
107
- following three tasks (in order):
108
- - Create any new graphical elements.
109
- - Call ``_add_child_widget`` on each child widget.
110
- - Call the ``CanvasWidget`` constructor.
111
- - ``_tags``: Returns a list of the canvas tags for all graphical
112
- elements managed by this canvas widget, not including
113
- graphical elements managed by its child widgets.
114
- - ``_manage``: Arranges the child widgets of this canvas widget.
115
- This is typically only called when the canvas widget is
116
- created.
117
- - ``_update``: Update this canvas widget in response to a
118
- change in a single child.
119
-
120
- For a ``CanvasWidget`` with no child widgets, the default
121
- definitions for ``_manage`` and ``_update`` may be used.
122
-
123
- If a subclass defines any attributes, then it should implement
124
- ``__getitem__`` and ``__setitem__``. If either of these methods is
125
- called with an unknown attribute, then they should propagate the
126
- request to ``CanvasWidget``.
127
-
128
- Most subclasses implement a number of additional methods that
129
- modify the ``CanvasWidget`` in some way. These methods must call
130
- ``parent.update(self)`` after making any changes to the canvas
131
- widget's graphical elements. The canvas widget must also call
132
- ``parent.update(self)`` after changing any attribute value that
133
- affects the shape or position of the canvas widget's graphical
134
- elements.
135
-
136
- :type __canvas: Tkinter.Canvas
137
- :ivar __canvas: This ``CanvasWidget``'s canvas.
138
-
139
- :type __parent: CanvasWidget or None
140
- :ivar __parent: This ``CanvasWidget``'s hierarchical parent widget.
141
- :type __children: list(CanvasWidget)
142
- :ivar __children: This ``CanvasWidget``'s hierarchical child widgets.
143
-
144
- :type __updating: bool
145
- :ivar __updating: Is this canvas widget currently performing an
146
- update? If it is, then it will ignore any new update requests
147
- from child widgets.
148
-
149
- :type __draggable: bool
150
- :ivar __draggable: Is this canvas widget draggable?
151
- :type __press: event
152
- :ivar __press: The ButtonPress event that we're currently handling.
153
- :type __drag_x: int
154
- :ivar __drag_x: Where it's been moved to (to find dx)
155
- :type __drag_y: int
156
- :ivar __drag_y: Where it's been moved to (to find dy)
157
- :type __callbacks: dictionary
158
- :ivar __callbacks: Registered callbacks. Currently, four keys are
159
- used: ``1``, ``2``, ``3``, and ``'drag'``. The values are
160
- callback functions. Each callback function takes a single
161
- argument, which is the ``CanvasWidget`` that triggered the
162
- callback.
163
- """
164
- def __init__(self, canvas, parent=None, **attribs):
165
- """
166
- Create a new canvas widget. This constructor should only be
167
- called by subclass constructors; and it should be called only
168
- "after" the subclass has constructed all graphical canvas
169
- objects and registered all child widgets.
170
-
171
- :param canvas: This canvas widget's canvas.
172
- :type canvas: Tkinter.Canvas
173
- :param parent: This canvas widget's hierarchical parent.
174
- :type parent: CanvasWidget
175
- :param attribs: The new canvas widget's attributes.
176
- """
177
- if self.__class__ == CanvasWidget:
178
- raise TypeError('CanvasWidget is an abstract base class')
179
-
180
- if not isinstance(canvas, Canvas):
181
- raise TypeError('Expected a canvas!')
182
-
183
- self.__canvas = canvas
184
- self.__parent = parent
185
-
186
- # If the subclass constructor called _add_child_widget, then
187
- # self.__children will already exist.
188
- if not hasattr(self, '_CanvasWidget__children'): self.__children = []
189
-
190
- # Is this widget hidden?
191
- self.__hidden = 0
192
-
193
- # Update control (prevents infinite loops)
194
- self.__updating = 0
195
-
196
- # Button-press and drag callback handling.
197
- self.__press = None
198
- self.__drag_x = self.__drag_y = 0
199
- self.__callbacks = {}
200
- self.__draggable = 0
201
-
202
- # Set up attributes.
203
- for (attr, value) in list(attribs.items()): self[attr] = value
204
-
205
- # Manage this canvas widget
206
- self._manage()
207
-
208
- # Register any new bindings
209
- for tag in self._tags():
210
- self.__canvas.tag_bind(tag, '<ButtonPress-1>',
211
- self.__press_cb)
212
- self.__canvas.tag_bind(tag, '<ButtonPress-2>',
213
- self.__press_cb)
214
- self.__canvas.tag_bind(tag, '<ButtonPress-3>',
215
- self.__press_cb)
216
-
217
- ##//////////////////////////////////////////////////////
218
- ## Inherited methods.
219
- ##//////////////////////////////////////////////////////
220
-
221
- def bbox(self):
222
- """
223
- :return: A bounding box for this ``CanvasWidget``. The bounding
224
- box is a tuple of four coordinates, *(xmin, ymin, xmax, ymax)*,
225
- for a rectangle which encloses all of the canvas
226
- widget's graphical elements. Bounding box coordinates are
227
- specified with respect to the coordinate space of the ``Canvas``.
228
- :rtype: tuple(int, int, int, int)
229
- """
230
- if self.__hidden: return (0,0,0,0)
231
- if len(self.tags()) == 0: raise ValueError('No tags')
232
- return self.__canvas.bbox(*self.tags())
233
-
234
- def width(self):
235
- """
236
- :return: The width of this canvas widget's bounding box, in
237
- its ``Canvas``'s coordinate space.
238
- :rtype: int
239
- """
240
- if len(self.tags()) == 0: raise ValueError('No tags')
241
- bbox = self.__canvas.bbox(*self.tags())
242
- return bbox[2]-bbox[0]
243
-
244
- def height(self):
245
- """
246
- :return: The height of this canvas widget's bounding box, in
247
- its ``Canvas``'s coordinate space.
248
- :rtype: int
249
- """
250
- if len(self.tags()) == 0: raise ValueError('No tags')
251
- bbox = self.__canvas.bbox(*self.tags())
252
- return bbox[3]-bbox[1]
253
-
254
- def parent(self):
255
- """
256
- :return: The hierarchical parent of this canvas widget.
257
- ``self`` is considered a subpart of its parent for
258
- purposes of user interaction.
259
- :rtype: CanvasWidget or None
260
- """
261
- return self.__parent
262
-
263
- def child_widgets(self):
264
- """
265
- :return: A list of the hierarchical children of this canvas
266
- widget. These children are considered part of ``self``
267
- for purposes of user interaction.
268
- :rtype: list of CanvasWidget
269
- """
270
- return self.__children
271
-
272
- def canvas(self):
273
- """
274
- :return: The canvas that this canvas widget is bound to.
275
- :rtype: Tkinter.Canvas
276
- """
277
- return self.__canvas
278
-
279
- def move(self, dx, dy):
280
- """
281
- Move this canvas widget by a given distance. In particular,
282
- shift the canvas widget right by ``dx`` pixels, and down by
283
- ``dy`` pixels. Both ``dx`` and ``dy`` may be negative, resulting
284
- in leftward or upward movement.
285
-
286
- :type dx: int
287
- :param dx: The number of pixels to move this canvas widget
288
- rightwards.
289
- :type dy: int
290
- :param dy: The number of pixels to move this canvas widget
291
- downwards.
292
- :rtype: None
293
- """
294
- if dx == dy == 0: return
295
- for tag in self.tags():
296
- self.__canvas.move(tag, dx, dy)
297
- if self.__parent: self.__parent.update(self)
298
-
299
- def moveto(self, x, y, anchor='NW'):
300
- """
301
- Move this canvas widget to the given location. In particular,
302
- shift the canvas widget such that the corner or side of the
303
- bounding box specified by ``anchor`` is at location (``x``,
304
- ``y``).
305
-
306
- :param x,y: The location that the canvas widget should be moved
307
- to.
308
- :param anchor: The corner or side of the canvas widget that
309
- should be moved to the specified location. ``'N'``
310
- specifies the top center; ``'NE'`` specifies the top right
311
- corner; etc.
312
- """
313
- x1,y1,x2,y2 = self.bbox()
314
- if anchor == 'NW': self.move(x-x1, y-y1)
315
- if anchor == 'N': self.move(x-x1/2-x2/2, y-y1)
316
- if anchor == 'NE': self.move(x-x2, y-y1)
317
- if anchor == 'E': self.move(x-x2, y-y1/2-y2/2)
318
- if anchor == 'SE': self.move(x-x2, y-y2)
319
- if anchor == 'S': self.move(x-x1/2-x2/2, y-y2)
320
- if anchor == 'SW': self.move(x-x1, y-y2)
321
- if anchor == 'W': self.move(x-x1, y-y1/2-y2/2)
322
-
323
- def destroy(self):
324
- """
325
- Remove this ``CanvasWidget`` from its ``Canvas``. After a
326
- ``CanvasWidget`` has been destroyed, it should not be accessed.
327
-
328
- Note that you only need to destroy a top-level
329
- ``CanvasWidget``; its child widgets will be destroyed
330
- automatically. If you destroy a non-top-level
331
- ``CanvasWidget``, then the entire top-level widget will be
332
- destroyed.
333
-
334
- :raise ValueError: if this ``CanvasWidget`` has a parent.
335
- :rtype: None
336
- """
337
- if self.__parent is not None:
338
- self.__parent.destroy()
339
- return
340
-
341
- for tag in self.tags():
342
- self.__canvas.tag_unbind(tag, '<ButtonPress-1>')
343
- self.__canvas.tag_unbind(tag, '<ButtonPress-2>')
344
- self.__canvas.tag_unbind(tag, '<ButtonPress-3>')
345
- self.__canvas.delete(*self.tags())
346
- self.__canvas = None
347
-
348
- def update(self, child):
349
- """
350
- Update the graphical display of this canvas widget, and all of
351
- its ancestors, in response to a change in one of this canvas
352
- widget's children.
353
-
354
- :param child: The child widget that changed.
355
- :type child: CanvasWidget
356
- """
357
- if self.__hidden or child.__hidden: return
358
- # If we're already updating, then do nothing. This prevents
359
- # infinite loops when _update modifies its children.
360
- if self.__updating: return
361
- self.__updating = 1
362
-
363
- # Update this CanvasWidget.
364
- self._update(child)
365
-
366
- # Propagate update request to the parent.
367
- if self.__parent: self.__parent.update(self)
368
-
369
- # We're done updating.
370
- self.__updating = 0
371
-
372
- def manage(self):
373
- """
374
- Arrange this canvas widget and all of its descendants.
375
-
376
- :rtype: None
377
- """
378
- if self.__hidden: return
379
- for child in self.__children: child.manage()
380
- self._manage()
381
-
382
- def tags(self):
383
- """
384
- :return: a list of the canvas tags for all graphical
385
- elements managed by this canvas widget, including
386
- graphical elements managed by its child widgets.
387
- :rtype: list of int
388
- """
389
- if self.__canvas is None:
390
- raise ValueError('Attempt to access a destroyed canvas widget')
391
- tags = []
392
- tags += self._tags()
393
- for child in self.__children:
394
- tags += child.tags()
395
- return tags
396
-
397
- def __setitem__(self, attr, value):
398
- """
399
- Set the value of the attribute ``attr`` to ``value``. See the
400
- class documentation for a list of attributes supported by this
401
- canvas widget.
402
-
403
- :rtype: None
404
- """
405
- if attr == 'draggable':
406
- self.__draggable = value
407
- else:
408
- raise ValueError('Unknown attribute %r' % attr)
409
-
410
- def __getitem__(self, attr):
411
- """
412
- :return: the value of the attribute ``attr``. See the class
413
- documentation for a list of attributes supported by this
414
- canvas widget.
415
- :rtype: (any)
416
- """
417
- if attr == 'draggable':
418
- return self.__draggable
419
- else:
420
- raise ValueError('Unknown attribute %r' % attr)
421
-
422
- def __repr__(self):
423
- """
424
- :return: a string representation of this canvas widget.
425
- :rtype: str
426
- """
427
- return '<%s>' % self.__class__.__name__
428
-
429
- def hide(self):
430
- """
431
- Temporarily hide this canvas widget.
432
-
433
- :rtype: None
434
- """
435
- self.__hidden = 1
436
- for tag in self.tags():
437
- self.__canvas.itemconfig(tag, state='hidden')
438
-
439
- def show(self):
440
- """
441
- Show a hidden canvas widget.
442
-
443
- :rtype: None
444
- """
445
- self.__hidden = 0
446
- for tag in self.tags():
447
- self.__canvas.itemconfig(tag, state='normal')
448
-
449
- def hidden(self):
450
- """
451
- :return: True if this canvas widget is hidden.
452
- :rtype: bool
453
- """
454
- return self.__hidden
455
-
456
- ##//////////////////////////////////////////////////////
457
- ## Callback interface
458
- ##//////////////////////////////////////////////////////
459
-
460
- def bind_click(self, callback, button=1):
461
- """
462
- Register a new callback that will be called whenever this
463
- ``CanvasWidget`` is clicked on.
464
-
465
- :type callback: function
466
- :param callback: The callback function that will be called
467
- whenever this ``CanvasWidget`` is clicked. This function
468
- will be called with this ``CanvasWidget`` as its argument.
469
- :type button: int
470
- :param button: Which button the user should use to click on
471
- this ``CanvasWidget``. Typically, this should be 1 (left
472
- button), 3 (right button), or 2 (middle button).
473
- """
474
- self.__callbacks[button] = callback
475
-
476
- def bind_drag(self, callback):
477
- """
478
- Register a new callback that will be called after this
479
- ``CanvasWidget`` is dragged. This implicitly makes this
480
- ``CanvasWidget`` draggable.
481
-
482
- :type callback: function
483
- :param callback: The callback function that will be called
484
- whenever this ``CanvasWidget`` is clicked. This function
485
- will be called with this ``CanvasWidget`` as its argument.
486
- """
487
- self.__draggable = 1
488
- self.__callbacks['drag'] = callback
489
-
490
- def unbind_click(self, button=1):
491
- """
492
- Remove a callback that was registered with ``bind_click``.
493
-
494
- :type button: int
495
- :param button: Which button the user should use to click on
496
- this ``CanvasWidget``. Typically, this should be 1 (left
497
- button), 3 (right button), or 2 (middle button).
498
- """
499
- try: del self.__callbacks[button]
500
- except: pass
501
-
502
- def unbind_drag(self):
503
- """
504
- Remove a callback that was registered with ``bind_drag``.
505
- """
506
- try: del self.__callbacks['drag']
507
- except: pass
508
-
509
- ##//////////////////////////////////////////////////////
510
- ## Callback internals
511
- ##//////////////////////////////////////////////////////
512
-
513
- def __press_cb(self, event):
514
- """
515
- Handle a button-press event:
516
- - record the button press event in ``self.__press``
517
- - register a button-release callback.
518
- - if this CanvasWidget or any of its ancestors are
519
- draggable, then register the appropriate motion callback.
520
- """
521
- # If we're already waiting for a button release, then ignore
522
- # this new button press.
523
- if (self.__canvas.bind('<ButtonRelease-1>') or
524
- self.__canvas.bind('<ButtonRelease-2>') or
525
- self.__canvas.bind('<ButtonRelease-3>')):
526
- return
527
-
528
- # Unbind motion (just in case; this shouldn't be necessary)
529
- self.__canvas.unbind('<Motion>')
530
-
531
- # Record the button press event.
532
- self.__press = event
533
-
534
- # If any ancestor is draggable, set up a motion callback.
535
- # (Only if they pressed button number 1)
536
- if event.num == 1:
537
- widget = self
538
- while widget is not None:
539
- if widget['draggable']:
540
- widget.__start_drag(event)
541
- break
542
- widget = widget.parent()
543
-
544
- # Set up the button release callback.
545
- self.__canvas.bind('<ButtonRelease-%d>' % event.num,
546
- self.__release_cb)
547
-
548
- def __start_drag(self, event):
549
- """
550
- Begin dragging this object:
551
- - register a motion callback
552
- - record the drag coordinates
553
- """
554
- self.__canvas.bind('<Motion>', self.__motion_cb)
555
- self.__drag_x = event.x
556
- self.__drag_y = event.y
557
-
558
- def __motion_cb(self, event):
559
- """
560
- Handle a motion event:
561
- - move this object to the new location
562
- - record the new drag coordinates
563
- """
564
- self.move(event.x-self.__drag_x, event.y-self.__drag_y)
565
- self.__drag_x = event.x
566
- self.__drag_y = event.y
567
-
568
- def __release_cb(self, event):
569
- """
570
- Handle a release callback:
571
- - unregister motion & button release callbacks.
572
- - decide whether they clicked, dragged, or cancelled
573
- - call the appropriate handler.
574
- """
575
- # Unbind the button release & motion callbacks.
576
- self.__canvas.unbind('<ButtonRelease-%d>' % event.num)
577
- self.__canvas.unbind('<Motion>')
578
-
579
- # Is it a click or a drag?
580
- if (event.time - self.__press.time < 100 and
581
- abs(event.x-self.__press.x) + abs(event.y-self.__press.y) < 5):
582
- # Move it back, if we were dragging.
583
- if self.__draggable and event.num == 1:
584
- self.move(self.__press.x - self.__drag_x,
585
- self.__press.y - self.__drag_y)
586
- self.__click(event.num)
587
- elif event.num == 1:
588
- self.__drag()
589
-
590
- self.__press = None
591
-
592
- def __drag(self):
593
- """
594
- If this ``CanvasWidget`` has a drag callback, then call it;
595
- otherwise, find the closest ancestor with a drag callback, and
596
- call it. If no ancestors have a drag callback, do nothing.
597
- """
598
- if self.__draggable:
599
- if 'drag' in self.__callbacks:
600
- cb = self.__callbacks['drag']
601
- try:
602
- cb(self)
603
- except:
604
- print('Error in drag callback for %r' % self)
605
- elif self.__parent is not None:
606
- self.__parent.__drag()
607
-
608
- def __click(self, button):
609
- """
610
- If this ``CanvasWidget`` has a drag callback, then call it;
611
- otherwise, find the closest ancestor with a click callback, and
612
- call it. If no ancestors have a click callback, do nothing.
613
- """
614
- if button in self.__callbacks:
615
- cb = self.__callbacks[button]
616
- #try:
617
- cb(self)
618
- #except:
619
- # print 'Error in click callback for %r' % self
620
- # raise
621
- elif self.__parent is not None:
622
- self.__parent.__click(button)
623
-
624
- ##//////////////////////////////////////////////////////
625
- ## Child/parent Handling
626
- ##//////////////////////////////////////////////////////
627
-
628
- def _add_child_widget(self, child):
629
- """
630
- Register a hierarchical child widget. The child will be
631
- considered part of this canvas widget for purposes of user
632
- interaction. ``_add_child_widget`` has two direct effects:
633
- - It sets ``child``'s parent to this canvas widget.
634
- - It adds ``child`` to the list of canvas widgets returned by
635
- the ``child_widgets`` member function.
636
-
637
- :param child: The new child widget. ``child`` must not already
638
- have a parent.
639
- :type child: CanvasWidget
640
- """
641
- if not hasattr(self, '_CanvasWidget__children'): self.__children = []
642
- if child.__parent is not None:
643
- raise ValueError('%s already has a parent', child)
644
- child.__parent = self
645
- self.__children.append(child)
646
-
647
- def _remove_child_widget(self, child):
648
- """
649
- Remove a hierarchical child widget. This child will no longer
650
- be considered part of this canvas widget for purposes of user
651
- interaction. ``_add_child_widget`` has two direct effects:
652
- - It sets ``child``'s parent to None.
653
- - It removes ``child`` from the list of canvas widgets
654
- returned by the ``child_widgets`` member function.
655
-
656
- :param child: The child widget to remove. ``child`` must be a
657
- child of this canvas widget.
658
- :type child: CanvasWidget
659
- """
660
- self.__children.remove(child)
661
- child.__parent = None
662
-
663
- ##//////////////////////////////////////////////////////
664
- ## Defined by subclass
665
- ##//////////////////////////////////////////////////////
666
-
667
- def _tags(self):
668
- """
669
- :return: a list of canvas tags for all graphical elements
670
- managed by this canvas widget, not including graphical
671
- elements managed by its child widgets.
672
- :rtype: list of int
673
- """
674
- raise NotImplementedError()
675
-
676
- def _manage(self):
677
- """
678
- Arrange the child widgets of this canvas widget. This method
679
- is called when the canvas widget is initially created. It is
680
- also called if the user calls the ``manage`` method on this
681
- canvas widget or any of its ancestors.
682
-
683
- :rtype: None
684
- """
685
- pass
686
-
687
- def _update(self, child):
688
- """
689
- Update this canvas widget in response to a change in one of
690
- its children.
691
-
692
- :param child: The child that changed.
693
- :type child: CanvasWidget
694
- :rtype: None
695
- """
696
- pass
697
-
698
- ##//////////////////////////////////////////////////////
699
- ## Basic widgets.
700
- ##//////////////////////////////////////////////////////
701
-
702
- class TextWidget(CanvasWidget):
703
- """
704
- A canvas widget that displays a single string of text.
705
-
706
- Attributes:
707
- - ``color``: the color of the text.
708
- - ``font``: the font used to display the text.
709
- - ``justify``: justification for multi-line texts. Valid values
710
- are ``left``, ``center``, and ``right``.
711
- - ``width``: the width of the text. If the text is wider than
712
- this width, it will be line-wrapped at whitespace.
713
- - ``draggable``: whether the text can be dragged by the user.
714
- """
715
- def __init__(self, canvas, text, **attribs):
716
- """
717
- Create a new text widget.
718
-
719
- :type canvas: Tkinter.Canvas
720
- :param canvas: This canvas widget's canvas.
721
- :type text: str
722
- :param text: The string of text to display.
723
- :param attribs: The new canvas widget's attributes.
724
- """
725
- self._text = text
726
- self._tag = canvas.create_text(1, 1, text=text)
727
- CanvasWidget.__init__(self, canvas, **attribs)
728
-
729
- def __setitem__(self, attr, value):
730
- if attr in ('color', 'font', 'justify', 'width'):
731
- if attr == 'color': attr = 'fill'
732
- self.canvas().itemconfig(self._tag, {attr:value})
733
- else:
734
- CanvasWidget.__setitem__(self, attr, value)
735
-
736
- def __getitem__(self, attr):
737
- if attr == 'width':
738
- return int(self.canvas().itemcget(self._tag, attr))
739
- elif attr in ('color', 'font', 'justify'):
740
- if attr == 'color': attr = 'fill'
741
- return self.canvas().itemcget(self._tag, attr)
742
- else:
743
- return CanvasWidget.__getitem__(self, attr)
744
-
745
- def _tags(self): return [self._tag]
746
-
747
- def text(self):
748
- """
749
- :return: The text displayed by this text widget.
750
- :rtype: str
751
- """
752
- return self.canvas().itemcget(self._tag, 'TEXT')
753
-
754
- def set_text(self, text):
755
- """
756
- Change the text that is displayed by this text widget.
757
-
758
- :type text: str
759
- :param text: The string of text to display.
760
- :rtype: None
761
- """
762
- self.canvas().itemconfig(self._tag, text=text)
763
- if self.parent() is not None:
764
- self.parent().update(self)
765
-
766
- def __repr__(self):
767
- return '[Text: %r]' % self._text
768
-
769
- class SymbolWidget(TextWidget):
770
- """
771
- A canvas widget that displays special symbols, such as the
772
- negation sign and the exists operator. Symbols are specified by
773
- name. Currently, the following symbol names are defined: ``neg``,
774
- ``disj``, ``conj``, ``lambda``, ``merge``, ``forall``, ``exists``,
775
- ``subseteq``, ``subset``, ``notsubset``, ``emptyset``, ``imp``,
776
- ``rightarrow``, ``equal``, ``notequal``, ``epsilon``.
777
-
778
- Attributes:
779
-
780
- - ``color``: the color of the text.
781
- - ``draggable``: whether the text can be dragged by the user.
782
-
783
- :cvar SYMBOLS: A dictionary mapping from symbols to the character
784
- in the ``symbol`` font used to render them.
785
- """
786
- SYMBOLS = {'neg':'\330', 'disj':'\332', 'conj': '\331',
787
- 'lambda': '\154', 'merge': '\304',
788
- 'forall': '\042', 'exists': '\044',
789
- 'subseteq': '\315', 'subset': '\314',
790
- 'notsubset': '\313', 'emptyset': '\306',
791
- 'imp': '\336', 'rightarrow': chr(222), #'\256',
792
- 'equal': '\75', 'notequal': '\271',
793
- 'intersection': '\307', 'union': '\310',
794
- 'epsilon': 'e',
795
- }
796
-
797
- def __init__(self, canvas, symbol, **attribs):
798
- """
799
- Create a new symbol widget.
800
-
801
- :type canvas: Tkinter.Canvas
802
- :param canvas: This canvas widget's canvas.
803
- :type symbol: str
804
- :param symbol: The name of the symbol to display.
805
- :param attribs: The new canvas widget's attributes.
806
- """
807
- attribs['font'] = 'symbol'
808
- TextWidget.__init__(self, canvas, '', **attribs)
809
- self.set_symbol(symbol)
810
-
811
- def symbol(self):
812
- """
813
- :return: the name of the symbol that is displayed by this
814
- symbol widget.
815
- :rtype: str
816
- """
817
- return self._symbol
818
-
819
- def set_symbol(self, symbol):
820
- """
821
- Change the symbol that is displayed by this symbol widget.
822
-
823
- :type symbol: str
824
- :param symbol: The name of the symbol to display.
825
- """
826
- if symbol not in SymbolWidget.SYMBOLS:
827
- raise ValueError('Unknown symbol: %s' % symbol)
828
- self._symbol = symbol
829
- self.set_text(SymbolWidget.SYMBOLS[symbol])
830
-
831
- def __repr__(self):
832
- return '[Symbol: %r]' % self._symbol
833
-
834
- @staticmethod
835
- def symbolsheet(size=20):
836
- """
837
- Open a new Tkinter window that displays the entire alphabet
838
- for the symbol font. This is useful for constructing the
839
- ``SymbolWidget.SYMBOLS`` dictionary.
840
- """
841
- top = Tk()
842
- def destroy(e, top=top): top.destroy()
843
- top.bind('q', destroy)
844
- Button(top, text='Quit', command=top.destroy).pack(side='bottom')
845
- text = Text(top, font=('helvetica', -size), width=20, height=30)
846
- text.pack(side='left')
847
- sb=Scrollbar(top, command=text.yview)
848
- text['yscrollcommand']=sb.set
849
- sb.pack(side='right', fill='y')
850
- text.tag_config('symbol', font=('symbol', -size))
851
- for i in range(256):
852
- if i in (0,10): continue # null and newline
853
- for k,v in list(SymbolWidget.SYMBOLS.items()):
854
- if v == chr(i):
855
- text.insert('end', '%-10s\t' % k)
856
- break
857
- else:
858
- text.insert('end', '%-10d \t' % i)
859
- text.insert('end', '[%s]\n' % chr(i), 'symbol')
860
- top.mainloop()
861
-
862
-
863
- class AbstractContainerWidget(CanvasWidget):
864
- """
865
- An abstract class for canvas widgets that contain a single child,
866
- such as ``BoxWidget`` and ``OvalWidget``. Subclasses must define
867
- a constructor, which should create any new graphical elements and
868
- then call the ``AbstractCanvasContainer`` constructor. Subclasses
869
- must also define the ``_update`` method and the ``_tags`` method;
870
- and any subclasses that define attributes should define
871
- ``__setitem__`` and ``__getitem__``.
872
- """
873
- def __init__(self, canvas, child, **attribs):
874
- """
875
- Create a new container widget. This constructor should only
876
- be called by subclass constructors.
877
-
878
- :type canvas: Tkinter.Canvas
879
- :param canvas: This canvas widget's canvas.
880
- :param child: The container's child widget. ``child`` must not
881
- have a parent.
882
- :type child: CanvasWidget
883
- :param attribs: The new canvas widget's attributes.
884
- """
885
- self._child = child
886
- self._add_child_widget(child)
887
- CanvasWidget.__init__(self, canvas, **attribs)
888
-
889
- def _manage(self):
890
- self._update(self._child)
891
-
892
- def child(self):
893
- """
894
- :return: The child widget contained by this container widget.
895
- :rtype: CanvasWidget
896
- """
897
- return self._child
898
-
899
- def set_child(self, child):
900
- """
901
- Change the child widget contained by this container widget.
902
-
903
- :param child: The new child widget. ``child`` must not have a
904
- parent.
905
- :type child: CanvasWidget
906
- :rtype: None
907
- """
908
- self._remove_child_widget(self._child)
909
- self._add_child_widget(child)
910
- self._child = child
911
- self.update(child)
912
-
913
- def __repr__(self):
914
- name = self.__class__.__name__
915
- if name[-6:] == 'Widget': name = name[:-6]
916
- return '[%s: %r]' % (name, self._child)
917
-
918
- class BoxWidget(AbstractContainerWidget):
919
- """
920
- A canvas widget that places a box around a child widget.
921
-
922
- Attributes:
923
- - ``fill``: The color used to fill the interior of the box.
924
- - ``outline``: The color used to draw the outline of the box.
925
- - ``width``: The width of the outline of the box.
926
- - ``margin``: The number of pixels space left between the child
927
- and the box.
928
- - ``draggable``: whether the text can be dragged by the user.
929
- """
930
- def __init__(self, canvas, child, **attribs):
931
- """
932
- Create a new box widget.
933
-
934
- :type canvas: Tkinter.Canvas
935
- :param canvas: This canvas widget's canvas.
936
- :param child: The child widget. ``child`` must not have a
937
- parent.
938
- :type child: CanvasWidget
939
- :param attribs: The new canvas widget's attributes.
940
- """
941
- self._child = child
942
- self._margin = 1
943
- self._box = canvas.create_rectangle(1,1,1,1)
944
- canvas.tag_lower(self._box)
945
- AbstractContainerWidget.__init__(self, canvas, child, **attribs)
946
-
947
- def __setitem__(self, attr, value):
948
- if attr == 'margin': self._margin = value
949
- elif attr in ('outline', 'fill', 'width'):
950
- self.canvas().itemconfig(self._box, {attr:value})
951
- else:
952
- CanvasWidget.__setitem__(self, attr, value)
953
-
954
- def __getitem__(self, attr):
955
- if attr == 'margin': return self._margin
956
- elif attr == 'width':
957
- return float(self.canvas().itemcget(self._box, attr))
958
- elif attr in ('outline', 'fill', 'width'):
959
- return self.canvas().itemcget(self._box, attr)
960
- else:
961
- return CanvasWidget.__getitem__(self, attr)
962
-
963
- def _update(self, child):
964
- (x1, y1, x2, y2) = child.bbox()
965
- margin = self._margin + self['width']/2
966
- self.canvas().coords(self._box, x1-margin, y1-margin,
967
- x2+margin, y2+margin)
968
-
969
- def _tags(self): return [self._box]
970
-
971
- class OvalWidget(AbstractContainerWidget):
972
- """
973
- A canvas widget that places a oval around a child widget.
974
-
975
- Attributes:
976
- - ``fill``: The color used to fill the interior of the oval.
977
- - ``outline``: The color used to draw the outline of the oval.
978
- - ``width``: The width of the outline of the oval.
979
- - ``margin``: The number of pixels space left between the child
980
- and the oval.
981
- - ``draggable``: whether the text can be dragged by the user.
982
- - ``double``: If true, then a double-oval is drawn.
983
- """
984
- def __init__(self, canvas, child, **attribs):
985
- """
986
- Create a new oval widget.
987
-
988
- :type canvas: Tkinter.Canvas
989
- :param canvas: This canvas widget's canvas.
990
- :param child: The child widget. ``child`` must not have a
991
- parent.
992
- :type child: CanvasWidget
993
- :param attribs: The new canvas widget's attributes.
994
- """
995
- self._child = child
996
- self._margin = 1
997
- self._oval = canvas.create_oval(1,1,1,1)
998
- self._circle = attribs.pop('circle', False)
999
- self._double = attribs.pop('double', False)
1000
- if self._double:
1001
- self._oval2 = canvas.create_oval(1,1,1,1)
1002
- else:
1003
- self._oval2 = None
1004
- canvas.tag_lower(self._oval)
1005
- AbstractContainerWidget.__init__(self, canvas, child, **attribs)
1006
-
1007
- def __setitem__(self, attr, value):
1008
- c = self.canvas()
1009
- if attr == 'margin': self._margin = value
1010
- elif attr == 'double':
1011
- if value==True and self._oval2 is None:
1012
- # Copy attributes & position from self._oval.
1013
- x1, y1, x2, y2 = c.bbox(self._oval)
1014
- w = self['width']*2
1015
- self._oval2 = c.create_oval(x1-w, y1-w, x2+w, y2+w,
1016
- outline=c.itemcget(self._oval, 'outline'),
1017
- width=c.itemcget(self._oval, 'width'))
1018
- c.tag_lower(self._oval2)
1019
- if value==False and self._oval2 is not None:
1020
- c.delete(self._oval2)
1021
- self._oval2 = None
1022
- elif attr in ('outline', 'fill', 'width'):
1023
- c.itemconfig(self._oval, {attr:value})
1024
- if self._oval2 is not None and attr!='fill':
1025
- c.itemconfig(self._oval2, {attr:value})
1026
- if self._oval2 is not None and attr!='fill':
1027
- self.canvas().itemconfig(self._oval2, {attr:value})
1028
- else:
1029
- CanvasWidget.__setitem__(self, attr, value)
1030
-
1031
- def __getitem__(self, attr):
1032
- if attr == 'margin': return self._margin
1033
- elif attr == 'double': return self._double is not None
1034
- elif attr == 'width':
1035
- return float(self.canvas().itemcget(self._oval, attr))
1036
- elif attr in ('outline', 'fill', 'width'):
1037
- return self.canvas().itemcget(self._oval, attr)
1038
- else:
1039
- return CanvasWidget.__getitem__(self, attr)
1040
-
1041
- # The ratio between inscribed & circumscribed ovals
1042
- RATIO = 1.4142135623730949
1043
-
1044
- def _update(self, child):
1045
- R = OvalWidget.RATIO
1046
- (x1, y1, x2, y2) = child.bbox()
1047
- margin = self._margin
1048
-
1049
- # If we're a circle, pretend our contents are square.
1050
- if self._circle:
1051
- dx, dy = abs(x1-x2), abs(y1-y2)
1052
- if dx > dy:
1053
- y = (y1+y2)/2
1054
- y1, y2 = y-dx/2, y+dx/2
1055
- elif dy > dx:
1056
- x = (x1+x2)/2
1057
- x1, x2 = x-dy/2, x+dy/2
1058
-
1059
- # Find the four corners.
1060
- left = int(( x1*(1+R) + x2*(1-R) ) / 2)
1061
- right = left + int((x2-x1)*R)
1062
- top = int(( y1*(1+R) + y2*(1-R) ) / 2)
1063
- bot = top + int((y2-y1)*R)
1064
- self.canvas().coords(self._oval, left-margin, top-margin,
1065
- right+margin, bot+margin)
1066
- if self._oval2 is not None:
1067
- self.canvas().coords(self._oval2, left-margin+2, top-margin+2,
1068
- right+margin-2, bot+margin-2)
1069
-
1070
- def _tags(self):
1071
- if self._oval2 is None:
1072
- return [self._oval]
1073
- else:
1074
- return [self._oval, self._oval2]
1075
-
1076
- class ParenWidget(AbstractContainerWidget):
1077
- """
1078
- A canvas widget that places a pair of parenthases around a child
1079
- widget.
1080
-
1081
- Attributes:
1082
- - ``color``: The color used to draw the parenthases.
1083
- - ``width``: The width of the parenthases.
1084
- - ``draggable``: whether the text can be dragged by the user.
1085
- """
1086
- def __init__(self, canvas, child, **attribs):
1087
- """
1088
- Create a new parenthasis widget.
1089
-
1090
- :type canvas: Tkinter.Canvas
1091
- :param canvas: This canvas widget's canvas.
1092
- :param child: The child widget. ``child`` must not have a
1093
- parent.
1094
- :type child: CanvasWidget
1095
- :param attribs: The new canvas widget's attributes.
1096
- """
1097
- self._child = child
1098
- self._oparen = canvas.create_arc(1,1,1,1, style='arc',
1099
- start=90, extent=180)
1100
- self._cparen = canvas.create_arc(1,1,1,1, style='arc',
1101
- start=-90, extent=180)
1102
- AbstractContainerWidget.__init__(self, canvas, child, **attribs)
1103
-
1104
- def __setitem__(self, attr, value):
1105
- if attr == 'color':
1106
- self.canvas().itemconfig(self._oparen, outline=value)
1107
- self.canvas().itemconfig(self._cparen, outline=value)
1108
- elif attr == 'width':
1109
- self.canvas().itemconfig(self._oparen, width=value)
1110
- self.canvas().itemconfig(self._cparen, width=value)
1111
- else:
1112
- CanvasWidget.__setitem__(self, attr, value)
1113
-
1114
- def __getitem__(self, attr):
1115
- if attr == 'color':
1116
- return self.canvas().itemcget(self._oparen, 'outline')
1117
- elif attr == 'width':
1118
- return self.canvas().itemcget(self._oparen, 'width')
1119
- else:
1120
- return CanvasWidget.__getitem__(self, attr)
1121
-
1122
- def _update(self, child):
1123
- (x1, y1, x2, y2) = child.bbox()
1124
- width = max((y2-y1)/6, 4)
1125
- self.canvas().coords(self._oparen, x1-width, y1, x1+width, y2)
1126
- self.canvas().coords(self._cparen, x2-width, y1, x2+width, y2)
1127
-
1128
- def _tags(self): return [self._oparen, self._cparen]
1129
-
1130
- class BracketWidget(AbstractContainerWidget):
1131
- """
1132
- A canvas widget that places a pair of brackets around a child
1133
- widget.
1134
-
1135
- Attributes:
1136
- - ``color``: The color used to draw the brackets.
1137
- - ``width``: The width of the brackets.
1138
- - ``draggable``: whether the text can be dragged by the user.
1139
- """
1140
- def __init__(self, canvas, child, **attribs):
1141
- """
1142
- Create a new bracket widget.
1143
-
1144
- :type canvas: Tkinter.Canvas
1145
- :param canvas: This canvas widget's canvas.
1146
- :param child: The child widget. ``child`` must not have a
1147
- parent.
1148
- :type child: CanvasWidget
1149
- :param attribs: The new canvas widget's attributes.
1150
- """
1151
- self._child = child
1152
- self._obrack = canvas.create_line(1,1,1,1,1,1,1,1)
1153
- self._cbrack = canvas.create_line(1,1,1,1,1,1,1,1)
1154
- AbstractContainerWidget.__init__(self, canvas, child, **attribs)
1155
-
1156
- def __setitem__(self, attr, value):
1157
- if attr == 'color':
1158
- self.canvas().itemconfig(self._obrack, fill=value)
1159
- self.canvas().itemconfig(self._cbrack, fill=value)
1160
- elif attr == 'width':
1161
- self.canvas().itemconfig(self._obrack, width=value)
1162
- self.canvas().itemconfig(self._cbrack, width=value)
1163
- else:
1164
- CanvasWidget.__setitem__(self, attr, value)
1165
-
1166
- def __getitem__(self, attr):
1167
- if attr == 'color':
1168
- return self.canvas().itemcget(self._obrack, 'outline')
1169
- elif attr == 'width':
1170
- return self.canvas().itemcget(self._obrack, 'width')
1171
- else:
1172
- return CanvasWidget.__getitem__(self, attr)
1173
-
1174
- def _update(self, child):
1175
- (x1, y1, x2, y2) = child.bbox()
1176
- width = max((y2-y1)/8, 2)
1177
- self.canvas().coords(self._obrack, x1, y1, x1-width, y1,
1178
- x1-width, y2, x1, y2)
1179
- self.canvas().coords(self._cbrack, x2, y1, x2+width, y1,
1180
- x2+width, y2, x2, y2)
1181
-
1182
- def _tags(self): return [self._obrack, self._cbrack]
1183
-
1184
- class SequenceWidget(CanvasWidget):
1185
- """
1186
- A canvas widget that keeps a list of canvas widgets in a
1187
- horizontal line.
1188
-
1189
- Attributes:
1190
- - ``align``: The vertical alignment of the children. Possible
1191
- values are ``'top'``, ``'center'``, and ``'bottom'``. By
1192
- default, children are center-aligned.
1193
- - ``space``: The amount of horizontal space to place between
1194
- children. By default, one pixel of space is used.
1195
- - ``ordered``: If true, then keep the children in their
1196
- original order.
1197
- """
1198
- def __init__(self, canvas, *children, **attribs):
1199
- """
1200
- Create a new sequence widget.
1201
-
1202
- :type canvas: Tkinter.Canvas
1203
- :param canvas: This canvas widget's canvas.
1204
- :param children: The widgets that should be aligned
1205
- horizontally. Each child must not have a parent.
1206
- :type children: list(CanvasWidget)
1207
- :param attribs: The new canvas widget's attributes.
1208
- """
1209
- self._align = 'center'
1210
- self._space = 1
1211
- self._ordered = False
1212
- self._children = list(children)
1213
- for child in children: self._add_child_widget(child)
1214
- CanvasWidget.__init__(self, canvas, **attribs)
1215
-
1216
- def __setitem__(self, attr, value):
1217
- if attr == 'align':
1218
- if value not in ('top', 'bottom', 'center'):
1219
- raise ValueError('Bad alignment: %r' % value)
1220
- self._align = value
1221
- elif attr == 'space': self._space = value
1222
- elif attr == 'ordered': self._ordered = value
1223
- else: CanvasWidget.__setitem__(self, attr, value)
1224
-
1225
- def __getitem__(self, attr):
1226
- if attr == 'align': return self._align
1227
- elif attr == 'space': return self._space
1228
- elif attr == 'ordered': return self._ordered
1229
- else: return CanvasWidget.__getitem__(self, attr)
1230
-
1231
- def _tags(self): return []
1232
-
1233
- def _yalign(self, top, bot):
1234
- if self._align == 'top': return top
1235
- if self._align == 'bottom': return bot
1236
- if self._align == 'center': return (top+bot)/2
1237
-
1238
- def _update(self, child):
1239
- # Align all children with child.
1240
- (left, top, right, bot) = child.bbox()
1241
- y = self._yalign(top, bot)
1242
- for c in self._children:
1243
- (x1, y1, x2, y2) = c.bbox()
1244
- c.move(0, y-self._yalign(y1,y2))
1245
-
1246
- if self._ordered and len(self._children) > 1:
1247
- index = self._children.index(child)
1248
-
1249
- x = right + self._space
1250
- for i in range(index+1, len(self._children)):
1251
- (x1, y1, x2, y2) = self._children[i].bbox()
1252
- if x > x1:
1253
- self._children[i].move(x-x1, 0)
1254
- x += x2-x1 + self._space
1255
-
1256
- x = left - self._space
1257
- for i in range(index-1, -1, -1):
1258
- (x1, y1, x2, y2) = self._children[i].bbox()
1259
- if x < x2:
1260
- self._children[i].move(x-x2, 0)
1261
- x -= x2-x1 + self._space
1262
-
1263
- def _manage(self):
1264
- if len(self._children) == 0: return
1265
- child = self._children[0]
1266
-
1267
- # Align all children with child.
1268
- (left, top, right, bot) = child.bbox()
1269
- y = self._yalign(top, bot)
1270
-
1271
- index = self._children.index(child)
1272
-
1273
- # Line up children to the right of child.
1274
- x = right + self._space
1275
- for i in range(index+1, len(self._children)):
1276
- (x1, y1, x2, y2) = self._children[i].bbox()
1277
- self._children[i].move(x-x1, y-self._yalign(y1,y2))
1278
- x += x2-x1 + self._space
1279
-
1280
- # Line up children to the left of child.
1281
- x = left - self._space
1282
- for i in range(index-1, -1, -1):
1283
- (x1, y1, x2, y2) = self._children[i].bbox()
1284
- self._children[i].move(x-x2, y-self._yalign(y1,y2))
1285
- x -= x2-x1 + self._space
1286
-
1287
- def __repr__(self):
1288
- return '[Sequence: ' + repr(self._children)[1:-1]+']'
1289
-
1290
- # Provide an alias for the child_widgets() member.
1291
- children = CanvasWidget.child_widgets
1292
-
1293
- def replace_child(self, oldchild, newchild):
1294
- """
1295
- Replace the child canvas widget ``oldchild`` with ``newchild``.
1296
- ``newchild`` must not have a parent. ``oldchild``'s parent will
1297
- be set to None.
1298
-
1299
- :type oldchild: CanvasWidget
1300
- :param oldchild: The child canvas widget to remove.
1301
- :type newchild: CanvasWidget
1302
- :param newchild: The canvas widget that should replace
1303
- ``oldchild``.
1304
- """
1305
- index = self._children.index(oldchild)
1306
- self._children[index] = newchild
1307
- self._remove_child_widget(oldchild)
1308
- self._add_child_widget(newchild)
1309
- self.update(newchild)
1310
-
1311
- def remove_child(self, child):
1312
- """
1313
- Remove the given child canvas widget. ``child``'s parent will
1314
- be set ot None.
1315
-
1316
- :type child: CanvasWidget
1317
- :param child: The child canvas widget to remove.
1318
- """
1319
- index = self._children.index(child)
1320
- del self._children[index]
1321
- self._remove_child_widget(child)
1322
- if len(self._children) > 0:
1323
- self.update(self._children[0])
1324
-
1325
- def insert_child(self, index, child):
1326
- """
1327
- Insert a child canvas widget before a given index.
1328
-
1329
- :type child: CanvasWidget
1330
- :param child: The canvas widget that should be inserted.
1331
- :type index: int
1332
- :param index: The index where the child widget should be
1333
- inserted. In particular, the index of ``child`` will be
1334
- ``index``; and the index of any children whose indices were
1335
- greater than equal to ``index`` before ``child`` was
1336
- inserted will be incremented by one.
1337
- """
1338
- self._children.insert(index, child)
1339
- self._add_child_widget(child)
1340
-
1341
- class StackWidget(CanvasWidget):
1342
- """
1343
- A canvas widget that keeps a list of canvas widgets in a vertical
1344
- line.
1345
-
1346
- Attributes:
1347
- - ``align``: The horizontal alignment of the children. Possible
1348
- values are ``'left'``, ``'center'``, and ``'right'``. By
1349
- default, children are center-aligned.
1350
- - ``space``: The amount of vertical space to place between
1351
- children. By default, one pixel of space is used.
1352
- - ``ordered``: If true, then keep the children in their
1353
- original order.
1354
- """
1355
- def __init__(self, canvas, *children, **attribs):
1356
- """
1357
- Create a new stack widget.
1358
-
1359
- :type canvas: Tkinter.Canvas
1360
- :param canvas: This canvas widget's canvas.
1361
- :param children: The widgets that should be aligned
1362
- vertically. Each child must not have a parent.
1363
- :type children: list(CanvasWidget)
1364
- :param attribs: The new canvas widget's attributes.
1365
- """
1366
- self._align = 'center'
1367
- self._space = 1
1368
- self._ordered = False
1369
- self._children = list(children)
1370
- for child in children: self._add_child_widget(child)
1371
- CanvasWidget.__init__(self, canvas, **attribs)
1372
-
1373
- def __setitem__(self, attr, value):
1374
- if attr == 'align':
1375
- if value not in ('left', 'right', 'center'):
1376
- raise ValueError('Bad alignment: %r' % value)
1377
- self._align = value
1378
- elif attr == 'space': self._space = value
1379
- elif attr == 'ordered': self._ordered = value
1380
- else: CanvasWidget.__setitem__(self, attr, value)
1381
-
1382
- def __getitem__(self, attr):
1383
- if attr == 'align': return self._align
1384
- elif attr == 'space': return self._space
1385
- elif attr == 'ordered': return self._ordered
1386
- else: return CanvasWidget.__getitem__(self, attr)
1387
-
1388
- def _tags(self): return []
1389
-
1390
- def _xalign(self, left, right):
1391
- if self._align == 'left': return left
1392
- if self._align == 'right': return right
1393
- if self._align == 'center': return (left+right)/2
1394
-
1395
- def _update(self, child):
1396
- # Align all children with child.
1397
- (left, top, right, bot) = child.bbox()
1398
- x = self._xalign(left, right)
1399
- for c in self._children:
1400
- (x1, y1, x2, y2) = c.bbox()
1401
- c.move(x-self._xalign(x1,x2), 0)
1402
-
1403
- if self._ordered and len(self._children) > 1:
1404
- index = self._children.index(child)
1405
-
1406
- y = bot + self._space
1407
- for i in range(index+1, len(self._children)):
1408
- (x1, y1, x2, y2) = self._children[i].bbox()
1409
- if y > y1:
1410
- self._children[i].move(0, y-y1)
1411
- y += y2-y1 + self._space
1412
-
1413
- y = top - self._space
1414
- for i in range(index-1, -1, -1):
1415
- (x1, y1, x2, y2) = self._children[i].bbox()
1416
- if y < y2:
1417
- self._children[i].move(0, y-y2)
1418
- y -= y2-y1 + self._space
1419
-
1420
- def _manage(self):
1421
- if len(self._children) == 0: return
1422
- child = self._children[0]
1423
-
1424
- # Align all children with child.
1425
- (left, top, right, bot) = child.bbox()
1426
- x = self._xalign(left, right)
1427
-
1428
- index = self._children.index(child)
1429
-
1430
- # Line up children below the child.
1431
- y = bot + self._space
1432
- for i in range(index+1, len(self._children)):
1433
- (x1, y1, x2, y2) = self._children[i].bbox()
1434
- self._children[i].move(x-self._xalign(x1,x2), y-y1)
1435
- y += y2-y1 + self._space
1436
-
1437
- # Line up children above the child.
1438
- y = top - self._space
1439
- for i in range(index-1, -1, -1):
1440
- (x1, y1, x2, y2) = self._children[i].bbox()
1441
- self._children[i].move(x-self._xalign(x1,x2), y-y2)
1442
- y -= y2-y1 + self._space
1443
-
1444
- def __repr__(self):
1445
- return '[Stack: ' + repr(self._children)[1:-1]+']'
1446
-
1447
- # Provide an alias for the child_widgets() member.
1448
- children = CanvasWidget.child_widgets
1449
-
1450
- def replace_child(self, oldchild, newchild):
1451
- """
1452
- Replace the child canvas widget ``oldchild`` with ``newchild``.
1453
- ``newchild`` must not have a parent. ``oldchild``'s parent will
1454
- be set to None.
1455
-
1456
- :type oldchild: CanvasWidget
1457
- :param oldchild: The child canvas widget to remove.
1458
- :type newchild: CanvasWidget
1459
- :param newchild: The canvas widget that should replace
1460
- ``oldchild``.
1461
- """
1462
- index = self._children.index(oldchild)
1463
- self._children[index] = newchild
1464
- self._remove_child_widget(oldchild)
1465
- self._add_child_widget(newchild)
1466
- self.update(newchild)
1467
-
1468
- def remove_child(self, child):
1469
- """
1470
- Remove the given child canvas widget. ``child``'s parent will
1471
- be set ot None.
1472
-
1473
- :type child: CanvasWidget
1474
- :param child: The child canvas widget to remove.
1475
- """
1476
- index = self._children.index(child)
1477
- del self._children[index]
1478
- self._remove_child_widget(child)
1479
- if len(self._children) > 0:
1480
- self.update(self._children[0])
1481
-
1482
- def insert_child(self, index, child):
1483
- """
1484
- Insert a child canvas widget before a given index.
1485
-
1486
- :type child: CanvasWidget
1487
- :param child: The canvas widget that should be inserted.
1488
- :type index: int
1489
- :param index: The index where the child widget should be
1490
- inserted. In particular, the index of ``child`` will be
1491
- ``index``; and the index of any children whose indices were
1492
- greater than equal to ``index`` before ``child`` was
1493
- inserted will be incremented by one.
1494
- """
1495
- self._children.insert(index, child)
1496
- self._add_child_widget(child)
1497
-
1498
- class SpaceWidget(CanvasWidget):
1499
- """
1500
- A canvas widget that takes up space but does not display
1501
- anything. A ``SpaceWidget`` can be used to add space between
1502
- elements. Each space widget is characterized by a width and a
1503
- height. If you wish to only create horizontal space, then use a
1504
- height of zero; and if you wish to only create vertical space, use
1505
- a width of zero.
1506
- """
1507
- def __init__(self, canvas, width, height, **attribs):
1508
- """
1509
- Create a new space widget.
1510
-
1511
- :type canvas: Tkinter.Canvas
1512
- :param canvas: This canvas widget's canvas.
1513
- :type width: int
1514
- :param width: The width of the new space widget.
1515
- :type height: int
1516
- :param height: The height of the new space widget.
1517
- :param attribs: The new canvas widget's attributes.
1518
- """
1519
- # For some reason,
1520
- if width > 4: width -= 4
1521
- if height > 4: height -= 4
1522
- self._tag = canvas.create_line(1, 1, width, height, fill='')
1523
- CanvasWidget.__init__(self, canvas, **attribs)
1524
-
1525
- # note: width() and height() are already defined by CanvasWidget.
1526
- def set_width(self, width):
1527
- """
1528
- Change the width of this space widget.
1529
-
1530
- :param width: The new width.
1531
- :type width: int
1532
- :rtype: None
1533
- """
1534
- [x1, y1, x2, y2] = self.bbox()
1535
- self.canvas().coords(self._tag, x1, y1, x1+width, y2)
1536
-
1537
- def set_height(self, height):
1538
- """
1539
- Change the height of this space widget.
1540
-
1541
- :param height: The new height.
1542
- :type height: int
1543
- :rtype: None
1544
- """
1545
- [x1, y1, x2, y2] = self.bbox()
1546
- self.canvas().coords(self._tag, x1, y1, x2, y1+height)
1547
-
1548
- def _tags(self): return [self._tag]
1549
-
1550
- def __repr__(self): return '[Space]'
1551
-
1552
- class ScrollWatcherWidget(CanvasWidget):
1553
- """
1554
- A special canvas widget that adjusts its ``Canvas``'s scrollregion
1555
- to always include the bounding boxes of all of its children. The
1556
- scroll-watcher widget will only increase the size of the
1557
- ``Canvas``'s scrollregion; it will never decrease it.
1558
- """
1559
- def __init__(self, canvas, *children, **attribs):
1560
- """
1561
- Create a new scroll-watcher widget.
1562
-
1563
- :type canvas: Tkinter.Canvas
1564
- :param canvas: This canvas widget's canvas.
1565
- :type children: list(CanvasWidget)
1566
- :param children: The canvas widgets watched by the
1567
- scroll-watcher. The scroll-watcher will ensure that these
1568
- canvas widgets are always contained in their canvas's
1569
- scrollregion.
1570
- :param attribs: The new canvas widget's attributes.
1571
- """
1572
- for child in children: self._add_child_widget(child)
1573
- CanvasWidget.__init__(self, canvas, **attribs)
1574
-
1575
- def add_child(self, canvaswidget):
1576
- """
1577
- Add a new canvas widget to the scroll-watcher. The
1578
- scroll-watcher will ensure that the new canvas widget is
1579
- always contained in its canvas's scrollregion.
1580
-
1581
- :param canvaswidget: The new canvas widget.
1582
- :type canvaswidget: CanvasWidget
1583
- :rtype: None
1584
- """
1585
- self._add_child_widget(canvaswidget)
1586
- self.update(canvaswidget)
1587
-
1588
- def remove_child(self, canvaswidget):
1589
- """
1590
- Remove a canvas widget from the scroll-watcher. The
1591
- scroll-watcher will no longer ensure that the new canvas
1592
- widget is always contained in its canvas's scrollregion.
1593
-
1594
- :param canvaswidget: The canvas widget to remove.
1595
- :type canvaswidget: CanvasWidget
1596
- :rtype: None
1597
- """
1598
- self._remove_child_widget(canvaswidget)
1599
-
1600
- def _tags(self): return []
1601
-
1602
- def _update(self, child):
1603
- self._adjust_scrollregion()
1604
-
1605
- def _adjust_scrollregion(self):
1606
- """
1607
- Adjust the scrollregion of this scroll-watcher's ``Canvas`` to
1608
- include the bounding boxes of all of its children.
1609
- """
1610
- bbox = self.bbox()
1611
- canvas = self.canvas()
1612
- scrollregion = [int(n) for n in canvas['scrollregion'].split()]
1613
- if len(scrollregion) != 4: return
1614
- if (bbox[0] < scrollregion[0] or bbox[1] < scrollregion[1] or
1615
- bbox[2] > scrollregion[2] or bbox[3] > scrollregion[3]):
1616
- scrollregion = ('%d %d %d %d' %
1617
- (min(bbox[0], scrollregion[0]),
1618
- min(bbox[1], scrollregion[1]),
1619
- max(bbox[2], scrollregion[2]),
1620
- max(bbox[3], scrollregion[3])))
1621
- canvas['scrollregion'] = scrollregion
1622
-
1623
- ##//////////////////////////////////////////////////////
1624
- ## Canvas Frame
1625
- ##//////////////////////////////////////////////////////
1626
-
1627
- class CanvasFrame(object):
1628
- """
1629
- A ``Tkinter`` frame containing a canvas and scrollbars.
1630
- ``CanvasFrame`` uses a ``ScrollWatcherWidget`` to ensure that all of
1631
- the canvas widgets contained on its canvas are within its
1632
- scrollregion. In order for ``CanvasFrame`` to make these checks,
1633
- all canvas widgets must be registered with ``add_widget`` when they
1634
- are added to the canvas; and destroyed with ``destroy_widget`` when
1635
- they are no longer needed.
1636
-
1637
- If a ``CanvasFrame`` is created with no parent, then it will create
1638
- its own main window, including a "Done" button and a "Print"
1639
- button.
1640
- """
1641
- def __init__(self, parent=None, **kw):
1642
- """
1643
- Create a new ``CanvasFrame``.
1644
-
1645
- :type parent: Tkinter.BaseWidget or Tkinter.Tk
1646
- :param parent: The parent ``Tkinter`` widget. If no parent is
1647
- specified, then ``CanvasFrame`` will create a new main
1648
- window.
1649
- :param kw: Keyword arguments for the new ``Canvas``. See the
1650
- documentation for ``Tkinter.Canvas`` for more information.
1651
- """
1652
- # If no parent was given, set up a top-level window.
1653
- if parent is None:
1654
- self._parent = Tk()
1655
- self._parent.title('NLTK')
1656
- self._parent.bind('<Control-p>', lambda e: self.print_to_file())
1657
- self._parent.bind('<Control-x>', self.destroy)
1658
- self._parent.bind('<Control-q>', self.destroy)
1659
- else:
1660
- self._parent = parent
1661
-
1662
- # Create a frame for the canvas & scrollbars
1663
- self._frame = frame = Frame(self._parent)
1664
- self._canvas = canvas = Canvas(frame, **kw)
1665
- xscrollbar = Scrollbar(self._frame, orient='horizontal')
1666
- yscrollbar = Scrollbar(self._frame, orient='vertical')
1667
- xscrollbar['command'] = canvas.xview
1668
- yscrollbar['command'] = canvas.yview
1669
- canvas['xscrollcommand'] = xscrollbar.set
1670
- canvas['yscrollcommand'] = yscrollbar.set
1671
- yscrollbar.pack(fill='y', side='right')
1672
- xscrollbar.pack(fill='x', side='bottom')
1673
- canvas.pack(expand=1, fill='both', side='left')
1674
-
1675
- # Set initial scroll region.
1676
- scrollregion = '0 0 %s %s' % (canvas['width'], canvas['height'])
1677
- canvas['scrollregion'] = scrollregion
1678
-
1679
- self._scrollwatcher = ScrollWatcherWidget(canvas)
1680
-
1681
- # If no parent was given, pack the frame, and add a menu.
1682
- if parent is None:
1683
- self.pack(expand=1, fill='both')
1684
- self._init_menubar()
1685
-
1686
- def _init_menubar(self):
1687
- menubar = Menu(self._parent)
1688
-
1689
- filemenu = Menu(menubar, tearoff=0)
1690
- filemenu.add_command(label='Print to Postscript', underline=0,
1691
- command=self.print_to_file, accelerator='Ctrl-p')
1692
- filemenu.add_command(label='Exit', underline=1,
1693
- command=self.destroy, accelerator='Ctrl-x')
1694
- menubar.add_cascade(label='File', underline=0, menu=filemenu)
1695
-
1696
- self._parent.config(menu=menubar)
1697
-
1698
- def print_to_file(self, filename=None):
1699
- """
1700
- Print the contents of this ``CanvasFrame`` to a postscript
1701
- file. If no filename is given, then prompt the user for one.
1702
-
1703
- :param filename: The name of the file to print the tree to.
1704
- :type filename: str
1705
- :rtype: None
1706
- """
1707
- if filename is None:
1708
- from tkinter.filedialog import asksaveasfilename
1709
- ftypes = [('Postscript files', '.ps'),
1710
- ('All files', '*')]
1711
- filename = asksaveasfilename(filetypes=ftypes,
1712
- defaultextension='.ps')
1713
- if not filename: return
1714
- (x0, y0, w, h) = self.scrollregion()
1715
- self._canvas.postscript(file=filename, x=x0, y=y0,
1716
- width=w+2, height=h+2,
1717
- pagewidth=w+2, # points = 1/72 inch
1718
- pageheight=h+2, # points = 1/72 inch
1719
- pagex=0, pagey=0)
1720
-
1721
- def scrollregion(self):
1722
- """
1723
- :return: The current scroll region for the canvas managed by
1724
- this ``CanvasFrame``.
1725
- :rtype: 4-tuple of int
1726
- """
1727
- (x1, y1, x2, y2) = self._canvas['scrollregion'].split()
1728
- return (int(x1), int(y1), int(x2), int(y2))
1729
-
1730
- def canvas(self):
1731
- """
1732
- :return: The canvas managed by this ``CanvasFrame``.
1733
- :rtype: Tkinter.Canvas
1734
- """
1735
- return self._canvas
1736
-
1737
- def add_widget(self, canvaswidget, x=None, y=None):
1738
- """
1739
- Register a canvas widget with this ``CanvasFrame``. The
1740
- ``CanvasFrame`` will ensure that this canvas widget is always
1741
- within the ``Canvas``'s scrollregion. If no coordinates are
1742
- given for the canvas widget, then the ``CanvasFrame`` will
1743
- attempt to find a clear area of the canvas for it.
1744
-
1745
- :type canvaswidget: CanvasWidget
1746
- :param canvaswidget: The new canvas widget. ``canvaswidget``
1747
- must have been created on this ``CanvasFrame``'s canvas.
1748
- :type x: int
1749
- :param x: The initial x coordinate for the upper left hand
1750
- corner of ``canvaswidget``, in the canvas's coordinate
1751
- space.
1752
- :type y: int
1753
- :param y: The initial y coordinate for the upper left hand
1754
- corner of ``canvaswidget``, in the canvas's coordinate
1755
- space.
1756
- """
1757
- if x is None or y is None:
1758
- (x, y) = self._find_room(canvaswidget, x, y)
1759
-
1760
- # Move to (x,y)
1761
- (x1,y1,x2,y2) = canvaswidget.bbox()
1762
- canvaswidget.move(x-x1,y-y1)
1763
-
1764
- # Register with scrollwatcher.
1765
- self._scrollwatcher.add_child(canvaswidget)
1766
-
1767
- def _find_room(self, widget, desired_x, desired_y):
1768
- """
1769
- Try to find a space for a given widget.
1770
- """
1771
- (left, top, right, bot) = self.scrollregion()
1772
- w = widget.width()
1773
- h = widget.height()
1774
-
1775
- if w >= (right-left): return (0,0)
1776
- if h >= (bot-top): return (0,0)
1777
-
1778
- # Move the widget out of the way, for now.
1779
- (x1,y1,x2,y2) = widget.bbox()
1780
- widget.move(left-x2-50, top-y2-50)
1781
-
1782
- if desired_x is not None:
1783
- x = desired_x
1784
- for y in range(top, bot-h, int((bot-top-h)/10)):
1785
- if not self._canvas.find_overlapping(x-5, y-5, x+w+5, y+h+5):
1786
- return (x,y)
1787
-
1788
- if desired_y is not None:
1789
- y = desired_y
1790
- for x in range(left, right-w, int((right-left-w)/10)):
1791
- if not self._canvas.find_overlapping(x-5, y-5, x+w+5, y+h+5):
1792
- return (x,y)
1793
-
1794
- for y in range(top, bot-h, int((bot-top-h)/10)):
1795
- for x in range(left, right-w, int((right-left-w)/10)):
1796
- if not self._canvas.find_overlapping(x-5, y-5, x+w+5, y+h+5):
1797
- return (x,y)
1798
- return (0,0)
1799
-
1800
- def destroy_widget(self, canvaswidget):
1801
- """
1802
- Remove a canvas widget from this ``CanvasFrame``. This
1803
- deregisters the canvas widget, and destroys it.
1804
- """
1805
- self.remove_widget(canvaswidget)
1806
- canvaswidget.destroy()
1807
-
1808
- def remove_widget(self, canvaswidget):
1809
- # Deregister with scrollwatcher.
1810
- self._scrollwatcher.remove_child(canvaswidget)
1811
-
1812
- def pack(self, cnf={}, **kw):
1813
- """
1814
- Pack this ``CanvasFrame``. See the documentation for
1815
- ``Tkinter.Pack`` for more information.
1816
- """
1817
- self._frame.pack(cnf, **kw)
1818
- # Adjust to be big enough for kids?
1819
-
1820
- def destroy(self, *e):
1821
- """
1822
- Destroy this ``CanvasFrame``. If this ``CanvasFrame`` created a
1823
- top-level window, then this will close that window.
1824
- """
1825
- if self._parent is None: return
1826
- self._parent.destroy()
1827
- self._parent = None
1828
-
1829
- def mainloop(self, *args, **kwargs):
1830
- """
1831
- Enter the Tkinter mainloop. This function must be called if
1832
- this frame is created from a non-interactive program (e.g.
1833
- from a secript); otherwise, the frame will close as soon as
1834
- the script completes.
1835
- """
1836
- if in_idle(): return
1837
- self._parent.mainloop(*args, **kwargs)
1838
-
1839
- ##//////////////////////////////////////////////////////
1840
- ## Text display
1841
- ##//////////////////////////////////////////////////////
1842
-
1843
- class ShowText(object):
1844
- """
1845
- A ``Tkinter`` window used to display a text. ``ShowText`` is
1846
- typically used by graphical tools to display help text, or similar
1847
- information.
1848
- """
1849
- def __init__(self, root, title, text, width=None, height=None,
1850
- **textbox_options):
1851
- if width is None or height is None:
1852
- (width, height) = self.find_dimentions(text, width, height)
1853
-
1854
- # Create the main window.
1855
- if root is None:
1856
- self._top = top = Tk()
1857
- else:
1858
- self._top = top = Toplevel(root)
1859
- top.title(title)
1860
-
1861
- b = Button(top, text='Ok', command=self.destroy)
1862
- b.pack(side='bottom')
1863
-
1864
- tbf = Frame(top)
1865
- tbf.pack(expand=1, fill='both')
1866
- scrollbar = Scrollbar(tbf, orient='vertical')
1867
- scrollbar.pack(side='right', fill='y')
1868
- textbox = Text(tbf, wrap='word', width=width,
1869
- height=height, **textbox_options)
1870
- textbox.insert('end', text)
1871
- textbox['state'] = 'disabled'
1872
- textbox.pack(side='left', expand=1, fill='both')
1873
- scrollbar['command'] = textbox.yview
1874
- textbox['yscrollcommand'] = scrollbar.set
1875
-
1876
- # Make it easy to close the window.
1877
- top.bind('q', self.destroy)
1878
- top.bind('x', self.destroy)
1879
- top.bind('c', self.destroy)
1880
- top.bind('<Return>', self.destroy)
1881
- top.bind('<Escape>', self.destroy)
1882
-
1883
- # Focus the scrollbar, so they can use up/down, etc.
1884
- scrollbar.focus()
1885
-
1886
- def find_dimentions(self, text, width, height):
1887
- lines = text.split('\n')
1888
- if width is None:
1889
- maxwidth = max(len(line) for line in lines)
1890
- width = min(maxwidth, 80)
1891
-
1892
- # Now, find height.
1893
- height = 0
1894
- for line in lines:
1895
- while len(line) > width:
1896
- brk = line[:width].rfind(' ')
1897
- line = line[brk:]
1898
- height += 1
1899
- height += 1
1900
- height = min(height, 25)
1901
-
1902
- return (width, height)
1903
-
1904
- def destroy(self, *e):
1905
- if self._top is None: return
1906
- self._top.destroy()
1907
- self._top = None
1908
-
1909
- def mainloop(self, *args, **kwargs):
1910
- """
1911
- Enter the Tkinter mainloop. This function must be called if
1912
- this window is created from a non-interactive program (e.g.
1913
- from a secript); otherwise, the window will close as soon as
1914
- the script completes.
1915
- """
1916
- if in_idle(): return
1917
- self._top.mainloop(*args, **kwargs)
1918
-
1919
- ##//////////////////////////////////////////////////////
1920
- ## Entry dialog
1921
- ##//////////////////////////////////////////////////////
1922
-
1923
- class EntryDialog(object):
1924
- """
1925
- A dialog box for entering
1926
- """
1927
- def __init__(self, parent, original_text='', instructions='',
1928
- set_callback=None, title=None):
1929
- self._parent = parent
1930
- self._original_text = original_text
1931
- self._set_callback = set_callback
1932
-
1933
- width = int(max(30, len(original_text)*3/2))
1934
- self._top = Toplevel(parent)
1935
-
1936
- if title: self._top.title(title)
1937
-
1938
- # The text entry box.
1939
- entryframe = Frame(self._top)
1940
- entryframe.pack(expand=1, fill='both', padx=5, pady=5,ipady=10)
1941
- if instructions:
1942
- l=Label(entryframe, text=instructions)
1943
- l.pack(side='top', anchor='w', padx=30)
1944
- self._entry = Entry(entryframe, width=width)
1945
- self._entry.pack(expand=1, fill='x', padx=30)
1946
- self._entry.insert(0, original_text)
1947
-
1948
- # A divider
1949
- divider = Frame(self._top, borderwidth=1, relief='sunken')
1950
- divider.pack(fill='x', ipady=1, padx=10)
1951
-
1952
- # The buttons.
1953
- buttons = Frame(self._top)
1954
- buttons.pack(expand=0, fill='x', padx=5, pady=5)
1955
- b = Button(buttons, text='Cancel', command=self._cancel, width=8)
1956
- b.pack(side='right', padx=5)
1957
- b = Button(buttons, text='Ok', command=self._ok,
1958
- width=8, default='active')
1959
- b.pack(side='left', padx=5)
1960
- b = Button(buttons, text='Apply', command=self._apply, width=8)
1961
- b.pack(side='left')
1962
-
1963
- self._top.bind('<Return>', self._ok)
1964
- self._top.bind('<Control-q>', self._cancel)
1965
- self._top.bind('<Escape>', self._cancel)
1966
-
1967
- self._entry.focus()
1968
-
1969
- def _reset(self, *e):
1970
- self._entry.delete(0,'end')
1971
- self._entry.insert(0, self._original_text)
1972
- if self._set_callback:
1973
- self._set_callback(self._original_text)
1974
-
1975
- def _cancel(self, *e):
1976
- try: self._reset()
1977
- except: pass
1978
- self._destroy()
1979
-
1980
- def _ok(self, *e):
1981
- self._apply()
1982
- self._destroy()
1983
-
1984
- def _apply(self, *e):
1985
- if self._set_callback:
1986
- self._set_callback(self._entry.get())
1987
-
1988
- def _destroy(self, *e):
1989
- if self._top is None: return
1990
- self._top.destroy()
1991
- self._top = None
1992
-
1993
- ##//////////////////////////////////////////////////////
1994
- ## Colorized List
1995
- ##//////////////////////////////////////////////////////
1996
-
1997
- class ColorizedList(object):
1998
- """
1999
- An abstract base class for displaying a colorized list of items.
2000
- Subclasses should define:
2001
- - ``_init_colortags``, which sets up Text color tags that
2002
- will be used by the list.
2003
- - ``_item_repr``, which returns a list of (text,colortag)
2004
- tuples that make up the colorized representation of the
2005
- item.
2006
- :note: Typically, you will want to register a callback for
2007
- ``'select'`` that calls ``mark`` on the given item.
2008
- """
2009
- def __init__(self, parent, items=[], **options):
2010
- """
2011
- Construct a new list.
2012
-
2013
- :param parent: The Tk widget that contains the colorized list
2014
- :param items: The initial contents of the colorized list.
2015
- :param options:
2016
- """
2017
- self._parent = parent
2018
- self._callbacks = {}
2019
-
2020
- # Which items are marked?
2021
- self._marks = {}
2022
-
2023
- # Initialize the Tkinter frames.
2024
- self._init_itemframe(options.copy())
2025
-
2026
- # Set up key & mouse bindings.
2027
- self._textwidget.bind('<KeyPress>', self._keypress)
2028
- self._textwidget.bind('<ButtonPress>', self._buttonpress)
2029
-
2030
- # Fill in the given CFG's items.
2031
- self._items = None
2032
- self.set(items)
2033
-
2034
- #////////////////////////////////////////////////////////////
2035
- # Abstract methods
2036
- #////////////////////////////////////////////////////////////
2037
-
2038
- def _init_colortags(self, textwidget, options):
2039
- """
2040
- Set up any colortags that will be used by this colorized list.
2041
- E.g.:
2042
- >>> textwidget.tag_config('terminal', foreground='black')
2043
- """
2044
- raise NotImplementedError()
2045
-
2046
- def _item_repr(self, item):
2047
- """
2048
- Return a list of (text, colortag) tuples that make up the
2049
- colorized representation of the item. Colorized
2050
- representations may not span multiple lines. I.e., the text
2051
- strings returned may not contain newline characters.
2052
- """
2053
- raise NotImplementedError()
2054
-
2055
- #////////////////////////////////////////////////////////////
2056
- # Item Access
2057
- #////////////////////////////////////////////////////////////
2058
-
2059
- def get(self, index=None):
2060
- """
2061
- :return: A list of the items contained by this list.
2062
- """
2063
- if index is None:
2064
- return self._items[:]
2065
- else:
2066
- return self._items[index]
2067
-
2068
- def set(self, items):
2069
- """
2070
- Modify the list of items contained by this list.
2071
- """
2072
- items = list(items)
2073
- if self._items == items: return
2074
- self._items = list(items)
2075
-
2076
- self._textwidget['state'] = 'normal'
2077
- self._textwidget.delete('1.0', 'end')
2078
- for item in items:
2079
- for (text, colortag) in self._item_repr(item):
2080
- assert '\n' not in text, 'item repr may not contain newline'
2081
- self._textwidget.insert('end', text, colortag)
2082
- self._textwidget.insert('end', '\n')
2083
- # Remove the final newline
2084
- self._textwidget.delete('end-1char', 'end')
2085
- self._textwidget.mark_set('insert', '1.0')
2086
- self._textwidget['state'] = 'disabled'
2087
- # Clear all marks
2088
- self._marks.clear()
2089
-
2090
- def unmark(self, item=None):
2091
- """
2092
- Remove highlighting from the given item; or from every item,
2093
- if no item is given.
2094
- :raise ValueError: If ``item`` is not contained in the list.
2095
- :raise KeyError: If ``item`` is not marked.
2096
- """
2097
- if item is None:
2098
- self._marks.clear()
2099
- self._textwidget.tag_remove('highlight', '1.0', 'end+1char')
2100
- else:
2101
- index = self._items.index(item)
2102
- del self._marks[item]
2103
- (start, end) = ('%d.0' % (index+1), '%d.0' % (index+2))
2104
- self._textwidget.tag_remove('highlight', start, end)
2105
-
2106
- def mark(self, item):
2107
- """
2108
- Highlight the given item.
2109
- :raise ValueError: If ``item`` is not contained in the list.
2110
- """
2111
- self._marks[item] = 1
2112
- index = self._items.index(item)
2113
- (start, end) = ('%d.0' % (index+1), '%d.0' % (index+2))
2114
- self._textwidget.tag_add('highlight', start, end)
2115
-
2116
- def markonly(self, item):
2117
- """
2118
- Remove any current highlighting, and mark the given item.
2119
- :raise ValueError: If ``item`` is not contained in the list.
2120
- """
2121
- self.unmark()
2122
- self.mark(item)
2123
-
2124
- def view(self, item):
2125
- """
2126
- Adjust the view such that the given item is visible. If
2127
- the item is already visible, then do nothing.
2128
- """
2129
- index = self._items.index(item)
2130
- self._textwidget.see('%d.0' % (index+1))
2131
-
2132
- #////////////////////////////////////////////////////////////
2133
- # Callbacks
2134
- #////////////////////////////////////////////////////////////
2135
-
2136
- def add_callback(self, event, func):
2137
- """
2138
- Register a callback function with the list. This function
2139
- will be called whenever the given event occurs.
2140
-
2141
- :param event: The event that will trigger the callback
2142
- function. Valid events are: click1, click2, click3,
2143
- space, return, select, up, down, next, prior, move
2144
- :param func: The function that should be called when
2145
- the event occurs. ``func`` will be called with a
2146
- single item as its argument. (The item selected
2147
- or the item moved to).
2148
- """
2149
- if event == 'select': events = ['click1', 'space', 'return']
2150
- elif event == 'move': events = ['up', 'down', 'next', 'prior']
2151
- else: events = [event]
2152
-
2153
- for e in events:
2154
- self._callbacks.setdefault(e,{})[func] = 1
2155
-
2156
- def remove_callback(self, event, func=None):
2157
- """
2158
- Deregister a callback function. If ``func`` is none, then
2159
- all callbacks are removed for the given event.
2160
- """
2161
- if event is None: events = list(self._callbacks.keys())
2162
- elif event == 'select': events = ['click1', 'space', 'return']
2163
- elif event == 'move': events = ['up', 'down', 'next', 'prior']
2164
- else: events = [event]
2165
-
2166
- for e in events:
2167
- if func is None: del self._callbacks[e]
2168
- else:
2169
- try: del self._callbacks[e][func]
2170
- except: pass
2171
-
2172
- #////////////////////////////////////////////////////////////
2173
- # Tkinter Methods
2174
- #////////////////////////////////////////////////////////////
2175
-
2176
- def pack(self, cnf={}, **kw):
2177
- # "@include: Tkinter.Pack.pack"
2178
- self._itemframe.pack(cnf, **kw)
2179
-
2180
- def grid(self, cnf={}, **kw):
2181
- # "@include: Tkinter.Grid.grid"
2182
- self._itemframe.grid(cnf, *kw)
2183
-
2184
- def focus(self):
2185
- # "@include: Tkinter.Widget.focus"
2186
- self._textwidget.focus()
2187
-
2188
- #////////////////////////////////////////////////////////////
2189
- # Internal Methods
2190
- #////////////////////////////////////////////////////////////
2191
-
2192
- def _init_itemframe(self, options):
2193
- self._itemframe = Frame(self._parent)
2194
-
2195
- # Create the basic Text widget & scrollbar.
2196
- options.setdefault('background', '#e0e0e0')
2197
- self._textwidget = Text(self._itemframe, **options)
2198
- self._textscroll = Scrollbar(self._itemframe, takefocus=0,
2199
- orient='vertical')
2200
- self._textwidget.config(yscrollcommand = self._textscroll.set)
2201
- self._textscroll.config(command=self._textwidget.yview)
2202
- self._textscroll.pack(side='right', fill='y')
2203
- self._textwidget.pack(expand=1, fill='both', side='left')
2204
-
2205
- # Initialize the colorization tags
2206
- self._textwidget.tag_config('highlight', background='#e0ffff',
2207
- border='1', relief='raised')
2208
- self._init_colortags(self._textwidget, options)
2209
-
2210
- # How do I want to mark keyboard selection?
2211
- self._textwidget.tag_config('sel', foreground='')
2212
- self._textwidget.tag_config('sel', foreground='', background='',
2213
- border='', underline=1)
2214
- self._textwidget.tag_lower('highlight', 'sel')
2215
-
2216
- def _fire_callback(self, event, itemnum):
2217
- if event not in self._callbacks: return
2218
- if 0 <= itemnum < len(self._items):
2219
- item = self._items[itemnum]
2220
- else:
2221
- item = None
2222
- for cb_func in list(self._callbacks[event].keys()):
2223
- cb_func(item)
2224
-
2225
- def _buttonpress(self, event):
2226
- clickloc = '@%d,%d' % (event.x,event.y)
2227
- insert_point = self._textwidget.index(clickloc)
2228
- itemnum = int(insert_point.split('.')[0])-1
2229
- self._fire_callback('click%d' % event.num, itemnum)
2230
-
2231
- def _keypress(self, event):
2232
- if event.keysym == 'Return' or event.keysym == 'space':
2233
- insert_point = self._textwidget.index('insert')
2234
- itemnum = int(insert_point.split('.')[0])-1
2235
- self._fire_callback(event.keysym.lower(), itemnum)
2236
- return
2237
- elif event.keysym == 'Down': delta='+1line'
2238
- elif event.keysym == 'Up': delta='-1line'
2239
- elif event.keysym == 'Next': delta='+10lines'
2240
- elif event.keysym == 'Prior': delta='-10lines'
2241
- else: return 'continue'
2242
-
2243
- self._textwidget.mark_set('insert', 'insert'+delta)
2244
- self._textwidget.see('insert')
2245
- self._textwidget.tag_remove('sel', '1.0', 'end+1char')
2246
- self._textwidget.tag_add('sel', 'insert linestart', 'insert lineend')
2247
-
2248
- insert_point = self._textwidget.index('insert')
2249
- itemnum = int(insert_point.split('.')[0])-1
2250
- self._fire_callback(event.keysym.lower(), itemnum)
2251
-
2252
- return 'break'
2253
-
2254
- ##//////////////////////////////////////////////////////
2255
- ## Improved OptionMenu
2256
- ##//////////////////////////////////////////////////////
2257
-
2258
- class MutableOptionMenu(Menubutton):
2259
- def __init__(self, master, values, **options):
2260
- self._callback = options.get('command')
2261
- if 'command' in options: del options['command']
2262
-
2263
- # Create a variable
2264
- self._variable = variable = StringVar()
2265
- if len(values) > 0:
2266
- variable.set(values[0])
2267
-
2268
- kw = {"borderwidth": 2, "textvariable": variable,
2269
- "indicatoron": 1, "relief": RAISED, "anchor": "c",
2270
- "highlightthickness": 2}
2271
- kw.update(options)
2272
- Widget.__init__(self, master, "menubutton", kw)
2273
- self.widgetName = 'tk_optionMenu'
2274
- self._menu = Menu(self, name="menu", tearoff=0,)
2275
- self.menuname = self._menu._w
2276
-
2277
- self._values = []
2278
- for value in values: self.add(value)
2279
-
2280
- self["menu"] = self._menu
2281
-
2282
- def add(self, value):
2283
- if value in self._values: return
2284
- def set(value=value): self.set(value)
2285
- self._menu.add_command(label=value, command=set)
2286
- self._values.append(value)
2287
-
2288
- def set(self, value):
2289
- self._variable.set(value)
2290
- if self._callback:
2291
- self._callback(value)
2292
-
2293
- def remove(self, value):
2294
- # Might raise indexerror: pass to parent.
2295
- i = self._values.index(value)
2296
- del self._values[i]
2297
- self._menu.delete(i, i)
2298
-
2299
- def __getitem__(self, name):
2300
- if name == 'menu':
2301
- return self.__menu
2302
- return Widget.__getitem__(self, name)
2303
-
2304
- def destroy(self):
2305
- """Destroy this widget and the associated menu."""
2306
- Menubutton.destroy(self)
2307
- self._menu = None
2308
-
2309
- ##//////////////////////////////////////////////////////
2310
- ## Test code.
2311
- ##//////////////////////////////////////////////////////
2312
-
2313
- def demo():
2314
- """
2315
- A simple demonstration showing how to use canvas widgets.
2316
- """
2317
- def fill(cw):
2318
- from random import randint
2319
- cw['fill'] = '#00%04d' % randint(0,9999)
2320
- def color(cw):
2321
- from random import randint
2322
- cw['color'] = '#ff%04d' % randint(0,9999)
2323
-
2324
- cf = CanvasFrame(closeenough=10, width=300, height=300)
2325
- c = cf.canvas()
2326
- ct3 = TextWidget(c, 'hiya there', draggable=1)
2327
- ct2 = TextWidget(c, 'o o\n||\n___\n U', draggable=1, justify='center')
2328
- co = OvalWidget(c, ct2, outline='red')
2329
- ct = TextWidget(c, 'o o\n||\n\\___/', draggable=1, justify='center')
2330
- cp = ParenWidget(c, ct, color='red')
2331
- cb = BoxWidget(c, cp, fill='cyan', draggable=1, width=3, margin=10)
2332
- equation = SequenceWidget(c,
2333
- SymbolWidget(c, 'forall'), TextWidget(c, 'x'),
2334
- SymbolWidget(c, 'exists'), TextWidget(c, 'y: '),
2335
- TextWidget(c, 'x'), SymbolWidget(c, 'notequal'),
2336
- TextWidget(c, 'y'))
2337
- space = SpaceWidget(c, 0, 30)
2338
- cstack = StackWidget(c, cb, ct3, space, co, equation, align='center')
2339
- foo = TextWidget(c, 'try clicking\nand dragging',
2340
- draggable=1, justify='center')
2341
- cs = SequenceWidget(c, cstack, foo)
2342
- zz = BracketWidget(c, cs, color='green4', width=3)
2343
- cf.add_widget(zz, 60, 30)
2344
-
2345
- cb.bind_click(fill)
2346
- ct.bind_click(color)
2347
- co.bind_click(fill)
2348
- ct2.bind_click(color)
2349
- ct3.bind_click(color)
2350
-
2351
- cf.mainloop()
2352
- #ShowText(None, 'title', ((('this is text'*150)+'\n')*5))
2353
-
2354
- if __name__ == '__main__':
2355
- demo()
2356
-
1
+ # Natural Language Toolkit: Drawing utilities
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
+ Tools for graphically displaying and interacting with the objects and
10
+ processing classes defined by the Toolkit. These tools are primarily
11
+ intended to help students visualize the objects that they create.
12
+
13
+ The graphical tools are typically built using "canvas widgets", each
14
+ of which encapsulates the graphical elements and bindings used to
15
+ display a complex object on a Tkinter ``Canvas``. For example, NLTK
16
+ defines canvas widgets for displaying trees and directed graphs, as
17
+ well as a number of simpler widgets. These canvas widgets make it
18
+ easier to build new graphical tools and demos. See the class
19
+ documentation for ``CanvasWidget`` for more information.
20
+
21
+ The ``nltk.draw`` module defines the abstract ``CanvasWidget`` base
22
+ class, and a number of simple canvas widgets. The remaining canvas
23
+ widgets are defined by submodules, such as ``nltk.draw.tree``.
24
+
25
+ The ``nltk.draw`` module also defines ``CanvasFrame``, which
26
+ encapsulates a ``Canvas`` and its scrollbars. It uses a
27
+ ``ScrollWatcherWidget`` to ensure that all canvas widgets contained on
28
+ its canvas are within the scroll region.
29
+
30
+ Acknowledgements: Many of the ideas behind the canvas widget system
31
+ are derived from ``CLIG``, a Tk-based grapher for linguistic data
32
+ structures. For more information, see the CLIG
33
+ homepage (http://www.ags.uni-sb.de/~konrad/clig.html).
34
+
35
+ """
36
+ from abc import ABCMeta, abstractmethod
37
+ from six import add_metaclass
38
+ from six.moves.tkinter import (Button, Canvas, Entry, Frame, Label, Menu,
39
+ Menubutton, Scrollbar, StringVar, Text, Tk,
40
+ Toplevel, Widget, RAISED)
41
+ from six.moves.tkinter_tkfiledialog import asksaveasfilename
42
+
43
+ from nltk.util import in_idle
44
+
45
+ ##//////////////////////////////////////////////////////
46
+ ## CanvasWidget
47
+ ##//////////////////////////////////////////////////////
48
+
49
+
50
+ @add_metaclass(ABCMeta)
51
+ class CanvasWidget(object):
52
+ """
53
+ A collection of graphical elements and bindings used to display a
54
+ complex object on a Tkinter ``Canvas``. A canvas widget is
55
+ responsible for managing the ``Canvas`` tags and callback bindings
56
+ necessary to display and interact with the object. Canvas widgets
57
+ are often organized into hierarchies, where parent canvas widgets
58
+ control aspects of their child widgets.
59
+
60
+ Each canvas widget is bound to a single ``Canvas``. This ``Canvas``
61
+ is specified as the first argument to the ``CanvasWidget``'s
62
+ constructor.
63
+
64
+ Attributes. Each canvas widget can support a variety of
65
+ "attributes", which control how the canvas widget is displayed.
66
+ Some typical examples attributes are ``color``, ``font``, and
67
+ ``radius``. Each attribute has a default value. This default
68
+ value can be overridden in the constructor, using keyword
69
+ arguments of the form ``attribute=value``:
70
+
71
+ >>> from nltk.draw.util import TextWidget
72
+ >>> cn = TextWidget(c, 'test', color='red')
73
+
74
+ Attribute values can also be changed after a canvas widget has
75
+ been constructed, using the ``__setitem__`` operator:
76
+
77
+ >>> cn['font'] = 'times'
78
+
79
+ The current value of an attribute value can be queried using the
80
+ ``__getitem__`` operator:
81
+
82
+ >>> cn['color']
83
+ red
84
+
85
+ For a list of the attributes supported by a type of canvas widget,
86
+ see its class documentation.
87
+
88
+ Interaction. The attribute ``'draggable'`` controls whether the
89
+ user can drag a canvas widget around the canvas. By default,
90
+ canvas widgets are not draggable.
91
+
92
+ ``CanvasWidget`` provides callback support for two types of user
93
+ interaction: clicking and dragging. The method ``bind_click``
94
+ registers a callback function that is called whenever the canvas
95
+ widget is clicked. The method ``bind_drag`` registers a callback
96
+ function that is called after the canvas widget is dragged. If
97
+ the user clicks or drags a canvas widget with no registered
98
+ callback function, then the interaction event will propagate to
99
+ its parent. For each canvas widget, only one callback function
100
+ may be registered for an interaction event. Callback functions
101
+ can be deregistered with the ``unbind_click`` and ``unbind_drag``
102
+ methods.
103
+
104
+ Subclassing. ``CanvasWidget`` is an abstract class. Subclasses
105
+ are required to implement the following methods:
106
+
107
+ - ``__init__``: Builds a new canvas widget. It must perform the
108
+ following three tasks (in order):
109
+ - Create any new graphical elements.
110
+ - Call ``_add_child_widget`` on each child widget.
111
+ - Call the ``CanvasWidget`` constructor.
112
+ - ``_tags``: Returns a list of the canvas tags for all graphical
113
+ elements managed by this canvas widget, not including
114
+ graphical elements managed by its child widgets.
115
+ - ``_manage``: Arranges the child widgets of this canvas widget.
116
+ This is typically only called when the canvas widget is
117
+ created.
118
+ - ``_update``: Update this canvas widget in response to a
119
+ change in a single child.
120
+
121
+ For a ``CanvasWidget`` with no child widgets, the default
122
+ definitions for ``_manage`` and ``_update`` may be used.
123
+
124
+ If a subclass defines any attributes, then it should implement
125
+ ``__getitem__`` and ``__setitem__``. If either of these methods is
126
+ called with an unknown attribute, then they should propagate the
127
+ request to ``CanvasWidget``.
128
+
129
+ Most subclasses implement a number of additional methods that
130
+ modify the ``CanvasWidget`` in some way. These methods must call
131
+ ``parent.update(self)`` after making any changes to the canvas
132
+ widget's graphical elements. The canvas widget must also call
133
+ ``parent.update(self)`` after changing any attribute value that
134
+ affects the shape or position of the canvas widget's graphical
135
+ elements.
136
+
137
+ :type __canvas: Tkinter.Canvas
138
+ :ivar __canvas: This ``CanvasWidget``'s canvas.
139
+
140
+ :type __parent: CanvasWidget or None
141
+ :ivar __parent: This ``CanvasWidget``'s hierarchical parent widget.
142
+ :type __children: list(CanvasWidget)
143
+ :ivar __children: This ``CanvasWidget``'s hierarchical child widgets.
144
+
145
+ :type __updating: bool
146
+ :ivar __updating: Is this canvas widget currently performing an
147
+ update? If it is, then it will ignore any new update requests
148
+ from child widgets.
149
+
150
+ :type __draggable: bool
151
+ :ivar __draggable: Is this canvas widget draggable?
152
+ :type __press: event
153
+ :ivar __press: The ButtonPress event that we're currently handling.
154
+ :type __drag_x: int
155
+ :ivar __drag_x: Where it's been moved to (to find dx)
156
+ :type __drag_y: int
157
+ :ivar __drag_y: Where it's been moved to (to find dy)
158
+ :type __callbacks: dictionary
159
+ :ivar __callbacks: Registered callbacks. Currently, four keys are
160
+ used: ``1``, ``2``, ``3``, and ``'drag'``. The values are
161
+ callback functions. Each callback function takes a single
162
+ argument, which is the ``CanvasWidget`` that triggered the
163
+ callback.
164
+ """
165
+ def __init__(self, canvas, parent=None, **attribs):
166
+ """
167
+ Create a new canvas widget. This constructor should only be
168
+ called by subclass constructors; and it should be called only
169
+ "after" the subclass has constructed all graphical canvas
170
+ objects and registered all child widgets.
171
+
172
+ :param canvas: This canvas widget's canvas.
173
+ :type canvas: Tkinter.Canvas
174
+ :param parent: This canvas widget's hierarchical parent.
175
+ :type parent: CanvasWidget
176
+ :param attribs: The new canvas widget's attributes.
177
+ """
178
+ if self.__class__ == CanvasWidget:
179
+ raise TypeError('CanvasWidget is an abstract base class')
180
+
181
+ if not isinstance(canvas, Canvas):
182
+ raise TypeError('Expected a canvas!')
183
+
184
+ self.__canvas = canvas
185
+ self.__parent = parent
186
+
187
+ # If the subclass constructor called _add_child_widget, then
188
+ # self.__children will already exist.
189
+ if not hasattr(self, '_CanvasWidget__children'): self.__children = []
190
+
191
+ # Is this widget hidden?
192
+ self.__hidden = 0
193
+
194
+ # Update control (prevents infinite loops)
195
+ self.__updating = 0
196
+
197
+ # Button-press and drag callback handling.
198
+ self.__press = None
199
+ self.__drag_x = self.__drag_y = 0
200
+ self.__callbacks = {}
201
+ self.__draggable = 0
202
+
203
+ # Set up attributes.
204
+ for (attr, value) in list(attribs.items()): self[attr] = value
205
+
206
+ # Manage this canvas widget
207
+ self._manage()
208
+
209
+ # Register any new bindings
210
+ for tag in self._tags():
211
+ self.__canvas.tag_bind(tag, '<ButtonPress-1>',
212
+ self.__press_cb)
213
+ self.__canvas.tag_bind(tag, '<ButtonPress-2>',
214
+ self.__press_cb)
215
+ self.__canvas.tag_bind(tag, '<ButtonPress-3>',
216
+ self.__press_cb)
217
+
218
+ ##//////////////////////////////////////////////////////
219
+ ## Inherited methods.
220
+ ##//////////////////////////////////////////////////////
221
+
222
+ def bbox(self):
223
+ """
224
+ :return: A bounding box for this ``CanvasWidget``. The bounding
225
+ box is a tuple of four coordinates, *(xmin, ymin, xmax, ymax)*,
226
+ for a rectangle which encloses all of the canvas
227
+ widget's graphical elements. Bounding box coordinates are
228
+ specified with respect to the coordinate space of the ``Canvas``.
229
+ :rtype: tuple(int, int, int, int)
230
+ """
231
+ if self.__hidden: return (0,0,0,0)
232
+ if len(self.tags()) == 0: raise ValueError('No tags')
233
+ return self.__canvas.bbox(*self.tags())
234
+
235
+ def width(self):
236
+ """
237
+ :return: The width of this canvas widget's bounding box, in
238
+ its ``Canvas``'s coordinate space.
239
+ :rtype: int
240
+ """
241
+ if len(self.tags()) == 0: raise ValueError('No tags')
242
+ bbox = self.__canvas.bbox(*self.tags())
243
+ return bbox[2]-bbox[0]
244
+
245
+ def height(self):
246
+ """
247
+ :return: The height of this canvas widget's bounding box, in
248
+ its ``Canvas``'s coordinate space.
249
+ :rtype: int
250
+ """
251
+ if len(self.tags()) == 0: raise ValueError('No tags')
252
+ bbox = self.__canvas.bbox(*self.tags())
253
+ return bbox[3]-bbox[1]
254
+
255
+ def parent(self):
256
+ """
257
+ :return: The hierarchical parent of this canvas widget.
258
+ ``self`` is considered a subpart of its parent for
259
+ purposes of user interaction.
260
+ :rtype: CanvasWidget or None
261
+ """
262
+ return self.__parent
263
+
264
+ def child_widgets(self):
265
+ """
266
+ :return: A list of the hierarchical children of this canvas
267
+ widget. These children are considered part of ``self``
268
+ for purposes of user interaction.
269
+ :rtype: list of CanvasWidget
270
+ """
271
+ return self.__children
272
+
273
+ def canvas(self):
274
+ """
275
+ :return: The canvas that this canvas widget is bound to.
276
+ :rtype: Tkinter.Canvas
277
+ """
278
+ return self.__canvas
279
+
280
+ def move(self, dx, dy):
281
+ """
282
+ Move this canvas widget by a given distance. In particular,
283
+ shift the canvas widget right by ``dx`` pixels, and down by
284
+ ``dy`` pixels. Both ``dx`` and ``dy`` may be negative, resulting
285
+ in leftward or upward movement.
286
+
287
+ :type dx: int
288
+ :param dx: The number of pixels to move this canvas widget
289
+ rightwards.
290
+ :type dy: int
291
+ :param dy: The number of pixels to move this canvas widget
292
+ downwards.
293
+ :rtype: None
294
+ """
295
+ if dx == dy == 0: return
296
+ for tag in self.tags():
297
+ self.__canvas.move(tag, dx, dy)
298
+ if self.__parent: self.__parent.update(self)
299
+
300
+ def moveto(self, x, y, anchor='NW'):
301
+ """
302
+ Move this canvas widget to the given location. In particular,
303
+ shift the canvas widget such that the corner or side of the
304
+ bounding box specified by ``anchor`` is at location (``x``,
305
+ ``y``).
306
+
307
+ :param x,y: The location that the canvas widget should be moved
308
+ to.
309
+ :param anchor: The corner or side of the canvas widget that
310
+ should be moved to the specified location. ``'N'``
311
+ specifies the top center; ``'NE'`` specifies the top right
312
+ corner; etc.
313
+ """
314
+ x1,y1,x2,y2 = self.bbox()
315
+ if anchor == 'NW': self.move(x-x1, y-y1)
316
+ if anchor == 'N': self.move(x-x1/2-x2/2, y-y1)
317
+ if anchor == 'NE': self.move(x-x2, y-y1)
318
+ if anchor == 'E': self.move(x-x2, y-y1/2-y2/2)
319
+ if anchor == 'SE': self.move(x-x2, y-y2)
320
+ if anchor == 'S': self.move(x-x1/2-x2/2, y-y2)
321
+ if anchor == 'SW': self.move(x-x1, y-y2)
322
+ if anchor == 'W': self.move(x-x1, y-y1/2-y2/2)
323
+
324
+ def destroy(self):
325
+ """
326
+ Remove this ``CanvasWidget`` from its ``Canvas``. After a
327
+ ``CanvasWidget`` has been destroyed, it should not be accessed.
328
+
329
+ Note that you only need to destroy a top-level
330
+ ``CanvasWidget``; its child widgets will be destroyed
331
+ automatically. If you destroy a non-top-level
332
+ ``CanvasWidget``, then the entire top-level widget will be
333
+ destroyed.
334
+
335
+ :raise ValueError: if this ``CanvasWidget`` has a parent.
336
+ :rtype: None
337
+ """
338
+ if self.__parent is not None:
339
+ self.__parent.destroy()
340
+ return
341
+
342
+ for tag in self.tags():
343
+ self.__canvas.tag_unbind(tag, '<ButtonPress-1>')
344
+ self.__canvas.tag_unbind(tag, '<ButtonPress-2>')
345
+ self.__canvas.tag_unbind(tag, '<ButtonPress-3>')
346
+ self.__canvas.delete(*self.tags())
347
+ self.__canvas = None
348
+
349
+ def update(self, child):
350
+ """
351
+ Update the graphical display of this canvas widget, and all of
352
+ its ancestors, in response to a change in one of this canvas
353
+ widget's children.
354
+
355
+ :param child: The child widget that changed.
356
+ :type child: CanvasWidget
357
+ """
358
+ if self.__hidden or child.__hidden: return
359
+ # If we're already updating, then do nothing. This prevents
360
+ # infinite loops when _update modifies its children.
361
+ if self.__updating: return
362
+ self.__updating = 1
363
+
364
+ # Update this CanvasWidget.
365
+ self._update(child)
366
+
367
+ # Propagate update request to the parent.
368
+ if self.__parent: self.__parent.update(self)
369
+
370
+ # We're done updating.
371
+ self.__updating = 0
372
+
373
+ def manage(self):
374
+ """
375
+ Arrange this canvas widget and all of its descendants.
376
+
377
+ :rtype: None
378
+ """
379
+ if self.__hidden: return
380
+ for child in self.__children: child.manage()
381
+ self._manage()
382
+
383
+ def tags(self):
384
+ """
385
+ :return: a list of the canvas tags for all graphical
386
+ elements managed by this canvas widget, including
387
+ graphical elements managed by its child widgets.
388
+ :rtype: list of int
389
+ """
390
+ if self.__canvas is None:
391
+ raise ValueError('Attempt to access a destroyed canvas widget')
392
+ tags = []
393
+ tags += self._tags()
394
+ for child in self.__children:
395
+ tags += child.tags()
396
+ return tags
397
+
398
+ def __setitem__(self, attr, value):
399
+ """
400
+ Set the value of the attribute ``attr`` to ``value``. See the
401
+ class documentation for a list of attributes supported by this
402
+ canvas widget.
403
+
404
+ :rtype: None
405
+ """
406
+ if attr == 'draggable':
407
+ self.__draggable = value
408
+ else:
409
+ raise ValueError('Unknown attribute %r' % attr)
410
+
411
+ def __getitem__(self, attr):
412
+ """
413
+ :return: the value of the attribute ``attr``. See the class
414
+ documentation for a list of attributes supported by this
415
+ canvas widget.
416
+ :rtype: (any)
417
+ """
418
+ if attr == 'draggable':
419
+ return self.__draggable
420
+ else:
421
+ raise ValueError('Unknown attribute %r' % attr)
422
+
423
+ def __repr__(self):
424
+ """
425
+ :return: a string representation of this canvas widget.
426
+ :rtype: str
427
+ """
428
+ return '<%s>' % self.__class__.__name__
429
+
430
+ def hide(self):
431
+ """
432
+ Temporarily hide this canvas widget.
433
+
434
+ :rtype: None
435
+ """
436
+ self.__hidden = 1
437
+ for tag in self.tags():
438
+ self.__canvas.itemconfig(tag, state='hidden')
439
+
440
+ def show(self):
441
+ """
442
+ Show a hidden canvas widget.
443
+
444
+ :rtype: None
445
+ """
446
+ self.__hidden = 0
447
+ for tag in self.tags():
448
+ self.__canvas.itemconfig(tag, state='normal')
449
+
450
+ def hidden(self):
451
+ """
452
+ :return: True if this canvas widget is hidden.
453
+ :rtype: bool
454
+ """
455
+ return self.__hidden
456
+
457
+ ##//////////////////////////////////////////////////////
458
+ ## Callback interface
459
+ ##//////////////////////////////////////////////////////
460
+
461
+ def bind_click(self, callback, button=1):
462
+ """
463
+ Register a new callback that will be called whenever this
464
+ ``CanvasWidget`` is clicked on.
465
+
466
+ :type callback: function
467
+ :param callback: The callback function that will be called
468
+ whenever this ``CanvasWidget`` is clicked. This function
469
+ will be called with this ``CanvasWidget`` as its argument.
470
+ :type button: int
471
+ :param button: Which button the user should use to click on
472
+ this ``CanvasWidget``. Typically, this should be 1 (left
473
+ button), 3 (right button), or 2 (middle button).
474
+ """
475
+ self.__callbacks[button] = callback
476
+
477
+ def bind_drag(self, callback):
478
+ """
479
+ Register a new callback that will be called after this
480
+ ``CanvasWidget`` is dragged. This implicitly makes this
481
+ ``CanvasWidget`` draggable.
482
+
483
+ :type callback: function
484
+ :param callback: The callback function that will be called
485
+ whenever this ``CanvasWidget`` is clicked. This function
486
+ will be called with this ``CanvasWidget`` as its argument.
487
+ """
488
+ self.__draggable = 1
489
+ self.__callbacks['drag'] = callback
490
+
491
+ def unbind_click(self, button=1):
492
+ """
493
+ Remove a callback that was registered with ``bind_click``.
494
+
495
+ :type button: int
496
+ :param button: Which button the user should use to click on
497
+ this ``CanvasWidget``. Typically, this should be 1 (left
498
+ button), 3 (right button), or 2 (middle button).
499
+ """
500
+ try: del self.__callbacks[button]
501
+ except: pass
502
+
503
+ def unbind_drag(self):
504
+ """
505
+ Remove a callback that was registered with ``bind_drag``.
506
+ """
507
+ try: del self.__callbacks['drag']
508
+ except: pass
509
+
510
+ ##//////////////////////////////////////////////////////
511
+ ## Callback internals
512
+ ##//////////////////////////////////////////////////////
513
+
514
+ def __press_cb(self, event):
515
+ """
516
+ Handle a button-press event:
517
+ - record the button press event in ``self.__press``
518
+ - register a button-release callback.
519
+ - if this CanvasWidget or any of its ancestors are
520
+ draggable, then register the appropriate motion callback.
521
+ """
522
+ # If we're already waiting for a button release, then ignore
523
+ # this new button press.
524
+ if (self.__canvas.bind('<ButtonRelease-1>') or
525
+ self.__canvas.bind('<ButtonRelease-2>') or
526
+ self.__canvas.bind('<ButtonRelease-3>')):
527
+ return
528
+
529
+ # Unbind motion (just in case; this shouldn't be necessary)
530
+ self.__canvas.unbind('<Motion>')
531
+
532
+ # Record the button press event.
533
+ self.__press = event
534
+
535
+ # If any ancestor is draggable, set up a motion callback.
536
+ # (Only if they pressed button number 1)
537
+ if event.num == 1:
538
+ widget = self
539
+ while widget is not None:
540
+ if widget['draggable']:
541
+ widget.__start_drag(event)
542
+ break
543
+ widget = widget.parent()
544
+
545
+ # Set up the button release callback.
546
+ self.__canvas.bind('<ButtonRelease-%d>' % event.num,
547
+ self.__release_cb)
548
+
549
+ def __start_drag(self, event):
550
+ """
551
+ Begin dragging this object:
552
+ - register a motion callback
553
+ - record the drag coordinates
554
+ """
555
+ self.__canvas.bind('<Motion>', self.__motion_cb)
556
+ self.__drag_x = event.x
557
+ self.__drag_y = event.y
558
+
559
+ def __motion_cb(self, event):
560
+ """
561
+ Handle a motion event:
562
+ - move this object to the new location
563
+ - record the new drag coordinates
564
+ """
565
+ self.move(event.x-self.__drag_x, event.y-self.__drag_y)
566
+ self.__drag_x = event.x
567
+ self.__drag_y = event.y
568
+
569
+ def __release_cb(self, event):
570
+ """
571
+ Handle a release callback:
572
+ - unregister motion & button release callbacks.
573
+ - decide whether they clicked, dragged, or cancelled
574
+ - call the appropriate handler.
575
+ """
576
+ # Unbind the button release & motion callbacks.
577
+ self.__canvas.unbind('<ButtonRelease-%d>' % event.num)
578
+ self.__canvas.unbind('<Motion>')
579
+
580
+ # Is it a click or a drag?
581
+ if (event.time - self.__press.time < 100 and
582
+ abs(event.x-self.__press.x) + abs(event.y-self.__press.y) < 5):
583
+ # Move it back, if we were dragging.
584
+ if self.__draggable and event.num == 1:
585
+ self.move(self.__press.x - self.__drag_x,
586
+ self.__press.y - self.__drag_y)
587
+ self.__click(event.num)
588
+ elif event.num == 1:
589
+ self.__drag()
590
+
591
+ self.__press = None
592
+
593
+ def __drag(self):
594
+ """
595
+ If this ``CanvasWidget`` has a drag callback, then call it;
596
+ otherwise, find the closest ancestor with a drag callback, and
597
+ call it. If no ancestors have a drag callback, do nothing.
598
+ """
599
+ if self.__draggable:
600
+ if 'drag' in self.__callbacks:
601
+ cb = self.__callbacks['drag']
602
+ try:
603
+ cb(self)
604
+ except:
605
+ print('Error in drag callback for %r' % self)
606
+ elif self.__parent is not None:
607
+ self.__parent.__drag()
608
+
609
+ def __click(self, button):
610
+ """
611
+ If this ``CanvasWidget`` has a drag callback, then call it;
612
+ otherwise, find the closest ancestor with a click callback, and
613
+ call it. If no ancestors have a click callback, do nothing.
614
+ """
615
+ if button in self.__callbacks:
616
+ cb = self.__callbacks[button]
617
+ #try:
618
+ cb(self)
619
+ #except:
620
+ # print 'Error in click callback for %r' % self
621
+ # raise
622
+ elif self.__parent is not None:
623
+ self.__parent.__click(button)
624
+
625
+ ##//////////////////////////////////////////////////////
626
+ ## Child/parent Handling
627
+ ##//////////////////////////////////////////////////////
628
+
629
+ def _add_child_widget(self, child):
630
+ """
631
+ Register a hierarchical child widget. The child will be
632
+ considered part of this canvas widget for purposes of user
633
+ interaction. ``_add_child_widget`` has two direct effects:
634
+ - It sets ``child``'s parent to this canvas widget.
635
+ - It adds ``child`` to the list of canvas widgets returned by
636
+ the ``child_widgets`` member function.
637
+
638
+ :param child: The new child widget. ``child`` must not already
639
+ have a parent.
640
+ :type child: CanvasWidget
641
+ """
642
+ if not hasattr(self, '_CanvasWidget__children'): self.__children = []
643
+ if child.__parent is not None:
644
+ raise ValueError('%s already has a parent', child)
645
+ child.__parent = self
646
+ self.__children.append(child)
647
+
648
+ def _remove_child_widget(self, child):
649
+ """
650
+ Remove a hierarchical child widget. This child will no longer
651
+ be considered part of this canvas widget for purposes of user
652
+ interaction. ``_add_child_widget`` has two direct effects:
653
+ - It sets ``child``'s parent to None.
654
+ - It removes ``child`` from the list of canvas widgets
655
+ returned by the ``child_widgets`` member function.
656
+
657
+ :param child: The child widget to remove. ``child`` must be a
658
+ child of this canvas widget.
659
+ :type child: CanvasWidget
660
+ """
661
+ self.__children.remove(child)
662
+ child.__parent = None
663
+
664
+ ##//////////////////////////////////////////////////////
665
+ ## Defined by subclass
666
+ ##//////////////////////////////////////////////////////
667
+
668
+ @abstractmethod
669
+ def _tags(self):
670
+ """
671
+ :return: a list of canvas tags for all graphical elements
672
+ managed by this canvas widget, not including graphical
673
+ elements managed by its child widgets.
674
+ :rtype: list of int
675
+ """
676
+
677
+ def _manage(self):
678
+ """
679
+ Arrange the child widgets of this canvas widget. This method
680
+ is called when the canvas widget is initially created. It is
681
+ also called if the user calls the ``manage`` method on this
682
+ canvas widget or any of its ancestors.
683
+
684
+ :rtype: None
685
+ """
686
+
687
+ def _update(self, child):
688
+ """
689
+ Update this canvas widget in response to a change in one of
690
+ its children.
691
+
692
+ :param child: The child that changed.
693
+ :type child: CanvasWidget
694
+ :rtype: None
695
+ """
696
+
697
+ ##//////////////////////////////////////////////////////
698
+ ## Basic widgets.
699
+ ##//////////////////////////////////////////////////////
700
+
701
+ class TextWidget(CanvasWidget):
702
+ """
703
+ A canvas widget that displays a single string of text.
704
+
705
+ Attributes:
706
+ - ``color``: the color of the text.
707
+ - ``font``: the font used to display the text.
708
+ - ``justify``: justification for multi-line texts. Valid values
709
+ are ``left``, ``center``, and ``right``.
710
+ - ``width``: the width of the text. If the text is wider than
711
+ this width, it will be line-wrapped at whitespace.
712
+ - ``draggable``: whether the text can be dragged by the user.
713
+ """
714
+ def __init__(self, canvas, text, **attribs):
715
+ """
716
+ Create a new text widget.
717
+
718
+ :type canvas: Tkinter.Canvas
719
+ :param canvas: This canvas widget's canvas.
720
+ :type text: str
721
+ :param text: The string of text to display.
722
+ :param attribs: The new canvas widget's attributes.
723
+ """
724
+ self._text = text
725
+ self._tag = canvas.create_text(1, 1, text=text)
726
+ CanvasWidget.__init__(self, canvas, **attribs)
727
+
728
+ def __setitem__(self, attr, value):
729
+ if attr in ('color', 'font', 'justify', 'width'):
730
+ if attr == 'color': attr = 'fill'
731
+ self.canvas().itemconfig(self._tag, {attr:value})
732
+ else:
733
+ CanvasWidget.__setitem__(self, attr, value)
734
+
735
+ def __getitem__(self, attr):
736
+ if attr == 'width':
737
+ return int(self.canvas().itemcget(self._tag, attr))
738
+ elif attr in ('color', 'font', 'justify'):
739
+ if attr == 'color': attr = 'fill'
740
+ return self.canvas().itemcget(self._tag, attr)
741
+ else:
742
+ return CanvasWidget.__getitem__(self, attr)
743
+
744
+ def _tags(self): return [self._tag]
745
+
746
+ def text(self):
747
+ """
748
+ :return: The text displayed by this text widget.
749
+ :rtype: str
750
+ """
751
+ return self.canvas().itemcget(self._tag, 'TEXT')
752
+
753
+ def set_text(self, text):
754
+ """
755
+ Change the text that is displayed by this text widget.
756
+
757
+ :type text: str
758
+ :param text: The string of text to display.
759
+ :rtype: None
760
+ """
761
+ self.canvas().itemconfig(self._tag, text=text)
762
+ if self.parent() is not None:
763
+ self.parent().update(self)
764
+
765
+ def __repr__(self):
766
+ return '[Text: %r]' % self._text
767
+
768
+ class SymbolWidget(TextWidget):
769
+ """
770
+ A canvas widget that displays special symbols, such as the
771
+ negation sign and the exists operator. Symbols are specified by
772
+ name. Currently, the following symbol names are defined: ``neg``,
773
+ ``disj``, ``conj``, ``lambda``, ``merge``, ``forall``, ``exists``,
774
+ ``subseteq``, ``subset``, ``notsubset``, ``emptyset``, ``imp``,
775
+ ``rightarrow``, ``equal``, ``notequal``, ``epsilon``.
776
+
777
+ Attributes:
778
+
779
+ - ``color``: the color of the text.
780
+ - ``draggable``: whether the text can be dragged by the user.
781
+
782
+ :cvar SYMBOLS: A dictionary mapping from symbols to the character
783
+ in the ``symbol`` font used to render them.
784
+ """
785
+ SYMBOLS = {'neg':'\330', 'disj':'\332', 'conj': '\331',
786
+ 'lambda': '\154', 'merge': '\304',
787
+ 'forall': '\042', 'exists': '\044',
788
+ 'subseteq': '\315', 'subset': '\314',
789
+ 'notsubset': '\313', 'emptyset': '\306',
790
+ 'imp': '\336', 'rightarrow': chr(222), #'\256',
791
+ 'equal': '\75', 'notequal': '\271',
792
+ 'intersection': '\307', 'union': '\310',
793
+ 'epsilon': 'e',
794
+ }
795
+
796
+ def __init__(self, canvas, symbol, **attribs):
797
+ """
798
+ Create a new symbol widget.
799
+
800
+ :type canvas: Tkinter.Canvas
801
+ :param canvas: This canvas widget's canvas.
802
+ :type symbol: str
803
+ :param symbol: The name of the symbol to display.
804
+ :param attribs: The new canvas widget's attributes.
805
+ """
806
+ attribs['font'] = 'symbol'
807
+ TextWidget.__init__(self, canvas, '', **attribs)
808
+ self.set_symbol(symbol)
809
+
810
+ def symbol(self):
811
+ """
812
+ :return: the name of the symbol that is displayed by this
813
+ symbol widget.
814
+ :rtype: str
815
+ """
816
+ return self._symbol
817
+
818
+ def set_symbol(self, symbol):
819
+ """
820
+ Change the symbol that is displayed by this symbol widget.
821
+
822
+ :type symbol: str
823
+ :param symbol: The name of the symbol to display.
824
+ """
825
+ if symbol not in SymbolWidget.SYMBOLS:
826
+ raise ValueError('Unknown symbol: %s' % symbol)
827
+ self._symbol = symbol
828
+ self.set_text(SymbolWidget.SYMBOLS[symbol])
829
+
830
+ def __repr__(self):
831
+ return '[Symbol: %r]' % self._symbol
832
+
833
+ @staticmethod
834
+ def symbolsheet(size=20):
835
+ """
836
+ Open a new Tkinter window that displays the entire alphabet
837
+ for the symbol font. This is useful for constructing the
838
+ ``SymbolWidget.SYMBOLS`` dictionary.
839
+ """
840
+ top = Tk()
841
+ def destroy(e, top=top): top.destroy()
842
+ top.bind('q', destroy)
843
+ Button(top, text='Quit', command=top.destroy).pack(side='bottom')
844
+ text = Text(top, font=('helvetica', -size), width=20, height=30)
845
+ text.pack(side='left')
846
+ sb=Scrollbar(top, command=text.yview)
847
+ text['yscrollcommand']=sb.set
848
+ sb.pack(side='right', fill='y')
849
+ text.tag_config('symbol', font=('symbol', -size))
850
+ for i in range(256):
851
+ if i in (0,10): continue # null and newline
852
+ for k,v in list(SymbolWidget.SYMBOLS.items()):
853
+ if v == chr(i):
854
+ text.insert('end', '%-10s\t' % k)
855
+ break
856
+ else:
857
+ text.insert('end', '%-10d \t' % i)
858
+ text.insert('end', '[%s]\n' % chr(i), 'symbol')
859
+ top.mainloop()
860
+
861
+
862
+ class AbstractContainerWidget(CanvasWidget):
863
+ """
864
+ An abstract class for canvas widgets that contain a single child,
865
+ such as ``BoxWidget`` and ``OvalWidget``. Subclasses must define
866
+ a constructor, which should create any new graphical elements and
867
+ then call the ``AbstractCanvasContainer`` constructor. Subclasses
868
+ must also define the ``_update`` method and the ``_tags`` method;
869
+ and any subclasses that define attributes should define
870
+ ``__setitem__`` and ``__getitem__``.
871
+ """
872
+ def __init__(self, canvas, child, **attribs):
873
+ """
874
+ Create a new container widget. This constructor should only
875
+ be called by subclass constructors.
876
+
877
+ :type canvas: Tkinter.Canvas
878
+ :param canvas: This canvas widget's canvas.
879
+ :param child: The container's child widget. ``child`` must not
880
+ have a parent.
881
+ :type child: CanvasWidget
882
+ :param attribs: The new canvas widget's attributes.
883
+ """
884
+ self._child = child
885
+ self._add_child_widget(child)
886
+ CanvasWidget.__init__(self, canvas, **attribs)
887
+
888
+ def _manage(self):
889
+ self._update(self._child)
890
+
891
+ def child(self):
892
+ """
893
+ :return: The child widget contained by this container widget.
894
+ :rtype: CanvasWidget
895
+ """
896
+ return self._child
897
+
898
+ def set_child(self, child):
899
+ """
900
+ Change the child widget contained by this container widget.
901
+
902
+ :param child: The new child widget. ``child`` must not have a
903
+ parent.
904
+ :type child: CanvasWidget
905
+ :rtype: None
906
+ """
907
+ self._remove_child_widget(self._child)
908
+ self._add_child_widget(child)
909
+ self._child = child
910
+ self.update(child)
911
+
912
+ def __repr__(self):
913
+ name = self.__class__.__name__
914
+ if name[-6:] == 'Widget': name = name[:-6]
915
+ return '[%s: %r]' % (name, self._child)
916
+
917
+ class BoxWidget(AbstractContainerWidget):
918
+ """
919
+ A canvas widget that places a box around a child widget.
920
+
921
+ Attributes:
922
+ - ``fill``: The color used to fill the interior of the box.
923
+ - ``outline``: The color used to draw the outline of the box.
924
+ - ``width``: The width of the outline of the box.
925
+ - ``margin``: The number of pixels space left between the child
926
+ and the box.
927
+ - ``draggable``: whether the text can be dragged by the user.
928
+ """
929
+ def __init__(self, canvas, child, **attribs):
930
+ """
931
+ Create a new box widget.
932
+
933
+ :type canvas: Tkinter.Canvas
934
+ :param canvas: This canvas widget's canvas.
935
+ :param child: The child widget. ``child`` must not have a
936
+ parent.
937
+ :type child: CanvasWidget
938
+ :param attribs: The new canvas widget's attributes.
939
+ """
940
+ self._child = child
941
+ self._margin = 1
942
+ self._box = canvas.create_rectangle(1,1,1,1)
943
+ canvas.tag_lower(self._box)
944
+ AbstractContainerWidget.__init__(self, canvas, child, **attribs)
945
+
946
+ def __setitem__(self, attr, value):
947
+ if attr == 'margin': self._margin = value
948
+ elif attr in ('outline', 'fill', 'width'):
949
+ self.canvas().itemconfig(self._box, {attr:value})
950
+ else:
951
+ CanvasWidget.__setitem__(self, attr, value)
952
+
953
+ def __getitem__(self, attr):
954
+ if attr == 'margin': return self._margin
955
+ elif attr == 'width':
956
+ return float(self.canvas().itemcget(self._box, attr))
957
+ elif attr in ('outline', 'fill', 'width'):
958
+ return self.canvas().itemcget(self._box, attr)
959
+ else:
960
+ return CanvasWidget.__getitem__(self, attr)
961
+
962
+ def _update(self, child):
963
+ (x1, y1, x2, y2) = child.bbox()
964
+ margin = self._margin + self['width']/2
965
+ self.canvas().coords(self._box, x1-margin, y1-margin,
966
+ x2+margin, y2+margin)
967
+
968
+ def _tags(self): return [self._box]
969
+
970
+ class OvalWidget(AbstractContainerWidget):
971
+ """
972
+ A canvas widget that places a oval around a child widget.
973
+
974
+ Attributes:
975
+ - ``fill``: The color used to fill the interior of the oval.
976
+ - ``outline``: The color used to draw the outline of the oval.
977
+ - ``width``: The width of the outline of the oval.
978
+ - ``margin``: The number of pixels space left between the child
979
+ and the oval.
980
+ - ``draggable``: whether the text can be dragged by the user.
981
+ - ``double``: If true, then a double-oval is drawn.
982
+ """
983
+ def __init__(self, canvas, child, **attribs):
984
+ """
985
+ Create a new oval widget.
986
+
987
+ :type canvas: Tkinter.Canvas
988
+ :param canvas: This canvas widget's canvas.
989
+ :param child: The child widget. ``child`` must not have a
990
+ parent.
991
+ :type child: CanvasWidget
992
+ :param attribs: The new canvas widget's attributes.
993
+ """
994
+ self._child = child
995
+ self._margin = 1
996
+ self._oval = canvas.create_oval(1,1,1,1)
997
+ self._circle = attribs.pop('circle', False)
998
+ self._double = attribs.pop('double', False)
999
+ if self._double:
1000
+ self._oval2 = canvas.create_oval(1,1,1,1)
1001
+ else:
1002
+ self._oval2 = None
1003
+ canvas.tag_lower(self._oval)
1004
+ AbstractContainerWidget.__init__(self, canvas, child, **attribs)
1005
+
1006
+ def __setitem__(self, attr, value):
1007
+ c = self.canvas()
1008
+ if attr == 'margin': self._margin = value
1009
+ elif attr == 'double':
1010
+ if value==True and self._oval2 is None:
1011
+ # Copy attributes & position from self._oval.
1012
+ x1, y1, x2, y2 = c.bbox(self._oval)
1013
+ w = self['width']*2
1014
+ self._oval2 = c.create_oval(x1-w, y1-w, x2+w, y2+w,
1015
+ outline=c.itemcget(self._oval, 'outline'),
1016
+ width=c.itemcget(self._oval, 'width'))
1017
+ c.tag_lower(self._oval2)
1018
+ if value==False and self._oval2 is not None:
1019
+ c.delete(self._oval2)
1020
+ self._oval2 = None
1021
+ elif attr in ('outline', 'fill', 'width'):
1022
+ c.itemconfig(self._oval, {attr:value})
1023
+ if self._oval2 is not None and attr!='fill':
1024
+ c.itemconfig(self._oval2, {attr:value})
1025
+ if self._oval2 is not None and attr!='fill':
1026
+ self.canvas().itemconfig(self._oval2, {attr:value})
1027
+ else:
1028
+ CanvasWidget.__setitem__(self, attr, value)
1029
+
1030
+ def __getitem__(self, attr):
1031
+ if attr == 'margin': return self._margin
1032
+ elif attr == 'double': return self._double is not None
1033
+ elif attr == 'width':
1034
+ return float(self.canvas().itemcget(self._oval, attr))
1035
+ elif attr in ('outline', 'fill', 'width'):
1036
+ return self.canvas().itemcget(self._oval, attr)
1037
+ else:
1038
+ return CanvasWidget.__getitem__(self, attr)
1039
+
1040
+ # The ratio between inscribed & circumscribed ovals
1041
+ RATIO = 1.4142135623730949
1042
+
1043
+ def _update(self, child):
1044
+ R = OvalWidget.RATIO
1045
+ (x1, y1, x2, y2) = child.bbox()
1046
+ margin = self._margin
1047
+
1048
+ # If we're a circle, pretend our contents are square.
1049
+ if self._circle:
1050
+ dx, dy = abs(x1-x2), abs(y1-y2)
1051
+ if dx > dy:
1052
+ y = (y1+y2)/2
1053
+ y1, y2 = y-dx/2, y+dx/2
1054
+ elif dy > dx:
1055
+ x = (x1+x2)/2
1056
+ x1, x2 = x-dy/2, x+dy/2
1057
+
1058
+ # Find the four corners.
1059
+ left = int(( x1*(1+R) + x2*(1-R) ) / 2)
1060
+ right = left + int((x2-x1)*R)
1061
+ top = int(( y1*(1+R) + y2*(1-R) ) / 2)
1062
+ bot = top + int((y2-y1)*R)
1063
+ self.canvas().coords(self._oval, left-margin, top-margin,
1064
+ right+margin, bot+margin)
1065
+ if self._oval2 is not None:
1066
+ self.canvas().coords(self._oval2, left-margin+2, top-margin+2,
1067
+ right+margin-2, bot+margin-2)
1068
+
1069
+ def _tags(self):
1070
+ if self._oval2 is None:
1071
+ return [self._oval]
1072
+ else:
1073
+ return [self._oval, self._oval2]
1074
+
1075
+ class ParenWidget(AbstractContainerWidget):
1076
+ """
1077
+ A canvas widget that places a pair of parenthases around a child
1078
+ widget.
1079
+
1080
+ Attributes:
1081
+ - ``color``: The color used to draw the parenthases.
1082
+ - ``width``: The width of the parenthases.
1083
+ - ``draggable``: whether the text can be dragged by the user.
1084
+ """
1085
+ def __init__(self, canvas, child, **attribs):
1086
+ """
1087
+ Create a new parenthasis widget.
1088
+
1089
+ :type canvas: Tkinter.Canvas
1090
+ :param canvas: This canvas widget's canvas.
1091
+ :param child: The child widget. ``child`` must not have a
1092
+ parent.
1093
+ :type child: CanvasWidget
1094
+ :param attribs: The new canvas widget's attributes.
1095
+ """
1096
+ self._child = child
1097
+ self._oparen = canvas.create_arc(1,1,1,1, style='arc',
1098
+ start=90, extent=180)
1099
+ self._cparen = canvas.create_arc(1,1,1,1, style='arc',
1100
+ start=-90, extent=180)
1101
+ AbstractContainerWidget.__init__(self, canvas, child, **attribs)
1102
+
1103
+ def __setitem__(self, attr, value):
1104
+ if attr == 'color':
1105
+ self.canvas().itemconfig(self._oparen, outline=value)
1106
+ self.canvas().itemconfig(self._cparen, outline=value)
1107
+ elif attr == 'width':
1108
+ self.canvas().itemconfig(self._oparen, width=value)
1109
+ self.canvas().itemconfig(self._cparen, width=value)
1110
+ else:
1111
+ CanvasWidget.__setitem__(self, attr, value)
1112
+
1113
+ def __getitem__(self, attr):
1114
+ if attr == 'color':
1115
+ return self.canvas().itemcget(self._oparen, 'outline')
1116
+ elif attr == 'width':
1117
+ return self.canvas().itemcget(self._oparen, 'width')
1118
+ else:
1119
+ return CanvasWidget.__getitem__(self, attr)
1120
+
1121
+ def _update(self, child):
1122
+ (x1, y1, x2, y2) = child.bbox()
1123
+ width = max((y2-y1)/6, 4)
1124
+ self.canvas().coords(self._oparen, x1-width, y1, x1+width, y2)
1125
+ self.canvas().coords(self._cparen, x2-width, y1, x2+width, y2)
1126
+
1127
+ def _tags(self): return [self._oparen, self._cparen]
1128
+
1129
+ class BracketWidget(AbstractContainerWidget):
1130
+ """
1131
+ A canvas widget that places a pair of brackets around a child
1132
+ widget.
1133
+
1134
+ Attributes:
1135
+ - ``color``: The color used to draw the brackets.
1136
+ - ``width``: The width of the brackets.
1137
+ - ``draggable``: whether the text can be dragged by the user.
1138
+ """
1139
+ def __init__(self, canvas, child, **attribs):
1140
+ """
1141
+ Create a new bracket widget.
1142
+
1143
+ :type canvas: Tkinter.Canvas
1144
+ :param canvas: This canvas widget's canvas.
1145
+ :param child: The child widget. ``child`` must not have a
1146
+ parent.
1147
+ :type child: CanvasWidget
1148
+ :param attribs: The new canvas widget's attributes.
1149
+ """
1150
+ self._child = child
1151
+ self._obrack = canvas.create_line(1,1,1,1,1,1,1,1)
1152
+ self._cbrack = canvas.create_line(1,1,1,1,1,1,1,1)
1153
+ AbstractContainerWidget.__init__(self, canvas, child, **attribs)
1154
+
1155
+ def __setitem__(self, attr, value):
1156
+ if attr == 'color':
1157
+ self.canvas().itemconfig(self._obrack, fill=value)
1158
+ self.canvas().itemconfig(self._cbrack, fill=value)
1159
+ elif attr == 'width':
1160
+ self.canvas().itemconfig(self._obrack, width=value)
1161
+ self.canvas().itemconfig(self._cbrack, width=value)
1162
+ else:
1163
+ CanvasWidget.__setitem__(self, attr, value)
1164
+
1165
+ def __getitem__(self, attr):
1166
+ if attr == 'color':
1167
+ return self.canvas().itemcget(self._obrack, 'outline')
1168
+ elif attr == 'width':
1169
+ return self.canvas().itemcget(self._obrack, 'width')
1170
+ else:
1171
+ return CanvasWidget.__getitem__(self, attr)
1172
+
1173
+ def _update(self, child):
1174
+ (x1, y1, x2, y2) = child.bbox()
1175
+ width = max((y2-y1)/8, 2)
1176
+ self.canvas().coords(self._obrack, x1, y1, x1-width, y1,
1177
+ x1-width, y2, x1, y2)
1178
+ self.canvas().coords(self._cbrack, x2, y1, x2+width, y1,
1179
+ x2+width, y2, x2, y2)
1180
+
1181
+ def _tags(self): return [self._obrack, self._cbrack]
1182
+
1183
+ class SequenceWidget(CanvasWidget):
1184
+ """
1185
+ A canvas widget that keeps a list of canvas widgets in a
1186
+ horizontal line.
1187
+
1188
+ Attributes:
1189
+ - ``align``: The vertical alignment of the children. Possible
1190
+ values are ``'top'``, ``'center'``, and ``'bottom'``. By
1191
+ default, children are center-aligned.
1192
+ - ``space``: The amount of horizontal space to place between
1193
+ children. By default, one pixel of space is used.
1194
+ - ``ordered``: If true, then keep the children in their
1195
+ original order.
1196
+ """
1197
+ def __init__(self, canvas, *children, **attribs):
1198
+ """
1199
+ Create a new sequence widget.
1200
+
1201
+ :type canvas: Tkinter.Canvas
1202
+ :param canvas: This canvas widget's canvas.
1203
+ :param children: The widgets that should be aligned
1204
+ horizontally. Each child must not have a parent.
1205
+ :type children: list(CanvasWidget)
1206
+ :param attribs: The new canvas widget's attributes.
1207
+ """
1208
+ self._align = 'center'
1209
+ self._space = 1
1210
+ self._ordered = False
1211
+ self._children = list(children)
1212
+ for child in children: self._add_child_widget(child)
1213
+ CanvasWidget.__init__(self, canvas, **attribs)
1214
+
1215
+ def __setitem__(self, attr, value):
1216
+ if attr == 'align':
1217
+ if value not in ('top', 'bottom', 'center'):
1218
+ raise ValueError('Bad alignment: %r' % value)
1219
+ self._align = value
1220
+ elif attr == 'space': self._space = value
1221
+ elif attr == 'ordered': self._ordered = value
1222
+ else: CanvasWidget.__setitem__(self, attr, value)
1223
+
1224
+ def __getitem__(self, attr):
1225
+ if attr == 'align': return self._align
1226
+ elif attr == 'space': return self._space
1227
+ elif attr == 'ordered': return self._ordered
1228
+ else: return CanvasWidget.__getitem__(self, attr)
1229
+
1230
+ def _tags(self): return []
1231
+
1232
+ def _yalign(self, top, bot):
1233
+ if self._align == 'top': return top
1234
+ if self._align == 'bottom': return bot
1235
+ if self._align == 'center': return (top+bot)/2
1236
+
1237
+ def _update(self, child):
1238
+ # Align all children with child.
1239
+ (left, top, right, bot) = child.bbox()
1240
+ y = self._yalign(top, bot)
1241
+ for c in self._children:
1242
+ (x1, y1, x2, y2) = c.bbox()
1243
+ c.move(0, y-self._yalign(y1,y2))
1244
+
1245
+ if self._ordered and len(self._children) > 1:
1246
+ index = self._children.index(child)
1247
+
1248
+ x = right + self._space
1249
+ for i in range(index+1, len(self._children)):
1250
+ (x1, y1, x2, y2) = self._children[i].bbox()
1251
+ if x > x1:
1252
+ self._children[i].move(x-x1, 0)
1253
+ x += x2-x1 + self._space
1254
+
1255
+ x = left - self._space
1256
+ for i in range(index-1, -1, -1):
1257
+ (x1, y1, x2, y2) = self._children[i].bbox()
1258
+ if x < x2:
1259
+ self._children[i].move(x-x2, 0)
1260
+ x -= x2-x1 + self._space
1261
+
1262
+ def _manage(self):
1263
+ if len(self._children) == 0: return
1264
+ child = self._children[0]
1265
+
1266
+ # Align all children with child.
1267
+ (left, top, right, bot) = child.bbox()
1268
+ y = self._yalign(top, bot)
1269
+
1270
+ index = self._children.index(child)
1271
+
1272
+ # Line up children to the right of child.
1273
+ x = right + self._space
1274
+ for i in range(index+1, len(self._children)):
1275
+ (x1, y1, x2, y2) = self._children[i].bbox()
1276
+ self._children[i].move(x-x1, y-self._yalign(y1,y2))
1277
+ x += x2-x1 + self._space
1278
+
1279
+ # Line up children to the left of child.
1280
+ x = left - self._space
1281
+ for i in range(index-1, -1, -1):
1282
+ (x1, y1, x2, y2) = self._children[i].bbox()
1283
+ self._children[i].move(x-x2, y-self._yalign(y1,y2))
1284
+ x -= x2-x1 + self._space
1285
+
1286
+ def __repr__(self):
1287
+ return '[Sequence: ' + repr(self._children)[1:-1]+']'
1288
+
1289
+ # Provide an alias for the child_widgets() member.
1290
+ children = CanvasWidget.child_widgets
1291
+
1292
+ def replace_child(self, oldchild, newchild):
1293
+ """
1294
+ Replace the child canvas widget ``oldchild`` with ``newchild``.
1295
+ ``newchild`` must not have a parent. ``oldchild``'s parent will
1296
+ be set to None.
1297
+
1298
+ :type oldchild: CanvasWidget
1299
+ :param oldchild: The child canvas widget to remove.
1300
+ :type newchild: CanvasWidget
1301
+ :param newchild: The canvas widget that should replace
1302
+ ``oldchild``.
1303
+ """
1304
+ index = self._children.index(oldchild)
1305
+ self._children[index] = newchild
1306
+ self._remove_child_widget(oldchild)
1307
+ self._add_child_widget(newchild)
1308
+ self.update(newchild)
1309
+
1310
+ def remove_child(self, child):
1311
+ """
1312
+ Remove the given child canvas widget. ``child``'s parent will
1313
+ be set ot None.
1314
+
1315
+ :type child: CanvasWidget
1316
+ :param child: The child canvas widget to remove.
1317
+ """
1318
+ index = self._children.index(child)
1319
+ del self._children[index]
1320
+ self._remove_child_widget(child)
1321
+ if len(self._children) > 0:
1322
+ self.update(self._children[0])
1323
+
1324
+ def insert_child(self, index, child):
1325
+ """
1326
+ Insert a child canvas widget before a given index.
1327
+
1328
+ :type child: CanvasWidget
1329
+ :param child: The canvas widget that should be inserted.
1330
+ :type index: int
1331
+ :param index: The index where the child widget should be
1332
+ inserted. In particular, the index of ``child`` will be
1333
+ ``index``; and the index of any children whose indices were
1334
+ greater than equal to ``index`` before ``child`` was
1335
+ inserted will be incremented by one.
1336
+ """
1337
+ self._children.insert(index, child)
1338
+ self._add_child_widget(child)
1339
+
1340
+ class StackWidget(CanvasWidget):
1341
+ """
1342
+ A canvas widget that keeps a list of canvas widgets in a vertical
1343
+ line.
1344
+
1345
+ Attributes:
1346
+ - ``align``: The horizontal alignment of the children. Possible
1347
+ values are ``'left'``, ``'center'``, and ``'right'``. By
1348
+ default, children are center-aligned.
1349
+ - ``space``: The amount of vertical space to place between
1350
+ children. By default, one pixel of space is used.
1351
+ - ``ordered``: If true, then keep the children in their
1352
+ original order.
1353
+ """
1354
+ def __init__(self, canvas, *children, **attribs):
1355
+ """
1356
+ Create a new stack widget.
1357
+
1358
+ :type canvas: Tkinter.Canvas
1359
+ :param canvas: This canvas widget's canvas.
1360
+ :param children: The widgets that should be aligned
1361
+ vertically. Each child must not have a parent.
1362
+ :type children: list(CanvasWidget)
1363
+ :param attribs: The new canvas widget's attributes.
1364
+ """
1365
+ self._align = 'center'
1366
+ self._space = 1
1367
+ self._ordered = False
1368
+ self._children = list(children)
1369
+ for child in children: self._add_child_widget(child)
1370
+ CanvasWidget.__init__(self, canvas, **attribs)
1371
+
1372
+ def __setitem__(self, attr, value):
1373
+ if attr == 'align':
1374
+ if value not in ('left', 'right', 'center'):
1375
+ raise ValueError('Bad alignment: %r' % value)
1376
+ self._align = value
1377
+ elif attr == 'space': self._space = value
1378
+ elif attr == 'ordered': self._ordered = value
1379
+ else: CanvasWidget.__setitem__(self, attr, value)
1380
+
1381
+ def __getitem__(self, attr):
1382
+ if attr == 'align': return self._align
1383
+ elif attr == 'space': return self._space
1384
+ elif attr == 'ordered': return self._ordered
1385
+ else: return CanvasWidget.__getitem__(self, attr)
1386
+
1387
+ def _tags(self): return []
1388
+
1389
+ def _xalign(self, left, right):
1390
+ if self._align == 'left': return left
1391
+ if self._align == 'right': return right
1392
+ if self._align == 'center': return (left+right)/2
1393
+
1394
+ def _update(self, child):
1395
+ # Align all children with child.
1396
+ (left, top, right, bot) = child.bbox()
1397
+ x = self._xalign(left, right)
1398
+ for c in self._children:
1399
+ (x1, y1, x2, y2) = c.bbox()
1400
+ c.move(x-self._xalign(x1,x2), 0)
1401
+
1402
+ if self._ordered and len(self._children) > 1:
1403
+ index = self._children.index(child)
1404
+
1405
+ y = bot + self._space
1406
+ for i in range(index+1, len(self._children)):
1407
+ (x1, y1, x2, y2) = self._children[i].bbox()
1408
+ if y > y1:
1409
+ self._children[i].move(0, y-y1)
1410
+ y += y2-y1 + self._space
1411
+
1412
+ y = top - self._space
1413
+ for i in range(index-1, -1, -1):
1414
+ (x1, y1, x2, y2) = self._children[i].bbox()
1415
+ if y < y2:
1416
+ self._children[i].move(0, y-y2)
1417
+ y -= y2-y1 + self._space
1418
+
1419
+ def _manage(self):
1420
+ if len(self._children) == 0: return
1421
+ child = self._children[0]
1422
+
1423
+ # Align all children with child.
1424
+ (left, top, right, bot) = child.bbox()
1425
+ x = self._xalign(left, right)
1426
+
1427
+ index = self._children.index(child)
1428
+
1429
+ # Line up children below the child.
1430
+ y = bot + self._space
1431
+ for i in range(index+1, len(self._children)):
1432
+ (x1, y1, x2, y2) = self._children[i].bbox()
1433
+ self._children[i].move(x-self._xalign(x1,x2), y-y1)
1434
+ y += y2-y1 + self._space
1435
+
1436
+ # Line up children above the child.
1437
+ y = top - self._space
1438
+ for i in range(index-1, -1, -1):
1439
+ (x1, y1, x2, y2) = self._children[i].bbox()
1440
+ self._children[i].move(x-self._xalign(x1,x2), y-y2)
1441
+ y -= y2-y1 + self._space
1442
+
1443
+ def __repr__(self):
1444
+ return '[Stack: ' + repr(self._children)[1:-1]+']'
1445
+
1446
+ # Provide an alias for the child_widgets() member.
1447
+ children = CanvasWidget.child_widgets
1448
+
1449
+ def replace_child(self, oldchild, newchild):
1450
+ """
1451
+ Replace the child canvas widget ``oldchild`` with ``newchild``.
1452
+ ``newchild`` must not have a parent. ``oldchild``'s parent will
1453
+ be set to None.
1454
+
1455
+ :type oldchild: CanvasWidget
1456
+ :param oldchild: The child canvas widget to remove.
1457
+ :type newchild: CanvasWidget
1458
+ :param newchild: The canvas widget that should replace
1459
+ ``oldchild``.
1460
+ """
1461
+ index = self._children.index(oldchild)
1462
+ self._children[index] = newchild
1463
+ self._remove_child_widget(oldchild)
1464
+ self._add_child_widget(newchild)
1465
+ self.update(newchild)
1466
+
1467
+ def remove_child(self, child):
1468
+ """
1469
+ Remove the given child canvas widget. ``child``'s parent will
1470
+ be set ot None.
1471
+
1472
+ :type child: CanvasWidget
1473
+ :param child: The child canvas widget to remove.
1474
+ """
1475
+ index = self._children.index(child)
1476
+ del self._children[index]
1477
+ self._remove_child_widget(child)
1478
+ if len(self._children) > 0:
1479
+ self.update(self._children[0])
1480
+
1481
+ def insert_child(self, index, child):
1482
+ """
1483
+ Insert a child canvas widget before a given index.
1484
+
1485
+ :type child: CanvasWidget
1486
+ :param child: The canvas widget that should be inserted.
1487
+ :type index: int
1488
+ :param index: The index where the child widget should be
1489
+ inserted. In particular, the index of ``child`` will be
1490
+ ``index``; and the index of any children whose indices were
1491
+ greater than equal to ``index`` before ``child`` was
1492
+ inserted will be incremented by one.
1493
+ """
1494
+ self._children.insert(index, child)
1495
+ self._add_child_widget(child)
1496
+
1497
+ class SpaceWidget(CanvasWidget):
1498
+ """
1499
+ A canvas widget that takes up space but does not display
1500
+ anything. A ``SpaceWidget`` can be used to add space between
1501
+ elements. Each space widget is characterized by a width and a
1502
+ height. If you wish to only create horizontal space, then use a
1503
+ height of zero; and if you wish to only create vertical space, use
1504
+ a width of zero.
1505
+ """
1506
+ def __init__(self, canvas, width, height, **attribs):
1507
+ """
1508
+ Create a new space widget.
1509
+
1510
+ :type canvas: Tkinter.Canvas
1511
+ :param canvas: This canvas widget's canvas.
1512
+ :type width: int
1513
+ :param width: The width of the new space widget.
1514
+ :type height: int
1515
+ :param height: The height of the new space widget.
1516
+ :param attribs: The new canvas widget's attributes.
1517
+ """
1518
+ # For some reason,
1519
+ if width > 4: width -= 4
1520
+ if height > 4: height -= 4
1521
+ self._tag = canvas.create_line(1, 1, width, height, fill='')
1522
+ CanvasWidget.__init__(self, canvas, **attribs)
1523
+
1524
+ # note: width() and height() are already defined by CanvasWidget.
1525
+ def set_width(self, width):
1526
+ """
1527
+ Change the width of this space widget.
1528
+
1529
+ :param width: The new width.
1530
+ :type width: int
1531
+ :rtype: None
1532
+ """
1533
+ [x1, y1, x2, y2] = self.bbox()
1534
+ self.canvas().coords(self._tag, x1, y1, x1+width, y2)
1535
+
1536
+ def set_height(self, height):
1537
+ """
1538
+ Change the height of this space widget.
1539
+
1540
+ :param height: The new height.
1541
+ :type height: int
1542
+ :rtype: None
1543
+ """
1544
+ [x1, y1, x2, y2] = self.bbox()
1545
+ self.canvas().coords(self._tag, x1, y1, x2, y1+height)
1546
+
1547
+ def _tags(self): return [self._tag]
1548
+
1549
+ def __repr__(self): return '[Space]'
1550
+
1551
+ class ScrollWatcherWidget(CanvasWidget):
1552
+ """
1553
+ A special canvas widget that adjusts its ``Canvas``'s scrollregion
1554
+ to always include the bounding boxes of all of its children. The
1555
+ scroll-watcher widget will only increase the size of the
1556
+ ``Canvas``'s scrollregion; it will never decrease it.
1557
+ """
1558
+ def __init__(self, canvas, *children, **attribs):
1559
+ """
1560
+ Create a new scroll-watcher widget.
1561
+
1562
+ :type canvas: Tkinter.Canvas
1563
+ :param canvas: This canvas widget's canvas.
1564
+ :type children: list(CanvasWidget)
1565
+ :param children: The canvas widgets watched by the
1566
+ scroll-watcher. The scroll-watcher will ensure that these
1567
+ canvas widgets are always contained in their canvas's
1568
+ scrollregion.
1569
+ :param attribs: The new canvas widget's attributes.
1570
+ """
1571
+ for child in children: self._add_child_widget(child)
1572
+ CanvasWidget.__init__(self, canvas, **attribs)
1573
+
1574
+ def add_child(self, canvaswidget):
1575
+ """
1576
+ Add a new canvas widget to the scroll-watcher. The
1577
+ scroll-watcher will ensure that the new canvas widget is
1578
+ always contained in its canvas's scrollregion.
1579
+
1580
+ :param canvaswidget: The new canvas widget.
1581
+ :type canvaswidget: CanvasWidget
1582
+ :rtype: None
1583
+ """
1584
+ self._add_child_widget(canvaswidget)
1585
+ self.update(canvaswidget)
1586
+
1587
+ def remove_child(self, canvaswidget):
1588
+ """
1589
+ Remove a canvas widget from the scroll-watcher. The
1590
+ scroll-watcher will no longer ensure that the new canvas
1591
+ widget is always contained in its canvas's scrollregion.
1592
+
1593
+ :param canvaswidget: The canvas widget to remove.
1594
+ :type canvaswidget: CanvasWidget
1595
+ :rtype: None
1596
+ """
1597
+ self._remove_child_widget(canvaswidget)
1598
+
1599
+ def _tags(self): return []
1600
+
1601
+ def _update(self, child):
1602
+ self._adjust_scrollregion()
1603
+
1604
+ def _adjust_scrollregion(self):
1605
+ """
1606
+ Adjust the scrollregion of this scroll-watcher's ``Canvas`` to
1607
+ include the bounding boxes of all of its children.
1608
+ """
1609
+ bbox = self.bbox()
1610
+ canvas = self.canvas()
1611
+ scrollregion = [int(n) for n in canvas['scrollregion'].split()]
1612
+ if len(scrollregion) != 4: return
1613
+ if (bbox[0] < scrollregion[0] or bbox[1] < scrollregion[1] or
1614
+ bbox[2] > scrollregion[2] or bbox[3] > scrollregion[3]):
1615
+ scrollregion = ('%d %d %d %d' %
1616
+ (min(bbox[0], scrollregion[0]),
1617
+ min(bbox[1], scrollregion[1]),
1618
+ max(bbox[2], scrollregion[2]),
1619
+ max(bbox[3], scrollregion[3])))
1620
+ canvas['scrollregion'] = scrollregion
1621
+
1622
+ ##//////////////////////////////////////////////////////
1623
+ ## Canvas Frame
1624
+ ##//////////////////////////////////////////////////////
1625
+
1626
+ class CanvasFrame(object):
1627
+ """
1628
+ A ``Tkinter`` frame containing a canvas and scrollbars.
1629
+ ``CanvasFrame`` uses a ``ScrollWatcherWidget`` to ensure that all of
1630
+ the canvas widgets contained on its canvas are within its
1631
+ scrollregion. In order for ``CanvasFrame`` to make these checks,
1632
+ all canvas widgets must be registered with ``add_widget`` when they
1633
+ are added to the canvas; and destroyed with ``destroy_widget`` when
1634
+ they are no longer needed.
1635
+
1636
+ If a ``CanvasFrame`` is created with no parent, then it will create
1637
+ its own main window, including a "Done" button and a "Print"
1638
+ button.
1639
+ """
1640
+ def __init__(self, parent=None, **kw):
1641
+ """
1642
+ Create a new ``CanvasFrame``.
1643
+
1644
+ :type parent: Tkinter.BaseWidget or Tkinter.Tk
1645
+ :param parent: The parent ``Tkinter`` widget. If no parent is
1646
+ specified, then ``CanvasFrame`` will create a new main
1647
+ window.
1648
+ :param kw: Keyword arguments for the new ``Canvas``. See the
1649
+ documentation for ``Tkinter.Canvas`` for more information.
1650
+ """
1651
+ # If no parent was given, set up a top-level window.
1652
+ if parent is None:
1653
+ self._parent = Tk()
1654
+ self._parent.title('NLTK')
1655
+ self._parent.bind('<Control-p>', lambda e: self.print_to_file())
1656
+ self._parent.bind('<Control-x>', self.destroy)
1657
+ self._parent.bind('<Control-q>', self.destroy)
1658
+ else:
1659
+ self._parent = parent
1660
+
1661
+ # Create a frame for the canvas & scrollbars
1662
+ self._frame = frame = Frame(self._parent)
1663
+ self._canvas = canvas = Canvas(frame, **kw)
1664
+ xscrollbar = Scrollbar(self._frame, orient='horizontal')
1665
+ yscrollbar = Scrollbar(self._frame, orient='vertical')
1666
+ xscrollbar['command'] = canvas.xview
1667
+ yscrollbar['command'] = canvas.yview
1668
+ canvas['xscrollcommand'] = xscrollbar.set
1669
+ canvas['yscrollcommand'] = yscrollbar.set
1670
+ yscrollbar.pack(fill='y', side='right')
1671
+ xscrollbar.pack(fill='x', side='bottom')
1672
+ canvas.pack(expand=1, fill='both', side='left')
1673
+
1674
+ # Set initial scroll region.
1675
+ scrollregion = '0 0 %s %s' % (canvas['width'], canvas['height'])
1676
+ canvas['scrollregion'] = scrollregion
1677
+
1678
+ self._scrollwatcher = ScrollWatcherWidget(canvas)
1679
+
1680
+ # If no parent was given, pack the frame, and add a menu.
1681
+ if parent is None:
1682
+ self.pack(expand=1, fill='both')
1683
+ self._init_menubar()
1684
+
1685
+ def _init_menubar(self):
1686
+ menubar = Menu(self._parent)
1687
+
1688
+ filemenu = Menu(menubar, tearoff=0)
1689
+ filemenu.add_command(label='Print to Postscript', underline=0,
1690
+ command=self.print_to_file, accelerator='Ctrl-p')
1691
+ filemenu.add_command(label='Exit', underline=1,
1692
+ command=self.destroy, accelerator='Ctrl-x')
1693
+ menubar.add_cascade(label='File', underline=0, menu=filemenu)
1694
+
1695
+ self._parent.config(menu=menubar)
1696
+
1697
+ def print_to_file(self, filename=None):
1698
+ """
1699
+ Print the contents of this ``CanvasFrame`` to a postscript
1700
+ file. If no filename is given, then prompt the user for one.
1701
+
1702
+ :param filename: The name of the file to print the tree to.
1703
+ :type filename: str
1704
+ :rtype: None
1705
+ """
1706
+ if filename is None:
1707
+ ftypes = [('Postscript files', '.ps'),
1708
+ ('All files', '*')]
1709
+ filename = asksaveasfilename(filetypes=ftypes,
1710
+ defaultextension='.ps')
1711
+ if not filename: return
1712
+ (x0, y0, w, h) = self.scrollregion()
1713
+ postscript = self._canvas.postscript(x=x0, y=y0,
1714
+ width=w+2, height=h+2,
1715
+ pagewidth=w+2, # points = 1/72 inch
1716
+ pageheight=h+2, # points = 1/72 inch
1717
+ pagex=0, pagey=0)
1718
+ # workaround for bug in Tk font handling
1719
+ postscript = postscript.replace(' 0 scalefont ', ' 9 scalefont ')
1720
+ with open(filename, 'wb') as f:
1721
+ f.write(postscript.encode('utf8'))
1722
+
1723
+ def scrollregion(self):
1724
+ """
1725
+ :return: The current scroll region for the canvas managed by
1726
+ this ``CanvasFrame``.
1727
+ :rtype: 4-tuple of int
1728
+ """
1729
+ (x1, y1, x2, y2) = self._canvas['scrollregion'].split()
1730
+ return (int(x1), int(y1), int(x2), int(y2))
1731
+
1732
+ def canvas(self):
1733
+ """
1734
+ :return: The canvas managed by this ``CanvasFrame``.
1735
+ :rtype: Tkinter.Canvas
1736
+ """
1737
+ return self._canvas
1738
+
1739
+ def add_widget(self, canvaswidget, x=None, y=None):
1740
+ """
1741
+ Register a canvas widget with this ``CanvasFrame``. The
1742
+ ``CanvasFrame`` will ensure that this canvas widget is always
1743
+ within the ``Canvas``'s scrollregion. If no coordinates are
1744
+ given for the canvas widget, then the ``CanvasFrame`` will
1745
+ attempt to find a clear area of the canvas for it.
1746
+
1747
+ :type canvaswidget: CanvasWidget
1748
+ :param canvaswidget: The new canvas widget. ``canvaswidget``
1749
+ must have been created on this ``CanvasFrame``'s canvas.
1750
+ :type x: int
1751
+ :param x: The initial x coordinate for the upper left hand
1752
+ corner of ``canvaswidget``, in the canvas's coordinate
1753
+ space.
1754
+ :type y: int
1755
+ :param y: The initial y coordinate for the upper left hand
1756
+ corner of ``canvaswidget``, in the canvas's coordinate
1757
+ space.
1758
+ """
1759
+ if x is None or y is None:
1760
+ (x, y) = self._find_room(canvaswidget, x, y)
1761
+
1762
+ # Move to (x,y)
1763
+ (x1,y1,x2,y2) = canvaswidget.bbox()
1764
+ canvaswidget.move(x-x1,y-y1)
1765
+
1766
+ # Register with scrollwatcher.
1767
+ self._scrollwatcher.add_child(canvaswidget)
1768
+
1769
+ def _find_room(self, widget, desired_x, desired_y):
1770
+ """
1771
+ Try to find a space for a given widget.
1772
+ """
1773
+ (left, top, right, bot) = self.scrollregion()
1774
+ w = widget.width()
1775
+ h = widget.height()
1776
+
1777
+ if w >= (right-left): return (0,0)
1778
+ if h >= (bot-top): return (0,0)
1779
+
1780
+ # Move the widget out of the way, for now.
1781
+ (x1,y1,x2,y2) = widget.bbox()
1782
+ widget.move(left-x2-50, top-y2-50)
1783
+
1784
+ if desired_x is not None:
1785
+ x = desired_x
1786
+ for y in range(top, bot-h, int((bot-top-h)/10)):
1787
+ if not self._canvas.find_overlapping(x-5, y-5, x+w+5, y+h+5):
1788
+ return (x,y)
1789
+
1790
+ if desired_y is not None:
1791
+ y = desired_y
1792
+ for x in range(left, right-w, int((right-left-w)/10)):
1793
+ if not self._canvas.find_overlapping(x-5, y-5, x+w+5, y+h+5):
1794
+ return (x,y)
1795
+
1796
+ for y in range(top, bot-h, int((bot-top-h)/10)):
1797
+ for x in range(left, right-w, int((right-left-w)/10)):
1798
+ if not self._canvas.find_overlapping(x-5, y-5, x+w+5, y+h+5):
1799
+ return (x,y)
1800
+ return (0,0)
1801
+
1802
+ def destroy_widget(self, canvaswidget):
1803
+ """
1804
+ Remove a canvas widget from this ``CanvasFrame``. This
1805
+ deregisters the canvas widget, and destroys it.
1806
+ """
1807
+ self.remove_widget(canvaswidget)
1808
+ canvaswidget.destroy()
1809
+
1810
+ def remove_widget(self, canvaswidget):
1811
+ # Deregister with scrollwatcher.
1812
+ self._scrollwatcher.remove_child(canvaswidget)
1813
+
1814
+ def pack(self, cnf={}, **kw):
1815
+ """
1816
+ Pack this ``CanvasFrame``. See the documentation for
1817
+ ``Tkinter.Pack`` for more information.
1818
+ """
1819
+ self._frame.pack(cnf, **kw)
1820
+ # Adjust to be big enough for kids?
1821
+
1822
+ def destroy(self, *e):
1823
+ """
1824
+ Destroy this ``CanvasFrame``. If this ``CanvasFrame`` created a
1825
+ top-level window, then this will close that window.
1826
+ """
1827
+ if self._parent is None: return
1828
+ self._parent.destroy()
1829
+ self._parent = None
1830
+
1831
+ def mainloop(self, *args, **kwargs):
1832
+ """
1833
+ Enter the Tkinter mainloop. This function must be called if
1834
+ this frame is created from a non-interactive program (e.g.
1835
+ from a secript); otherwise, the frame will close as soon as
1836
+ the script completes.
1837
+ """
1838
+ if in_idle(): return
1839
+ self._parent.mainloop(*args, **kwargs)
1840
+
1841
+ ##//////////////////////////////////////////////////////
1842
+ ## Text display
1843
+ ##//////////////////////////////////////////////////////
1844
+
1845
+ class ShowText(object):
1846
+ """
1847
+ A ``Tkinter`` window used to display a text. ``ShowText`` is
1848
+ typically used by graphical tools to display help text, or similar
1849
+ information.
1850
+ """
1851
+ def __init__(self, root, title, text, width=None, height=None,
1852
+ **textbox_options):
1853
+ if width is None or height is None:
1854
+ (width, height) = self.find_dimentions(text, width, height)
1855
+
1856
+ # Create the main window.
1857
+ if root is None:
1858
+ self._top = top = Tk()
1859
+ else:
1860
+ self._top = top = Toplevel(root)
1861
+ top.title(title)
1862
+
1863
+ b = Button(top, text='Ok', command=self.destroy)
1864
+ b.pack(side='bottom')
1865
+
1866
+ tbf = Frame(top)
1867
+ tbf.pack(expand=1, fill='both')
1868
+ scrollbar = Scrollbar(tbf, orient='vertical')
1869
+ scrollbar.pack(side='right', fill='y')
1870
+ textbox = Text(tbf, wrap='word', width=width,
1871
+ height=height, **textbox_options)
1872
+ textbox.insert('end', text)
1873
+ textbox['state'] = 'disabled'
1874
+ textbox.pack(side='left', expand=1, fill='both')
1875
+ scrollbar['command'] = textbox.yview
1876
+ textbox['yscrollcommand'] = scrollbar.set
1877
+
1878
+ # Make it easy to close the window.
1879
+ top.bind('q', self.destroy)
1880
+ top.bind('x', self.destroy)
1881
+ top.bind('c', self.destroy)
1882
+ top.bind('<Return>', self.destroy)
1883
+ top.bind('<Escape>', self.destroy)
1884
+
1885
+ # Focus the scrollbar, so they can use up/down, etc.
1886
+ scrollbar.focus()
1887
+
1888
+ def find_dimentions(self, text, width, height):
1889
+ lines = text.split('\n')
1890
+ if width is None:
1891
+ maxwidth = max(len(line) for line in lines)
1892
+ width = min(maxwidth, 80)
1893
+
1894
+ # Now, find height.
1895
+ height = 0
1896
+ for line in lines:
1897
+ while len(line) > width:
1898
+ brk = line[:width].rfind(' ')
1899
+ line = line[brk:]
1900
+ height += 1
1901
+ height += 1
1902
+ height = min(height, 25)
1903
+
1904
+ return (width, height)
1905
+
1906
+ def destroy(self, *e):
1907
+ if self._top is None: return
1908
+ self._top.destroy()
1909
+ self._top = None
1910
+
1911
+ def mainloop(self, *args, **kwargs):
1912
+ """
1913
+ Enter the Tkinter mainloop. This function must be called if
1914
+ this window is created from a non-interactive program (e.g.
1915
+ from a secript); otherwise, the window will close as soon as
1916
+ the script completes.
1917
+ """
1918
+ if in_idle(): return
1919
+ self._top.mainloop(*args, **kwargs)
1920
+
1921
+ ##//////////////////////////////////////////////////////
1922
+ ## Entry dialog
1923
+ ##//////////////////////////////////////////////////////
1924
+
1925
+ class EntryDialog(object):
1926
+ """
1927
+ A dialog box for entering
1928
+ """
1929
+ def __init__(self, parent, original_text='', instructions='',
1930
+ set_callback=None, title=None):
1931
+ self._parent = parent
1932
+ self._original_text = original_text
1933
+ self._set_callback = set_callback
1934
+
1935
+ width = int(max(30, len(original_text)*3/2))
1936
+ self._top = Toplevel(parent)
1937
+
1938
+ if title: self._top.title(title)
1939
+
1940
+ # The text entry box.
1941
+ entryframe = Frame(self._top)
1942
+ entryframe.pack(expand=1, fill='both', padx=5, pady=5,ipady=10)
1943
+ if instructions:
1944
+ l=Label(entryframe, text=instructions)
1945
+ l.pack(side='top', anchor='w', padx=30)
1946
+ self._entry = Entry(entryframe, width=width)
1947
+ self._entry.pack(expand=1, fill='x', padx=30)
1948
+ self._entry.insert(0, original_text)
1949
+
1950
+ # A divider
1951
+ divider = Frame(self._top, borderwidth=1, relief='sunken')
1952
+ divider.pack(fill='x', ipady=1, padx=10)
1953
+
1954
+ # The buttons.
1955
+ buttons = Frame(self._top)
1956
+ buttons.pack(expand=0, fill='x', padx=5, pady=5)
1957
+ b = Button(buttons, text='Cancel', command=self._cancel, width=8)
1958
+ b.pack(side='right', padx=5)
1959
+ b = Button(buttons, text='Ok', command=self._ok,
1960
+ width=8, default='active')
1961
+ b.pack(side='left', padx=5)
1962
+ b = Button(buttons, text='Apply', command=self._apply, width=8)
1963
+ b.pack(side='left')
1964
+
1965
+ self._top.bind('<Return>', self._ok)
1966
+ self._top.bind('<Control-q>', self._cancel)
1967
+ self._top.bind('<Escape>', self._cancel)
1968
+
1969
+ self._entry.focus()
1970
+
1971
+ def _reset(self, *e):
1972
+ self._entry.delete(0,'end')
1973
+ self._entry.insert(0, self._original_text)
1974
+ if self._set_callback:
1975
+ self._set_callback(self._original_text)
1976
+
1977
+ def _cancel(self, *e):
1978
+ try: self._reset()
1979
+ except: pass
1980
+ self._destroy()
1981
+
1982
+ def _ok(self, *e):
1983
+ self._apply()
1984
+ self._destroy()
1985
+
1986
+ def _apply(self, *e):
1987
+ if self._set_callback:
1988
+ self._set_callback(self._entry.get())
1989
+
1990
+ def _destroy(self, *e):
1991
+ if self._top is None: return
1992
+ self._top.destroy()
1993
+ self._top = None
1994
+
1995
+ ##//////////////////////////////////////////////////////
1996
+ ## Colorized List
1997
+ ##//////////////////////////////////////////////////////
1998
+
1999
+ class ColorizedList(object):
2000
+ """
2001
+ An abstract base class for displaying a colorized list of items.
2002
+ Subclasses should define:
2003
+ - ``_init_colortags``, which sets up Text color tags that
2004
+ will be used by the list.
2005
+ - ``_item_repr``, which returns a list of (text,colortag)
2006
+ tuples that make up the colorized representation of the
2007
+ item.
2008
+ :note: Typically, you will want to register a callback for
2009
+ ``'select'`` that calls ``mark`` on the given item.
2010
+ """
2011
+ def __init__(self, parent, items=[], **options):
2012
+ """
2013
+ Construct a new list.
2014
+
2015
+ :param parent: The Tk widget that contains the colorized list
2016
+ :param items: The initial contents of the colorized list.
2017
+ :param options:
2018
+ """
2019
+ self._parent = parent
2020
+ self._callbacks = {}
2021
+
2022
+ # Which items are marked?
2023
+ self._marks = {}
2024
+
2025
+ # Initialize the Tkinter frames.
2026
+ self._init_itemframe(options.copy())
2027
+
2028
+ # Set up key & mouse bindings.
2029
+ self._textwidget.bind('<KeyPress>', self._keypress)
2030
+ self._textwidget.bind('<ButtonPress>', self._buttonpress)
2031
+
2032
+ # Fill in the given CFG's items.
2033
+ self._items = None
2034
+ self.set(items)
2035
+
2036
+ #////////////////////////////////////////////////////////////
2037
+ # Abstract methods
2038
+ #////////////////////////////////////////////////////////////
2039
+ @abstractmethod
2040
+ def _init_colortags(self, textwidget, options):
2041
+ """
2042
+ Set up any colortags that will be used by this colorized list.
2043
+ E.g.:
2044
+ >>> textwidget.tag_config('terminal', foreground='black')
2045
+ """
2046
+
2047
+ @abstractmethod
2048
+ def _item_repr(self, item):
2049
+ """
2050
+ Return a list of (text, colortag) tuples that make up the
2051
+ colorized representation of the item. Colorized
2052
+ representations may not span multiple lines. I.e., the text
2053
+ strings returned may not contain newline characters.
2054
+ """
2055
+
2056
+ #////////////////////////////////////////////////////////////
2057
+ # Item Access
2058
+ #////////////////////////////////////////////////////////////
2059
+
2060
+ def get(self, index=None):
2061
+ """
2062
+ :return: A list of the items contained by this list.
2063
+ """
2064
+ if index is None:
2065
+ return self._items[:]
2066
+ else:
2067
+ return self._items[index]
2068
+
2069
+ def set(self, items):
2070
+ """
2071
+ Modify the list of items contained by this list.
2072
+ """
2073
+ items = list(items)
2074
+ if self._items == items: return
2075
+ self._items = list(items)
2076
+
2077
+ self._textwidget['state'] = 'normal'
2078
+ self._textwidget.delete('1.0', 'end')
2079
+ for item in items:
2080
+ for (text, colortag) in self._item_repr(item):
2081
+ assert '\n' not in text, 'item repr may not contain newline'
2082
+ self._textwidget.insert('end', text, colortag)
2083
+ self._textwidget.insert('end', '\n')
2084
+ # Remove the final newline
2085
+ self._textwidget.delete('end-1char', 'end')
2086
+ self._textwidget.mark_set('insert', '1.0')
2087
+ self._textwidget['state'] = 'disabled'
2088
+ # Clear all marks
2089
+ self._marks.clear()
2090
+
2091
+ def unmark(self, item=None):
2092
+ """
2093
+ Remove highlighting from the given item; or from every item,
2094
+ if no item is given.
2095
+ :raise ValueError: If ``item`` is not contained in the list.
2096
+ :raise KeyError: If ``item`` is not marked.
2097
+ """
2098
+ if item is None:
2099
+ self._marks.clear()
2100
+ self._textwidget.tag_remove('highlight', '1.0', 'end+1char')
2101
+ else:
2102
+ index = self._items.index(item)
2103
+ del self._marks[item]
2104
+ (start, end) = ('%d.0' % (index+1), '%d.0' % (index+2))
2105
+ self._textwidget.tag_remove('highlight', start, end)
2106
+
2107
+ def mark(self, item):
2108
+ """
2109
+ Highlight the given item.
2110
+ :raise ValueError: If ``item`` is not contained in the list.
2111
+ """
2112
+ self._marks[item] = 1
2113
+ index = self._items.index(item)
2114
+ (start, end) = ('%d.0' % (index+1), '%d.0' % (index+2))
2115
+ self._textwidget.tag_add('highlight', start, end)
2116
+
2117
+ def markonly(self, item):
2118
+ """
2119
+ Remove any current highlighting, and mark the given item.
2120
+ :raise ValueError: If ``item`` is not contained in the list.
2121
+ """
2122
+ self.unmark()
2123
+ self.mark(item)
2124
+
2125
+ def view(self, item):
2126
+ """
2127
+ Adjust the view such that the given item is visible. If
2128
+ the item is already visible, then do nothing.
2129
+ """
2130
+ index = self._items.index(item)
2131
+ self._textwidget.see('%d.0' % (index+1))
2132
+
2133
+ #////////////////////////////////////////////////////////////
2134
+ # Callbacks
2135
+ #////////////////////////////////////////////////////////////
2136
+
2137
+ def add_callback(self, event, func):
2138
+ """
2139
+ Register a callback function with the list. This function
2140
+ will be called whenever the given event occurs.
2141
+
2142
+ :param event: The event that will trigger the callback
2143
+ function. Valid events are: click1, click2, click3,
2144
+ space, return, select, up, down, next, prior, move
2145
+ :param func: The function that should be called when
2146
+ the event occurs. ``func`` will be called with a
2147
+ single item as its argument. (The item selected
2148
+ or the item moved to).
2149
+ """
2150
+ if event == 'select': events = ['click1', 'space', 'return']
2151
+ elif event == 'move': events = ['up', 'down', 'next', 'prior']
2152
+ else: events = [event]
2153
+
2154
+ for e in events:
2155
+ self._callbacks.setdefault(e,{})[func] = 1
2156
+
2157
+ def remove_callback(self, event, func=None):
2158
+ """
2159
+ Deregister a callback function. If ``func`` is none, then
2160
+ all callbacks are removed for the given event.
2161
+ """
2162
+ if event is None: events = list(self._callbacks.keys())
2163
+ elif event == 'select': events = ['click1', 'space', 'return']
2164
+ elif event == 'move': events = ['up', 'down', 'next', 'prior']
2165
+ else: events = [event]
2166
+
2167
+ for e in events:
2168
+ if func is None: del self._callbacks[e]
2169
+ else:
2170
+ try: del self._callbacks[e][func]
2171
+ except: pass
2172
+
2173
+ #////////////////////////////////////////////////////////////
2174
+ # Tkinter Methods
2175
+ #////////////////////////////////////////////////////////////
2176
+
2177
+ def pack(self, cnf={}, **kw):
2178
+ # "@include: Tkinter.Pack.pack"
2179
+ self._itemframe.pack(cnf, **kw)
2180
+
2181
+ def grid(self, cnf={}, **kw):
2182
+ # "@include: Tkinter.Grid.grid"
2183
+ self._itemframe.grid(cnf, *kw)
2184
+
2185
+ def focus(self):
2186
+ # "@include: Tkinter.Widget.focus"
2187
+ self._textwidget.focus()
2188
+
2189
+ #////////////////////////////////////////////////////////////
2190
+ # Internal Methods
2191
+ #////////////////////////////////////////////////////////////
2192
+
2193
+ def _init_itemframe(self, options):
2194
+ self._itemframe = Frame(self._parent)
2195
+
2196
+ # Create the basic Text widget & scrollbar.
2197
+ options.setdefault('background', '#e0e0e0')
2198
+ self._textwidget = Text(self._itemframe, **options)
2199
+ self._textscroll = Scrollbar(self._itemframe, takefocus=0,
2200
+ orient='vertical')
2201
+ self._textwidget.config(yscrollcommand = self._textscroll.set)
2202
+ self._textscroll.config(command=self._textwidget.yview)
2203
+ self._textscroll.pack(side='right', fill='y')
2204
+ self._textwidget.pack(expand=1, fill='both', side='left')
2205
+
2206
+ # Initialize the colorization tags
2207
+ self._textwidget.tag_config('highlight', background='#e0ffff',
2208
+ border='1', relief='raised')
2209
+ self._init_colortags(self._textwidget, options)
2210
+
2211
+ # How do I want to mark keyboard selection?
2212
+ self._textwidget.tag_config('sel', foreground='')
2213
+ self._textwidget.tag_config('sel', foreground='', background='',
2214
+ border='', underline=1)
2215
+ self._textwidget.tag_lower('highlight', 'sel')
2216
+
2217
+ def _fire_callback(self, event, itemnum):
2218
+ if event not in self._callbacks: return
2219
+ if 0 <= itemnum < len(self._items):
2220
+ item = self._items[itemnum]
2221
+ else:
2222
+ item = None
2223
+ for cb_func in list(self._callbacks[event].keys()):
2224
+ cb_func(item)
2225
+
2226
+ def _buttonpress(self, event):
2227
+ clickloc = '@%d,%d' % (event.x,event.y)
2228
+ insert_point = self._textwidget.index(clickloc)
2229
+ itemnum = int(insert_point.split('.')[0])-1
2230
+ self._fire_callback('click%d' % event.num, itemnum)
2231
+
2232
+ def _keypress(self, event):
2233
+ if event.keysym == 'Return' or event.keysym == 'space':
2234
+ insert_point = self._textwidget.index('insert')
2235
+ itemnum = int(insert_point.split('.')[0])-1
2236
+ self._fire_callback(event.keysym.lower(), itemnum)
2237
+ return
2238
+ elif event.keysym == 'Down': delta='+1line'
2239
+ elif event.keysym == 'Up': delta='-1line'
2240
+ elif event.keysym == 'Next': delta='+10lines'
2241
+ elif event.keysym == 'Prior': delta='-10lines'
2242
+ else: return 'continue'
2243
+
2244
+ self._textwidget.mark_set('insert', 'insert'+delta)
2245
+ self._textwidget.see('insert')
2246
+ self._textwidget.tag_remove('sel', '1.0', 'end+1char')
2247
+ self._textwidget.tag_add('sel', 'insert linestart', 'insert lineend')
2248
+
2249
+ insert_point = self._textwidget.index('insert')
2250
+ itemnum = int(insert_point.split('.')[0])-1
2251
+ self._fire_callback(event.keysym.lower(), itemnum)
2252
+
2253
+ return 'break'
2254
+
2255
+ ##//////////////////////////////////////////////////////
2256
+ ## Improved OptionMenu
2257
+ ##//////////////////////////////////////////////////////
2258
+
2259
+ class MutableOptionMenu(Menubutton):
2260
+ def __init__(self, master, values, **options):
2261
+ self._callback = options.get('command')
2262
+ if 'command' in options: del options['command']
2263
+
2264
+ # Create a variable
2265
+ self._variable = variable = StringVar()
2266
+ if len(values) > 0:
2267
+ variable.set(values[0])
2268
+
2269
+ kw = {"borderwidth": 2, "textvariable": variable,
2270
+ "indicatoron": 1, "relief": RAISED, "anchor": "c",
2271
+ "highlightthickness": 2}
2272
+ kw.update(options)
2273
+ Widget.__init__(self, master, "menubutton", kw)
2274
+ self.widgetName = 'tk_optionMenu'
2275
+ self._menu = Menu(self, name="menu", tearoff=0,)
2276
+ self.menuname = self._menu._w
2277
+
2278
+ self._values = []
2279
+ for value in values: self.add(value)
2280
+
2281
+ self["menu"] = self._menu
2282
+
2283
+ def add(self, value):
2284
+ if value in self._values: return
2285
+ def set(value=value): self.set(value)
2286
+ self._menu.add_command(label=value, command=set)
2287
+ self._values.append(value)
2288
+
2289
+ def set(self, value):
2290
+ self._variable.set(value)
2291
+ if self._callback:
2292
+ self._callback(value)
2293
+
2294
+ def remove(self, value):
2295
+ # Might raise indexerror: pass to parent.
2296
+ i = self._values.index(value)
2297
+ del self._values[i]
2298
+ self._menu.delete(i, i)
2299
+
2300
+ def __getitem__(self, name):
2301
+ if name == 'menu':
2302
+ return self.__menu
2303
+ return Widget.__getitem__(self, name)
2304
+
2305
+ def destroy(self):
2306
+ """Destroy this widget and the associated menu."""
2307
+ Menubutton.destroy(self)
2308
+ self._menu = None
2309
+
2310
+ ##//////////////////////////////////////////////////////
2311
+ ## Test code.
2312
+ ##//////////////////////////////////////////////////////
2313
+
2314
+ def demo():
2315
+ """
2316
+ A simple demonstration showing how to use canvas widgets.
2317
+ """
2318
+ def fill(cw):
2319
+ from random import randint
2320
+ cw['fill'] = '#00%04d' % randint(0,9999)
2321
+ def color(cw):
2322
+ from random import randint
2323
+ cw['color'] = '#ff%04d' % randint(0,9999)
2324
+
2325
+ cf = CanvasFrame(closeenough=10, width=300, height=300)
2326
+ c = cf.canvas()
2327
+ ct3 = TextWidget(c, 'hiya there', draggable=1)
2328
+ ct2 = TextWidget(c, 'o o\n||\n___\n U', draggable=1, justify='center')
2329
+ co = OvalWidget(c, ct2, outline='red')
2330
+ ct = TextWidget(c, 'o o\n||\n\\___/', draggable=1, justify='center')
2331
+ cp = ParenWidget(c, ct, color='red')
2332
+ cb = BoxWidget(c, cp, fill='cyan', draggable=1, width=3, margin=10)
2333
+ equation = SequenceWidget(c,
2334
+ SymbolWidget(c, 'forall'), TextWidget(c, 'x'),
2335
+ SymbolWidget(c, 'exists'), TextWidget(c, 'y: '),
2336
+ TextWidget(c, 'x'), SymbolWidget(c, 'notequal'),
2337
+ TextWidget(c, 'y'))
2338
+ space = SpaceWidget(c, 0, 30)
2339
+ cstack = StackWidget(c, cb, ct3, space, co, equation, align='center')
2340
+ foo = TextWidget(c, 'try clicking\nand dragging',
2341
+ draggable=1, justify='center')
2342
+ cs = SequenceWidget(c, cstack, foo)
2343
+ zz = BracketWidget(c, cs, color='green4', width=3)
2344
+ cf.add_widget(zz, 60, 30)
2345
+
2346
+ cb.bind_click(fill)
2347
+ ct.bind_click(color)
2348
+ co.bind_click(fill)
2349
+ ct2.bind_click(color)
2350
+ ct3.bind_click(color)
2351
+
2352
+ cf.mainloop()
2353
+ #ShowText(None, 'title', ((('this is text'*150)+'\n')*5))
2354
+
2355
+ if __name__ == '__main__':
2356
+ demo()