修訂 | f082099bdfa750688805093c93370cf2d4510254 (tree) |
---|---|
時間 | 2018-03-15 13:16:06 |
作者 | Dreas Nielsen <dreas.nielsen@gmai...> |
Commiter | Dreas Nielsen |
Added initialization of datadict for custom panes with default values. Added init parameters 'required' and 'blank_is_valid' to the TextPane class. Added function 'run_validity_callbacks()'.
@@ -4,7 +4,7 @@ | ||
4 | 4 | ========== ======= ================================================================================= |
5 | 5 | Date Version Revision |
6 | 6 | ========== ======= ================================================================================= |
7 | -2018-03-14 0.29.0 Modified EntryPane; added parameters 'required' and 'blank_is_valid'. | |
7 | +2018-03-14 0.30.0 Modified EntryPane; added parameters 'required' and 'blank_is_valid'. Modified RadiobuttonPane to allow either orientation. Initialized datadict in panes with default values. Added function 'run_validity_callbacks()'. | |
8 | 8 | 2018-03-12 0.27.0 Modified title of UserPane in lib.py. |
9 | 9 | 2018-03-10 0.26.0 Added 'set_entry_validator()' method to EntryPane in lib.py. Changed invalid colors to be configurable at module level. Changed use of ttk or tk widgets to be configurable at the module level. |
10 | 10 | 2018-03-01 0.22.0 Added 'requires_datavalue() and 'clear_on_disable()' methods. Improved support for 'on_save_change' callback list. Added 'set_filename_validator()' method to InputFilePane. |
@@ -55,9 +55,9 @@ | ||
55 | 55 | # built documents. |
56 | 56 | # |
57 | 57 | # The short X.Y version. |
58 | -version = u'0.29.0' | |
58 | +version = u'0.30.0' | |
59 | 59 | # The full version, including alpha/beta/rc tags. |
60 | -release = u'0.29.0' | |
60 | +release = u'0.30.0' | |
61 | 61 | |
62 | 62 | # The language for content autogenerated by Sphinx. Refer to documentation |
63 | 63 | # for a list of supported languages. |
@@ -714,6 +714,8 @@ | ||
714 | 714 | Several functions in the ``tkpane`` package simplify the initialization |
715 | 715 | of a UI or the integration of the ``tkpane`` and ``tklayout`` libraries. |
716 | 716 | |
717 | +.. autofunction:: run_validity_callbacks | |
718 | + | |
717 | 719 | .. autofunction:: enable_or_disable_all |
718 | 720 | |
719 | 721 | .. autofunction:: layout_panes |
@@ -918,10 +920,7 @@ | ||
918 | 920 | """Update the pane's data dictionary with data from the Entry widget.""" |
919 | 921 | text = self.entry_var.get() |
920 | 922 | if is_valid: |
921 | - if text == "": | |
922 | - self.clear_own() | |
923 | - else: | |
924 | - self.datadict[self.datakeyname] = text | |
923 | + self.datadict[self.datakeyname] = text | |
925 | 924 | else: |
926 | 925 | self.clear_own() |
927 | 926 |
@@ -1231,7 +1230,7 @@ | ||
1231 | 1230 | .. note:: |
1232 | 1231 | |
1233 | 1232 | The 'invalid' background color for themed (ttk) widgets is not |
1234 | - getting set to light red on Windows when native widgets are being | |
1233 | + set to light red on Windows when native widgets are being | |
1235 | 1234 | used (as in the default theme). |
1236 | 1235 | |
1237 | 1236 | 2. Every pane that manages data must have a dictionary key (or keys) |
@@ -1317,7 +1316,7 @@ | ||
1317 | 1316 | ================================================================ |
1318 | 1317 | |
1319 | 1318 | Elizabeth Shea |
1320 | - Conceptualization of pane interactions, radio button panes, original | |
1319 | + Conceptualization of pane interactions, radio button pane, original | |
1321 | 1320 | versions of ``UserPane`` and ``OutputDirPane``, and rigorous |
1322 | 1321 | testing. |
1323 | 1322 |
@@ -2,7 +2,7 @@ | ||
2 | 2 | |
3 | 3 | setup(name='tkpane', |
4 | 4 | packages=['tkpane'], |
5 | - version='0.29.0', | |
5 | + version='0.30.0', | |
6 | 6 | description="Encapsulates Tkinter UI elements in 'panes' that can be combined into an overall UI, integrating them by specifying callback functions and data keys.", |
7 | 7 | author='Dreas Nielsen', |
8 | 8 | author_email='dreas.nielsen@gmail.com', |
@@ -87,8 +87,11 @@ | ||
87 | 87 | button1.can_use(entry2) |
88 | 88 | |
89 | 89 | |
90 | +# Propagate initial values to dependent panes. | |
91 | +tkpane.run_validity_callbacks([entry1, cb1, cb2, cb3, entry2]) | |
92 | + | |
90 | 93 | # Ensure panes are initially enabled or disabled as appropriate. |
91 | -tkpane.en_or_dis_able_all([cb1, cb2, cb3, entry2, button1]) | |
94 | +tkpane.enable_or_disable_all([cb1, cb2, cb3, entry2, button1]) | |
92 | 95 | |
93 | 96 | # Make the checkboxes report their status changes to the text pane. |
94 | 97 | cb1.status_reporter = text_pane |
@@ -112,5 +115,7 @@ | ||
112 | 115 | |
113 | 116 | |
114 | 117 | |
118 | + | |
119 | + | |
115 | 120 | # Run the application |
116 | 121 | root.mainloop() |
@@ -1,113 +1,116 @@ | ||
1 | -#!/usr/bin/python | |
2 | -# test_radiobutton.py | |
3 | -# | |
4 | -# PURPOSE | |
5 | -# Test the RadiobuttonPane. | |
6 | -# | |
7 | -# NOTES | |
8 | -# 1. | |
9 | -# | |
10 | -# AUTHOR | |
11 | -# Elizabeth Shea | |
12 | -# | |
13 | -# HISTORY | |
14 | -# Date Remarks | |
15 | -# ---------- ----------------------------------------------------------- | |
16 | -# 2018-03-13 Created. ES. | |
17 | -# ========================================================================= | |
18 | - | |
19 | -try: | |
20 | - import Tkinter as tk | |
21 | -except: | |
22 | - import tkinter as tk | |
23 | - | |
24 | -import tkpane | |
25 | -import tkpane.lib | |
26 | -import tklayout | |
27 | - | |
28 | -# Add a method to the AppLayout class to get a pane: the first child of | |
29 | -# a frame's widgets. | |
30 | -def layout_pane(self, pane_name): | |
31 | - return self.frame_widgets(pane_name)[0] | |
32 | -tklayout.AppLayout.pane = layout_pane | |
33 | - | |
34 | - | |
35 | -lo = tklayout.AppLayout() | |
36 | -# Use an empty pane to push buttons right. | |
37 | -buttons = lo.row_elements(["empty_pane", "button1", "button2"], column_weights=[1,0,0]) | |
38 | -app = lo.column_elements(["entry1", "entry2", "rb1", "text_pane", buttons], row_weights=[0,0,1,0]) | |
39 | - | |
40 | -root = tk.Tk() | |
41 | -root.title("Testing of Radiobutton") | |
42 | - | |
43 | -appframe = tk.Frame(root, padx=11, pady=11) | |
44 | -appframe.pack(expand=True, fill=tk.BOTH) | |
45 | - | |
46 | -lo.create_layout(appframe, app) | |
47 | - | |
48 | -# Use the pane class constructors to populate each UI element in the layout. | |
49 | -# Panes from the library are used; no custom panes are created for this demo. | |
50 | -lo.build_elements({ | |
51 | - "entry1": lambda p: tkpane.lib.EntryPane(p, "entry1", "Entry 1:", "entry1"), | |
52 | - "entry2": lambda p: tkpane.lib.EntryPane(p, "entry2", "Details:", "entry2"), | |
53 | - "text_pane": tkpane.lib.TextPane | |
54 | - }) | |
55 | -tkpane.lib.current_panestyle = "closex" | |
56 | -lo.build_elements({ | |
57 | - "button1": lambda p: tkpane.lib.ButtonPane(p, "Show data"), | |
58 | - "button2": lambda p: tkpane.lib.ButtonPane(p, "Clear") | |
59 | - }) | |
60 | -tkpane.lib.current_panestyle = "closey" | |
61 | -lo.build_elements({ | |
62 | - "rb1": lambda p: tkpane.lib.RadiobuttonPane(p, "rb1", "Radiobutton1", [("Option1",1), ("Option2",2), ("Option3", 3)], "Option1") | |
63 | - }) | |
64 | - | |
65 | - | |
66 | - | |
67 | -# Get references to the actual pane objects so that they can be customized. | |
68 | -entry1 = lo.pane("entry1") | |
69 | -entry2 = lo.pane("entry2") | |
70 | -text_pane = lo.pane("text_pane") | |
71 | -rb1 = lo.pane("rb1") | |
72 | -#rb1.required = True | |
73 | -button1 = lo.pane("button1") | |
74 | -button2 = lo.pane("button2") | |
75 | - | |
76 | -# Create dependencies. | |
77 | -rb1.requires(entry1, clear_on_disable=True) | |
78 | - | |
79 | - | |
80 | -button1.can_use(rb1) | |
81 | -button1.can_use(entry2) | |
82 | - | |
83 | - | |
84 | -# Ensure panes are initially enabled or disabled as appropriate. | |
85 | -tkpane.en_or_dis_able_all([rb1, button1]) | |
86 | - | |
87 | -# Make the radiobutton report status changes to the text pane. | |
88 | -rb1.status_reporter = text_pane | |
89 | - | |
90 | -entry2.status_reporter=text_pane | |
91 | - | |
92 | -# Disable user entry into the text pane; it is used only as a status log. | |
93 | -text_pane.disable() | |
94 | - | |
95 | -# Make button1 report its data to the text pane. | |
96 | -def b1_click(*args): | |
97 | - # This does not work | |
98 | - text_pane.set_status("\nButton's data:\n%s\n" % str(button1.all_data())) | |
99 | - | |
100 | - # This works | |
101 | - #text_pane.set_status("\nButton's data:\n%s\n" % str(rb1.all_data())) | |
102 | - | |
103 | -button1.set_button_action(b1_click) | |
104 | - | |
105 | -# Button2 clears the entry. | |
106 | -def b2_click(*args): | |
107 | - entry1.clear() | |
108 | -button2.set_button_action(b2_click) | |
109 | - | |
110 | - | |
111 | - | |
112 | -# Run the application | |
113 | -root.mainloop() | |
1 | +#!/usr/bin/python | |
2 | +# test_radiobutton.py | |
3 | +# | |
4 | +# PURPOSE | |
5 | +# Test the RadiobuttonPane. | |
6 | +# | |
7 | +# NOTES | |
8 | +# 1. | |
9 | +# | |
10 | +# AUTHOR | |
11 | +# Elizabeth Shea | |
12 | +# | |
13 | +# HISTORY | |
14 | +# Date Remarks | |
15 | +# ---------- ----------------------------------------------------------- | |
16 | +# 2018-03-13 Created. ES. | |
17 | +# ========================================================================= | |
18 | + | |
19 | +try: | |
20 | + import Tkinter as tk | |
21 | +except: | |
22 | + import tkinter as tk | |
23 | + | |
24 | +import tkpane | |
25 | +import tkpane.lib | |
26 | +import tklayout | |
27 | + | |
28 | +# Add a method to the AppLayout class to get a pane: the first child of | |
29 | +# a frame's widgets. | |
30 | +def layout_pane(self, pane_name): | |
31 | + return self.frame_widgets(pane_name)[0] | |
32 | +tklayout.AppLayout.pane = layout_pane | |
33 | + | |
34 | + | |
35 | +lo = tklayout.AppLayout() | |
36 | +# Use an empty pane to push buttons right. | |
37 | +buttons = lo.row_elements(["empty_pane", "button1", "button2"], column_weights=[1,0,0]) | |
38 | +app = lo.column_elements(["entry1", "entry2", "rb1", "text_pane", buttons], row_weights=[0,0,1,0]) | |
39 | + | |
40 | +root = tk.Tk() | |
41 | +root.title("Testing of Radiobutton") | |
42 | + | |
43 | +appframe = tk.Frame(root, padx=11, pady=11) | |
44 | +appframe.pack(expand=True, fill=tk.BOTH) | |
45 | + | |
46 | +lo.create_layout(appframe, app) | |
47 | + | |
48 | +# Use the pane class constructors to populate each UI element in the layout. | |
49 | +# Panes from the library are used; no custom panes are created for this demo. | |
50 | +lo.build_elements({ | |
51 | + "entry1": lambda p: tkpane.lib.EntryPane(p, "entry1", "Entry 1:", "entry1"), | |
52 | + "entry2": lambda p: tkpane.lib.EntryPane(p, "entry2", "Details:", "entry2"), | |
53 | + "text_pane": tkpane.lib.TextPane | |
54 | + }) | |
55 | +tkpane.lib.current_panestyle = "closex" | |
56 | +lo.build_elements({ | |
57 | + "button1": lambda p: tkpane.lib.ButtonPane(p, "Show data"), | |
58 | + "button2": lambda p: tkpane.lib.ButtonPane(p, "Clear") | |
59 | + }) | |
60 | +tkpane.lib.current_panestyle = "closey" | |
61 | +lo.build_elements({ | |
62 | + "rb1": lambda p: tkpane.lib.RadiobuttonPane(p, "rb1", "Radiobutton1", [("Option1",1), ("Option2",2), ("Option3", 3)], "Option1") | |
63 | + }) | |
64 | + | |
65 | + | |
66 | + | |
67 | +# Get references to the actual pane objects so that they can be customized. | |
68 | +entry1 = lo.pane("entry1") | |
69 | +entry2 = lo.pane("entry2") | |
70 | +text_pane = lo.pane("text_pane") | |
71 | +rb1 = lo.pane("rb1") | |
72 | +#rb1.required = True | |
73 | +button1 = lo.pane("button1") | |
74 | +button2 = lo.pane("button2") | |
75 | + | |
76 | +# Create dependencies. | |
77 | +rb1.requires(entry1, clear_on_disable=True) | |
78 | + | |
79 | + | |
80 | +button1.can_use(rb1) | |
81 | +button1.can_use(entry2) | |
82 | + | |
83 | + | |
84 | +# Propagate initial values to dependent panes. | |
85 | +tkpane.run_validity_callbacks([entry1, rb1, entry2]) | |
86 | + | |
87 | +# Ensure panes are initially enabled or disabled as appropriate. | |
88 | +tkpane.en_or_dis_able_all([rb1, button1]) | |
89 | + | |
90 | +# Make the radiobutton report status changes to the text pane. | |
91 | +rb1.status_reporter = text_pane | |
92 | + | |
93 | +entry2.status_reporter=text_pane | |
94 | + | |
95 | +# Disable user entry into the text pane; it is used only as a status log. | |
96 | +text_pane.disable() | |
97 | + | |
98 | +# Make button1 report its data to the text pane. | |
99 | +def b1_click(*args): | |
100 | + # This does not work | |
101 | + text_pane.set_status("\nButton's data:\n%s\n" % str(button1.all_data())) | |
102 | + | |
103 | + # This works | |
104 | + #text_pane.set_status("\nButton's data:\n%s\n" % str(rb1.all_data())) | |
105 | + | |
106 | +button1.set_button_action(b1_click) | |
107 | + | |
108 | +# Button2 clears the entry. | |
109 | +def b2_click(*args): | |
110 | + entry1.clear() | |
111 | +button2.set_button_action(b2_click) | |
112 | + | |
113 | + | |
114 | + | |
115 | +# Run the application | |
116 | +root.mainloop() |
@@ -24,7 +24,7 @@ | ||
24 | 24 | for creation of other custom pane classes. |
25 | 25 | """ |
26 | 26 | |
27 | -__version__ = "0.15.0" | |
27 | +__version__ = "0.16.0" | |
28 | 28 | |
29 | 29 | |
30 | 30 | try: |
@@ -390,7 +390,7 @@ | ||
390 | 390 | def entry_widgets(self): |
391 | 391 | return [self.checkbox] |
392 | 392 | |
393 | - def valid_data(self, widget): | |
393 | + def valid_data(self, widget=None): | |
394 | 394 | """Returns an indication of whether the checkbox is in a valid state.""" |
395 | 395 | if self.valid_state is None: |
396 | 396 | return True |
@@ -1128,7 +1128,7 @@ | ||
1128 | 1128 | def entry_widgets(self): |
1129 | 1129 | return [self.listbox] |
1130 | 1130 | |
1131 | - def valid_data(self, widget): | |
1131 | + def valid_data(self, widget=None): | |
1132 | 1132 | """Returns an indication of whether the listbox is both required and has at least one selection.""" |
1133 | 1133 | if self.required: |
1134 | 1134 | selected = self.listbox.curselection() |
@@ -1682,15 +1682,13 @@ | ||
1682 | 1682 | :param pane_name: The name to be used to identify this pane in status messages. |
1683 | 1683 | :param prompt: The text associated with the set of radiobuttons. |
1684 | 1684 | :param option_list: List of radiobutton options, consisting of tuples in the format: (label, value). |
1685 | - :param default_option: The label of the default option, e.g., "radioopt1" for ("radioopt", 1). If the default | |
1686 | - option is not actually present on the option list (or if no default option is specified), the default value is | |
1687 | - set to the empty string "". | |
1688 | - :param orient_vertical: Whether the radio button group should be oriented horizontally or vertically. Default is True (vertical) | |
1685 | + :param default_option: The label of the default option, e.g., "radioopt1" for ("radioopt", 1). If the default option is not actually present on the option list (or if no default option is specified), the default value is set to the empty string "". | |
1686 | + :param orient_vertical: Whether the radio button group should be oriented horizontally or vertically. Default is True (vertical) | |
1689 | 1687 | :param button_action: A callback to perform an action when a value is selected. |
1690 | 1688 | :param key_name: The name to be used with the internal data dictionary to identify the entry data; use to avoid name conflicts with other RadiobuttonPane panes on the same UI (optional). |
1691 | 1689 | :param config_opts: A dictionary of configuration options for the Radiobutton widget |
1692 | 1690 | |
1693 | - Data keys managed by this pane "radio" or the key name specified during initialization. | |
1691 | + Data keys managed by this pane: "radio" or the key name specified during initialization. | |
1694 | 1692 | |
1695 | 1693 | Name used by this pane: user-defined on initialization. |
1696 | 1694 |
@@ -1709,8 +1707,6 @@ | ||
1709 | 1707 | * set_key |
1710 | 1708 | * set_button_action |
1711 | 1709 | * do_button_action |
1712 | - | |
1713 | - | |
1714 | 1710 | """ |
1715 | 1711 | |
1716 | 1712 | def __init__(self, parent, pane_name, prompt, option_list, default_option=None, orient_vertical=True, button_action=None, key_name=None, config_opts=None): |
@@ -1744,7 +1740,7 @@ | ||
1744 | 1740 | if config_opts is not None: |
1745 | 1741 | self.radio_btn.configure(**config_opts) |
1746 | 1742 | if orient_vertical: |
1747 | - radio_btn.grid(row=i, column=1, padx=3, pady=3, sticky=tk.EW) | |
1743 | + radio_btn.grid(row=i, column=1, padx=3, pady=1, sticky=tk.EW) | |
1748 | 1744 | self.rowconfigure(i, weight=1) |
1749 | 1745 | else: |
1750 | 1746 | radio_btn.grid(row=0, column=i+1, padx=3, pady=3, sticky=tk.EW) |
@@ -1767,14 +1763,13 @@ | ||
1767 | 1763 | return radio != "" |
1768 | 1764 | return True |
1769 | 1765 | |
1770 | - | |
1771 | 1766 | def save_data(self, is_valid, entry_widget=None): |
1772 | 1767 | """Update the pane's data dictionary with data from the Radiobutton widget.""" |
1773 | 1768 | state = self.radio_var.get() |
1774 | 1769 | self.datadict[self.datakeyname] = state |
1775 | 1770 | |
1776 | 1771 | def clear_pane(self): |
1777 | - self.radio_var.set(self.default_item if self.default_item else u"") | |
1772 | + self.radio_var.set(self.default_item if self.default_item else u"") | |
1778 | 1773 | |
1779 | 1774 | def enable_pane(self): |
1780 | 1775 | self._enablewidgets(self.winfo_children()) |
@@ -1865,6 +1860,7 @@ | ||
1865 | 1860 | self.datakeylist = [self.datakeyname] |
1866 | 1861 | self.entry_var = tk.DoubleVar() |
1867 | 1862 | self.entry_var.set(init_value) |
1863 | + self.datadict[self.datakeyname] = self.entry_var.get() | |
1868 | 1864 | if tkpane.use_ttk: |
1869 | 1865 | self.entrywidget = ttk.Scale(self, variable=self.entry_var, length=length, orient=orientation, from_=min_value, to=max_value, value=init_value) |
1870 | 1866 | self.widget_type = "ttk" |
@@ -1967,6 +1963,7 @@ | ||
1967 | 1963 | self.datakeylist = [self.datakeyname] |
1968 | 1964 | self.entry_var = tk.DoubleVar() |
1969 | 1965 | self.entry_var.set(init_value) |
1966 | + self.datadict[self.datakeyname] = self.entry_var.get() | |
1970 | 1967 | if tkpane.use_ttk: |
1971 | 1968 | self.prompt = ttk.Label(self, text=prompt, width=max(12, len(prompt)), anchor=tk.E) |
1972 | 1969 | self.entrywidget = ttk.Scale(self, variable=self.entry_var, orient=tk.HORIZONTAL, from_=min_value, to=max_value, value=init_value) |
@@ -2091,6 +2088,8 @@ | ||
2091 | 2088 | self.datakeyname = "spinbox" if key_name is None else key_name |
2092 | 2089 | self.datakeylist = [self.datakeyname] |
2093 | 2090 | self.entry_var = tk.StringVar() |
2091 | + self.entry_var.set(min_value) | |
2092 | + self.datadict[self.datakeyname] = self.entry_var.get() | |
2094 | 2093 | if tkpane.use_ttk: |
2095 | 2094 | self.prompt = ttk.Label(self, text=prompt, width=max(12, len(prompt)), anchor=tk.E) |
2096 | 2095 | try: |
@@ -2625,13 +2624,15 @@ | ||
2625 | 2624 | * set_status |
2626 | 2625 | * clear_status |
2627 | 2626 | * set_key |
2627 | + * set_entry_validator | |
2628 | 2628 | """ |
2629 | 2629 | |
2630 | - def __init__(self, parent, key_name=None, optiondict=None, initial_text=None): | |
2630 | + def __init__(self, parent, key_name=None, optiondict=None, initial_text=None, required=False, blank_is_valid=False): | |
2631 | 2631 | tkpane.TkPane.__init__(self, parent, "Text", frame_config_opts(), frame_grid_opts()) |
2632 | 2632 | opts = {} if optiondict is None else optiondict |
2633 | 2633 | self.datakeyname = "text" if key_name is None else key_name |
2634 | 2634 | self.datakeylist = [self.datakeyname] |
2635 | + self.entry_validator = None | |
2635 | 2636 | self.widget_type = "tk" |
2636 | 2637 | self.textwidget = tk.Text(self, exportselection=False, **opts) |
2637 | 2638 | self.textwidget.bind("<Key>", self.check_entrychange) |
@@ -2644,6 +2645,7 @@ | ||
2644 | 2645 | self.textwidget.configure(yscrollcommand=self.ysb.set, xscrollcommand=self.xsb.set) |
2645 | 2646 | if initial_text is not None: |
2646 | 2647 | self.replace_all(initial_text) |
2648 | + self.datadict[self.datakeyname] = initial_text | |
2647 | 2649 | self.textwidget.grid(row=0, column=0, padx=(3,0), pady=(3,0), sticky=tk.NSEW) |
2648 | 2650 | self.ysb.grid(column=1, row=0, padx=(0,3), pady=(3,0), sticky=tk.NS) |
2649 | 2651 | self.xsb.grid(column=0, row=1, padx=(3,0), pady=(0,3), sticky=tk.EW) |
@@ -2663,17 +2665,20 @@ | ||
2663 | 2665 | """Update the pane's data dictionary with data from the text widget.""" |
2664 | 2666 | text = self.textwidget.get("1.0", tk.END) |
2665 | 2667 | if is_valid: |
2666 | - if text == "": | |
2667 | - self.clear_own() | |
2668 | - else: | |
2669 | - self.datadict["text"] = text | |
2668 | + self.datadict[self.datakeyname] = text | |
2670 | 2669 | else: |
2671 | 2670 | self.clear_own() |
2672 | 2671 | |
2673 | 2672 | def valid_data(self, entry_widget=None): |
2674 | 2673 | text = self.textwidget.get("1.0", tk.END) |
2675 | - return not (text == "" and self.required) | |
2676 | - | |
2674 | + if text == "": | |
2675 | + return (not self.required) and self.blank_is_valid | |
2676 | + else: | |
2677 | + if self.entry_validator is not None: | |
2678 | + return self.entry_validator(text) | |
2679 | + else: | |
2680 | + return True | |
2681 | + | |
2677 | 2682 | def clear_pane(self): |
2678 | 2683 | widget_state = self.textwidget.cget("state") |
2679 | 2684 | self.textwidget.configure(state=tk.NORMAL) |
@@ -2756,7 +2761,14 @@ | ||
2756 | 2761 | del self.datadict[self.datakeyname] |
2757 | 2762 | self.datakeyname = key_name |
2758 | 2763 | self.datakeylist = [key_name] |
2759 | - | |
2764 | + | |
2765 | + def set_entry_validator(self, fn): | |
2766 | + """Set the callback function that will be used to check the entered value. | |
2767 | + | |
2768 | + This function must take the entry value as an argument and return a Boolean. | |
2769 | + """ | |
2770 | + self.entry_validator = fn | |
2771 | + | |
2760 | 2772 | |
2761 | 2773 | |
2762 | 2774 | class UserPane(tkpane.TkPane): |
@@ -28,7 +28,7 @@ | ||
28 | 28 | that pass specific data to allow communication between panes, and callback methods |
29 | 29 | for reporting status and progress.""" |
30 | 30 | |
31 | -__version__ = "0.29.0" | |
31 | +__version__ = "0.30.0" | |
32 | 32 | |
33 | 33 | |
34 | 34 | try: |
@@ -792,6 +792,18 @@ | ||
792 | 792 | # Utility functions |
793 | 793 | #------------------------------------------------------------------------------- |
794 | 794 | |
795 | +def run_validity_callbacks(panelist): | |
796 | + """Run the 'on_exit_data_(in)valid' callbacks for all panes in the list, | |
797 | + | |
798 | + This function is intended to be used after all panes have been instantiated, | |
799 | + when some panes have default values and are required by other panes. This | |
800 | + function will ensure that the dependent panes have the required initial data | |
801 | + values. | |
802 | + """ | |
803 | + for p in panelist: | |
804 | + p.handle_exit_validity(p.valid_data()) | |
805 | + | |
806 | + | |
795 | 807 | def enable_or_disable_all(panelist): |
796 | 808 | """Enable or disable all panes in the list, as required by their data status. |
797 | 809 |