diff --git a/PySimpleGUI.py b/PySimpleGUI.py index 230d927e..c17831dd 100644 --- a/PySimpleGUI.py +++ b/PySimpleGUI.py @@ -35,7 +35,7 @@ OK, let's get the bullshit out of the way This software is available for your use under a MODIFIED LGPL3+ license -This notice, these first 83 lines of code shall remain unchanged +This notice, these first 100 lines of code shall remain unchanged # # ## ## #### ##### # ###### # ###### ##### @@ -60,12 +60,13 @@ And just what the fuck is that? Well, it's LPGL3+ and these FOUR simple stipula 1. These and all comments are to remain in this document 2. You will not post this software in a repository or a location for others to download from: A. Unless you have made 10 lines of changes + B. A notice is posted with the code that it is not the original code but instead derived from an original 3. Forking is OK and does NOT require any changes as long as it is obvious forked and stated on the page where your software is being hosted. For example, GitHub does a fantastic job of indicating if a repository is the result of a fork. -4. The "Official" version of PySimpleGUI and the associated documentation lives on two (and only two) places: +4. The "Official" version of PySimpleGUI and the associated documentation lives on two (and **only** two) places: 1. GitHub - (http://www.PySimpleGUI.com) currently pointing at: - # https://github.com/PySimpleGUI/PySimpleGUI + https://github.com/PySimpleGUI/PySimpleGUI 2. Read the Docs (via http://www.PySimpleGUI.org). Currently is pointed at: https://pysimplegui.readthedocs.io/en/latest/ If you've obtained this software in any other way, then those listed here, then SUPPORT WILL NOT BE PROVIDED. @@ -73,7 +74,7 @@ And just what the fuck is that? Well, it's LPGL3+ and these FOUR simple stipula ----------------------------------------------------------------------------------------------------------------- -I absolutely hate having to include that nonsense, but every word is there for solid reasons. +I absolutely hate having to include this kind of nonsense, but every word is here for solid reasons. How about having FUN with this package?? Terrible note to begin this journey of actually having fun making GUI based applications so I'll try to make it up to you. @@ -96,6 +97,7 @@ The User Manual and the Cookbook are both designed to paint some nice looking GU + # do the Python 2 or 3 check so the right tkinter stuff can get pulled in import sys if sys.version_info[0] >= 3: @@ -119,7 +121,7 @@ import pickle import calendar import textwrap import inspect -from typing import List, Any, Union, Tuple, Dict +from typing import List, Any, Union, Tuple, Dict # because this code has to run on 2.7 can't use real type hints. Must do typing only in comments from random import randint import warnings @@ -1713,12 +1715,12 @@ class StatusBar(Element): key=None, tooltip=None, visible=True): """ - :param text: Text that is to be displayed in the widget - :param size: (w,h) w=characters-wide, h=rows-high - :param auto_size_text: True if size should fit the text length + :param text: (str) Text that is to be displayed in the widget + :param size: Tuple[(int), (int)] (w,h) w=characters-wide, h=rows-high + :param auto_size_text: (bool) True if size should fit the text length :param click_submits: (bool) DO NOT USE. Only listed for backwards compat - Use enable_events instead :param enable_events: (bool) Turns on the element specific events. StatusBar events occur when the bar is clicked - :param relief: relief style. Values are same as progress meter relief values. Can be a constant or a string: `RELIEF_RAISED RELIEF_SUNKEN RELIEF_FLAT RELIEF_RIDGE RELIEF_GROOVE RELIEF_SOLID` + :param relief: (enum) relief style. Values are same as progress meter relief values. Can be a constant or a string: `RELIEF_RAISED RELIEF_SUNKEN RELIEF_FLAT RELIEF_RIDGE RELIEF_GROOVE RELIEF_SOLID` :param font: Union[str, tuple] specifies the font family, size, etc :param text_color: (str) color of the text :param background_color: (str) color of background @@ -1748,7 +1750,6 @@ class StatusBar(Element): def Update(self, value=None, background_color=None, text_color=None, font=None, visible=None): """ Changes some of the settings for the Status Bar Element. Must call `Window.Read` or `Window.Finalize` prior - :param value: (str) new text to show :param background_color: (str) color of background :param text_color: (str) color of the text @@ -1871,7 +1872,9 @@ class TKProgressBar(): # Scroll bar will span the length of the frame # # ---------------------------------------------------------------------- # class TKOutput(tk.Frame): - """ """ + """ + tkinter style class. Inherits Frame class from tkinter. Adds a tk.Text and a scrollbar together + """ def __init__(self, parent, width, height, bd, background_color=None, text_color=None, font=None, pad=None): """ @@ -1938,25 +1941,24 @@ class TKOutput(tk.Frame): # Routes stdout, stderr to a scrolled window # # ---------------------------------------------------------------------- # class Output(Element): - """ """ - + """ + Output Element - a multi-lined text area where stdout and stderr are re-routed to. + """ def __init__(self, size=(None, None), background_color=None, text_color=None, pad=None, font=None, tooltip=None, key=None, right_click_menu=None, visible=True): """ - - :param size: (w,h) w=characters-wide, h=rows-high - :param background_color: color of background - :param text_color: color of the text - :param pad: Amount of padding to put around element - :param font: specifies the font family, size, etc + :param size: Tuple[(int), (int)] (w,h) w=characters-wide, h=rows-high + :param background_color: (str) color of background + :param text_color: (str) color of the text + :param pad: (int, int) or ((int, int),(int,int)) Amount of padding to put around element (left/right, top/bottom) or ((left, right), (top, bottom)) + :param font: Union[str, tuple] specifies the font family, size, etc :param tooltip: (str) text, that will appear when mouse hovers over the element - :param key: Used with window.FindElement and with return values to uniquely identify this element to uniquely identify this element - :param right_click_menu: List[List[str]] see "Right Click Menus" for format - :param visible: set visibility state of the element (Default = True) - + :param key: (Any) Used with window.FindElement and with return values to uniquely identify this element to uniquely identify this element + :param right_click_menu: List[List[str]] see "Right Click Menus" + :param visible: (bool) set visibility state of the element """ - self._TKOut = None + self._TKOut = self.Widget = None # type: TKOutput bg = background_color if background_color else DEFAULT_INPUT_ELEMENTS_COLOR fg = text_color if text_color is not None else DEFAULT_INPUT_TEXT_COLOR self.RightClickMenu = right_click_menu @@ -1976,7 +1978,7 @@ class Output(Element): """ Changes some of the settings for the Output Element. Must call `Window.Read` or `Window.Finalize` prior - :param value: + :param value: (str) string that will replace current contents of the output area :param visible: (bool) control visibility of element """ @@ -4809,19 +4811,27 @@ class Window: @classmethod def GetAContainerNumber(cls): - """ """ + """ + Not user callable! + :return: A simple counter that makes each container element unique + """ cls.container_element_counter += 1 return cls.container_element_counter @classmethod def IncrementOpenCount(self): - """ """ + """ + Not user callable! Increments the number of open windows + Note - there is a bug where this count easily gets out of sync. Issue has been opened already. No ill effects + """ self.NumOpenWindows += 1 # print('+++++ INCREMENTING Num Open Windows = {} ---'.format(Window.NumOpenWindows)) @classmethod def DecrementOpenCount(self): - """ """ + """ + Not user callable! Decrements the number of open windows + """ self.NumOpenWindows -= 1 * (self.NumOpenWindows != 0) # decrement if not 0 # print('----- DECREMENTING Num Open Windows = {} ---'.format(Window.NumOpenWindows)) @@ -4867,7 +4877,9 @@ class Window: # ------------------------- Add Multiple Rows to Form ------------------------- # def AddRows(self, rows): """ - Loops through a list of lists of elements and adds each row, list, to the layout + Loops through a list of lists of elements and adds each row, list, to the layout. + This is NOT the best way to go about creating a window. Sending the entire layout at one time and passing + it as a parameter to the Window call is better. :param rows: List[List[Elements]] A list of a list of elements @@ -4878,22 +4890,24 @@ class Window: def Layout(self, rows): """ Second of two preferred ways of telling a Window what its layout is. The other way is to pass the layout as - a parameter to Window object. The parameter method is the currently preferred method. This call to Layout - has been removed from examples contained in documents and in the Demo Programs. Trying to remove this call + a parameter to Window object. The parameter method is the currently preferred method. This call to Layout + has been removed from examples contained in documents and in the Demo Programs. Trying to remove this call from history and replace with sending as a parameter to Window. :param rows: List[List[Elements]] Your entire layout + :return: (Window} self so that you can chain method calls """ self.AddRows(rows) - self.BuildKeyDict() + self._BuildKeyDict() return self + def LayoutAndRead(self, rows, non_blocking=False): """ - Deprecated. Now you layout your window's rows (layout) and then separately call Read. - :param rows: - :param non_blocking: (Default = False) + Deprecated!! Now your layout your window's rows (layout) and then separately call Read. + :param rows: (List[List[Element]]) The layout of the window + :param non_blocking: (bool) if True the Read call will not block """ raise DeprecationWarning( 'LayoutAndRead is no longer supported... change your call window.Layout(layout).Read()\nor window(title, layout).Read()') @@ -4913,9 +4927,10 @@ class Window: def _Show(self, non_blocking=False): """ NOT TO BE CALLED BY USERS. INTERNAL ONLY! - It's this + It's this method that first shows the window to the user, collects results :param non_blocking: (bool) if True, this is a non-blocking call + :return: Tuple[Any, Dict] The event, values turple that is returned from Read calls """ self.Shown = True # Compute num rows & num cols (it'll come in handy debugging) @@ -4947,7 +4962,7 @@ class Window: StartupTK(self) # If a button or keyboard event happened but no results have been built, build the results if self.LastKeyboardEvent is not None or self.LastButtonClicked is not None: - return BuildResults(self, False, self) + return _BuildResults(self, False, self) return self.ReturnValues # ------------------------- SetIcon - set the window's fav icon ------------------------- # @@ -4960,7 +4975,6 @@ class Window: :param icon: (str) Filename or bytes object :param pngbase64: (str) Base64 encoded GIF or PNG file - """ if type(icon) is bytes: wicon = tkinter.PhotoImage(data=icon) @@ -4984,6 +4998,7 @@ class Window: except: pass + def _GetElementAtLocation(self, location): """ Given a (row, col) location in a layout, return the element located at that position @@ -5010,7 +5025,6 @@ class Window: """ Function that's called by tkinter when autoclode timer expires. Closes the window - :return: None """ try: window = self @@ -5027,8 +5041,6 @@ class Window: def _TimeoutAlarmCallback(self): """ Read Timeout Alarm callback. Will kick a mainloop call out of the tkinter event loop and cause it to return - - :return: None """ # first, get the results table built # modify the Results table in the parent FlexForm object @@ -5040,7 +5052,9 @@ class Window: self.FormRemainedOpen = True self.TKroot.quit() # kick the users out of the mainloop + def Read(self, timeout=None, timeout_key=TIMEOUT_KEY): + # type: (int, Any) -> Tuple[Any, Union[Dict, List]] """ THE biggest deal method in the Window class! This is how you get all of your data from your Window. Pass in a timeout (in milliseconds) to wait for a maximum of timeout milliseconds. Will return timeout_key @@ -5074,7 +5088,7 @@ class Window: # if already have a button waiting, the return previously built results if self.LastButtonClicked is not None and not self.LastButtonClickedWasRealtime: # print(f'*** Found previous clicked saved {self.LastButtonClicked}') - results = BuildResults(self, False, self) + results = _BuildResults(self, False, self) self.LastButtonClicked = None return results InitializeResults(self) @@ -5091,7 +5105,7 @@ class Window: Window.DecrementOpenCount() # _my_windows.Decrement() # print('ROOT Destroyed') - results = BuildResults(self, False, self) + results = _BuildResults(self, False, self) if results[0] != None and results[0] != timeout_key: return results else: @@ -5145,7 +5159,7 @@ class Window: # _my_windows.Decrement() # Determine return values if self.LastKeyboardEvent is not None or self.LastButtonClicked is not None: - results = BuildResults(self, False, self) + results = _BuildResults(self, False, self) if not self.LastButtonClickedWasRealtime: self.LastButtonClicked = None return results @@ -5159,9 +5173,10 @@ class Window: self.ReturnValues = self.TimeoutKey, self.ReturnValues[1] # fake a timeout return self.ReturnValues + def _ReadNonBlocking(self): """ - Should be never called directly by the user. The user can call Window.Read(timeout=0) to get same effect + Should be NEVER called directly by the user. The user can call Window.Read(timeout=0) to get same effect :return: Tuple[(Any), Union[Dict[Any:Any], List[Any], None] (event, values) (event or timeout_key or None, Dictionary of values or List of values from all elements in the Window @@ -5192,7 +5207,8 @@ class Window: self.Values = None self.LastButtonClicked = None return None, None - return BuildResults(self, False, self) + return _BuildResults(self, False, self) + def Finalize(self): """ @@ -5223,8 +5239,7 @@ class Window: Use this call when you want something to appear in your Window immediately (as soon as this function is called). Without this call your changes to a Window will not be visible to the user until the next Read call - :return: Tuple[(Any), Union[Dict[Any:Any], List[Any], None] (event, values) - (event or timeout_key or None, Dictionary of values or List of values from all elements in the Window + :return: (Window) `self` so that method calls can be easily "chained" """ if self.TKrootDestroyed: @@ -5235,23 +5250,34 @@ class Window: pass return self + def Fill(self, values_dict): """ + Fill in elements that are input fields with data based on a 'values dictionary' - :param values_dict: ????????????????? - + :param values_dict: (Dict[Any:Any]) {Element key : value} pairs + :return: (Window) returns self so can be chained with other methods """ + FillFormWithValues(self, values_dict) return self + def FindElement(self, key, silent_on_error=False): """ - Find element object associated with the provided key - :returns Found element object, an Error Element, or None + Find element object associated with the provided key. This call can be abbreviated to any of these: + FindElement == Element == Find + So take your pick as to how much typing you want to do. + Rememeber that this call will return None if no match is found which may cause your code to crash if not + checked for. - :param key: Used with window.FindElement and with return values to uniquely identify this element - :param silent_on_error: (Default = False) + :param key: (Any) Used with window.FindElement and with return values to uniquely identify this element + :param silent_on_error: (bool) If True do not display popup nor print warning of key errors + :return: Union[Element, Error Element, None] Return value can be: + * the Element that matches the supplied key if found + * an Error Element if silent_on_error is False + * None if silent_on_error True """ try: element = self.AllKeysDict[key] @@ -5270,24 +5296,33 @@ class Window: Element = FindElement # Shortcut function Find = FindElement + def FindElementWithFocus(self): - """ """ + """ + Returns the Element that currently has focus as reported by tkinter. If no element is found None is returned! + :return: Union[Element, None] An Element if one has been found with focus or None if no element found + """ element = _FindElementWithFocusInSubForm(self) return element - def BuildKeyDict(self): - """ """ + def _BuildKeyDict(self): + """ + Used internally only! Not user callable + Builds a dictionary containing all elements with keys for this window. + """ dict = {} self.AllKeysDict = self._BuildKeyDictForWindow(self, self, dict) # print(f'keys built = {self.AllKeysDict}') def _BuildKeyDictForWindow(self, top_window, window, key_dict): """ + Loop through all Rows and all Container Elements for this window and create the keys for all of them. + Note that the calls are recursive as all pathes must be walked - :param top_window: - :param window: - :param key_dict: - + :param top_window: (Window) The highest level of the window + :param window: Union[Column, Frame, TabGroup, Pane, Tab] The "sub-window" (container element) to be searched + :param key_dict: The dictionary as it currently stands.... used as part of recursive call + :return: (dict) Dictionary filled with all keys in the window """ for row_num, row in enumerate(window.Rows): for col_num, element in enumerate(row): @@ -5324,12 +5359,13 @@ class Window: def SaveToDisk(self, filename): """ - + Saves the values contained in each of the input areas of the form. Basically saves what would be returned from + a call to Read :param filename: ????????????????? """ try: - results = BuildResults(self, False, self) + results = _BuildResults(self, False, self) with open(filename, 'wb') as sf: pickle.dump(results[1], sf) except: @@ -5442,7 +5478,7 @@ class Window: else: self.LastKeyboardEvent = str(event.keysym) + ':' + str(event.keycode) if not self.NonBlocking: - BuildResults(self, False, self) + _BuildResults(self, False, self) if self.CurrentlyRunningMainloop: # quit if this is the current mainloop, otherwise don't quit! self.TKroot.quit() @@ -5456,7 +5492,7 @@ class Window: self.FormRemainedOpen = True self.LastKeyboardEvent = 'MouseWheel:Down' if event.delta < 0 else 'MouseWheel:Up' if not self.NonBlocking: - BuildResults(self, False, self) + _BuildResults(self, False, self) if self.CurrentlyRunningMainloop: # quit if this is the current mainloop, otherwise don't quit! self.TKroot.quit() @@ -5467,7 +5503,7 @@ class Window: except: pass if not self.NonBlocking: - BuildResults(self, False, self) + _BuildResults(self, False, self) if self.TKrootDestroyed: return None self.TKrootDestroyed = True @@ -6395,7 +6431,7 @@ def InitializeResults(form): :param form: """ - BuildResults(form, True, form) + _BuildResults(form, True, form) return @@ -6428,7 +6464,7 @@ def EncodeRadioRowCol(container, row, col): # ------- FUNCTION BuildResults. Form exiting so build the results to pass back ------- # # format of return values is # (Button Pressed, input_values) -def BuildResults(form, initialize_only, top_level_form): +def _BuildResults(form, initialize_only, top_level_form): """ :param form: @@ -6445,13 +6481,13 @@ def BuildResults(form, initialize_only, top_level_form): form.DictionaryKeyCounter = 0 form.ReturnValuesDictionary = {} form.ReturnValuesList = [] - BuildResultsForSubform(form, initialize_only, top_level_form) + _BuildResultsForSubform(form, initialize_only, top_level_form) if not top_level_form.LastButtonClickedWasRealtime: top_level_form.LastButtonClicked = None return form.ReturnValues -def BuildResultsForSubform(form, initialize_only, top_level_form): +def _BuildResultsForSubform(form, initialize_only, top_level_form): """ :param form: @@ -6469,7 +6505,7 @@ def BuildResultsForSubform(form, initialize_only, top_level_form): element.DictionaryKeyCounter = top_level_form.DictionaryKeyCounter element.ReturnValuesList = [] element.ReturnValuesDictionary = {} - BuildResultsForSubform(element, initialize_only, top_level_form) + _BuildResultsForSubform(element, initialize_only, top_level_form) for item in element.ReturnValuesList: AddToReturnList(top_level_form, item) if element.UseDictionary: @@ -6481,7 +6517,7 @@ def BuildResultsForSubform(form, initialize_only, top_level_form): element.DictionaryKeyCounter = top_level_form.DictionaryKeyCounter element.ReturnValuesList = [] element.ReturnValuesDictionary = {} - BuildResultsForSubform(element, initialize_only, top_level_form) + _BuildResultsForSubform(element, initialize_only, top_level_form) for item in element.ReturnValuesList: AddToReturnList(top_level_form, item) if element.UseDictionary: @@ -6493,7 +6529,7 @@ def BuildResultsForSubform(form, initialize_only, top_level_form): element.DictionaryKeyCounter = top_level_form.DictionaryKeyCounter element.ReturnValuesList = [] element.ReturnValuesDictionary = {} - BuildResultsForSubform(element, initialize_only, top_level_form) + _BuildResultsForSubform(element, initialize_only, top_level_form) for item in element.ReturnValuesList: AddToReturnList(top_level_form, item) if element.UseDictionary: @@ -6505,7 +6541,7 @@ def BuildResultsForSubform(form, initialize_only, top_level_form): element.DictionaryKeyCounter = top_level_form.DictionaryKeyCounter element.ReturnValuesList = [] element.ReturnValuesDictionary = {} - BuildResultsForSubform(element, initialize_only, top_level_form) + _BuildResultsForSubform(element, initialize_only, top_level_form) for item in element.ReturnValuesList: AddToReturnList(top_level_form, item) if element.UseDictionary: @@ -6517,7 +6553,7 @@ def BuildResultsForSubform(form, initialize_only, top_level_form): element.DictionaryKeyCounter = top_level_form.DictionaryKeyCounter element.ReturnValuesList = [] element.ReturnValuesDictionary = {} - BuildResultsForSubform(element, initialize_only, top_level_form) + _BuildResultsForSubform(element, initialize_only, top_level_form) for item in element.ReturnValuesList: AddToReturnList(top_level_form, item) if element.UseDictionary: @@ -6670,99 +6706,27 @@ def BuildResultsForSubform(form, initialize_only, top_level_form): return form.ReturnValues -def FillFormWithValues(form, values_dict): +def FillFormWithValues(window, values_dict): + """ + Fills a window with values provided in a values dictionary { element_key : new_value } + + :param window: (Window) The window object to fill + :param values_dict: (Dict[Any:Any]) A dictionary with element keys as key and value is values parm for Update call """ - :param form: - :param values_dict: - - """ - FillSubformWithValues(form, values_dict) - - -def FillSubformWithValues(form, values_dict): - """ - - :param form: - :param values_dict: - - """ - for row_num, row in enumerate(form.Rows): - for col_num, element in enumerate(row): - value = None - if element.Type == ELEM_TYPE_COLUMN: - FillSubformWithValues(element, values_dict) - if element.Type == ELEM_TYPE_FRAME: - FillSubformWithValues(element, values_dict) - if element.Type == ELEM_TYPE_TAB_GROUP: - FillSubformWithValues(element, values_dict) - if element.Type == ELEM_TYPE_TAB: - FillSubformWithValues(element, values_dict) - try: - value = values_dict[element.Key] - except: - continue - if element.Type == ELEM_TYPE_INPUT_TEXT: - element.Update(value) - elif element.Type == ELEM_TYPE_INPUT_CHECKBOX: - element.Update(value) - elif element.Type == ELEM_TYPE_INPUT_RADIO: - element.Update(value) - elif element.Type == ELEM_TYPE_INPUT_COMBO: - element.Update(value) - elif element.Type == ELEM_TYPE_INPUT_OPTION_MENU: - element.Update(value) - elif element.Type == ELEM_TYPE_INPUT_LISTBOX: - element.SetValue(value) - elif element.Type == ELEM_TYPE_INPUT_SLIDER: - element.Update(value) - elif element.Type == ELEM_TYPE_INPUT_MULTILINE: - element.Update(value) - elif element.Type == ELEM_TYPE_INPUT_SPIN: - element.Update(value) - elif element.Type == ELEM_TYPE_BUTTON: - element.Update(value) - - -def _FindElementFromKeyInSubForm(form, key): - """ - - :param form: - :param key: Used with window.FindElement and with return values to uniquely identify this element - - """ - for row_num, row in enumerate(form.Rows): - for col_num, element in enumerate(row): - if element.Type == ELEM_TYPE_COLUMN: - matching_elem = _FindElementFromKeyInSubForm(element, key) - if matching_elem is not None: - return matching_elem - if element.Type == ELEM_TYPE_FRAME: - matching_elem = _FindElementFromKeyInSubForm(element, key) - if matching_elem is not None: - return matching_elem - if element.Type == ELEM_TYPE_TAB_GROUP: - matching_elem = _FindElementFromKeyInSubForm(element, key) - if matching_elem is not None: - return matching_elem - if element.Type == ELEM_TYPE_PANE: - matching_elem = _FindElementFromKeyInSubForm(element, key) - if matching_elem is not None: - return matching_elem - if element.Type == ELEM_TYPE_TAB: - matching_elem = _FindElementFromKeyInSubForm(element, key) - if matching_elem is not None: - return matching_elem - if element.Key == key: - return element + for element_key in values_dict: + try: + window.AllKeysDict[element_key].Update(values_dict[element_key]) + except Exception as e: + print('Problem filling form. Perhaps bad key? This is a suspected bad key: {}'.format(element_key)) def _FindElementWithFocusInSubForm(form): - # type: (...) -> Element or None """ Searches through a "sub-form" (can be a window or container) for the current element with focus - :param form: a Window, Column, Frame, or TabGroup (container elements) + :param form: a Window, Column, Frame, or TabGroup (container elements) + :return: Union[Element, None] """ for row_num, row in enumerate(form.Rows): for col_num, element in enumerate(row): @@ -6799,7 +6763,7 @@ def _FindElementWithFocusInSubForm(form): if sys.version_info[0] >= 3: def AddMenuItem(top_menu, sub_menu_info, element, is_sub_menu=False, skip=False): """ - + Only to be used internally. Not user callable :param top_menu: :param sub_menu_info: :param element: @@ -10736,7 +10700,7 @@ def main(): frame2 = [ [Listbox(['Listbox 1', 'Listbox 2', 'Listbox 3'], select_mode=SELECT_MODE_EXTENDED, size=(20, 5))], [Combo(['Combo item 1',2,3,4 ], size=(20, 3),readonly=True, text_color='red', background_color='red', key='_COMBO1_')], - [Combo(['Combo item 1', 2,3,4], size=(20, 3), readonly=False, text_color='red', background_color='red', key='_COMBO2_')], + # [Combo(['Combo item 1', 2,3,4], size=(20, 3), readonly=False, text_color='red', background_color='red', key='_COMBO2_')], [Spin([1, 2, 3, 'a','b','c'], size=(4, 3))], ] @@ -10767,8 +10731,8 @@ def main(): [graph_elem], ] - tab1 = Tab('Graph Number 1', frame6, tooltip='tab 1') - tab2 = Tab('Graph Number 2', [[]]) + tab1 = Tab('Graph Number 1', frame6, tooltip='tab 1', ) + tab2 = Tab('Graph Number 2', [[]],) layout1 = [ [Image(data=DEFAULT_BASE64_ICON)], @@ -10783,7 +10747,7 @@ def main(): Frame('Variable Choice Group', frame4, title_color='blue')], [Frame('Structured Data Group', frame5, title_color='red'), ], # [Frame('Graphing Group', frame6)], - [TabGroup([[tab1, tab2]])], + [TabGroup([[tab1, tab2]], )], [ProgressBar(max_value=800, size=(60, 25), key='+PROGRESS+'), Button('Button'), B('Normal'), Button('Exit', tooltip='Exit button')], ] @@ -10796,6 +10760,7 @@ def main(): right_click_menu=['&Right', ['Right', '!&Click', '&Menu', 'E&xit', 'Properties']], # transparent_color= '#9FB8AD', resizable=True, + debugger_enabled=False, # icon=r'X:\VMWare Virtual Machines\SHARED FOLDER\kingb.ico' ).Finalize() graph_elem.DrawCircle((200, 200), 50, 'blue')