From 5aaa18fb51141d6f711e294024846ea13b9b7d7a Mon Sep 17 00:00:00 2001 From: MikeTheWatchGuy Date: Sat, 26 Jan 2019 14:31:53 -0500 Subject: [PATCH 1/6] Relase 0.4.0 - Events for all elements, spinner, multiline output with updates, new test harness --- PySimpleGUIWeb/PySimpleGUIWeb.py | 621 ++++++++++++++++--------------- 1 file changed, 314 insertions(+), 307 deletions(-) diff --git a/PySimpleGUIWeb/PySimpleGUIWeb.py b/PySimpleGUIWeb/PySimpleGUIWeb.py index ad271c0c..268df4bb 100644 --- a/PySimpleGUIWeb/PySimpleGUIWeb.py +++ b/PySimpleGUIWeb/PySimpleGUIWeb.py @@ -12,6 +12,8 @@ from queue import Queue # from remi import start, App import remi import logging +import traceback + g_time_start = 0 g_time_end = 0 @@ -229,9 +231,11 @@ ELEM_TYPE_INPUT_COMBO = 'combo' ELEM_TYPE_INPUT_OPTION_MENU = 'option menu' ELEM_TYPE_INPUT_RADIO = 'radio' ELEM_TYPE_INPUT_MULTILINE = 'multiline' +ELEM_TYPE_MULTILINE_OUTPUT = 'multioutput' ELEM_TYPE_INPUT_CHECKBOX = 'checkbox' -ELEM_TYPE_INPUT_SPIN = 'spind' +ELEM_TYPE_INPUT_SPIN = 'spin' ELEM_TYPE_BUTTON = 'button' +ELEM_TYPE_BUTTONMENU = 'buttonmenu' ELEM_TYPE_IMAGE = 'image' ELEM_TYPE_CANVAS = 'canvas' ELEM_TYPE_FRAME = 'frame' @@ -387,6 +391,12 @@ class Element(): return rc return None + # ------------------------- REMI CHANGED CALLBACK ----------------------- + # called when a widget has changed and the element has events enabled + def ChangedCallback(self, widget:remi.Widget, *args): + self.ParentForm.LastButtonClicked = self.Key if self.Key is not None else '' + self.ParentForm.MessageQueue.put(self.ParentForm.LastButtonClicked) + def TextClickedHandler(self, event): if self.Key is not None: self.ParentForm.LastButtonClicked = self.Key @@ -521,6 +531,11 @@ class InputText(Element): super().__init__(ELEM_TYPE_INPUT_TEXT, size=size, background_color=bg, text_color=fg, key=key, pad=pad, font=font, tooltip=tooltip, visible=visible, size_px=size_px) + def InputTextCallback(self, widget:remi.Widget, value, keycode): + # print(f'text widget value = {widget.get_value()}') + self.ParentForm.LastButtonClicked = chr(int(keycode)) + self.ParentForm.MessageQueue.put(self.ParentForm.LastButtonClicked) + def Update(self, value=None, disabled=None): if disabled is True: self.TKEntry['state'] = 'disabled' @@ -580,11 +595,6 @@ class Combo(Element): text_color=fg, key=key, pad=pad, tooltip=tooltip, font=font or DEFAULT_FONT, visible=visible, size_px=size_px) - def QtCurrentItemChanged(self, state): - if self.ChangeSubmits: - element_callback_quit_mainloop(self) - - def Update(self, value=None, values=None, set_to_index=None, disabled=None, readonly=None, background_color=None, text_color=None, font=None, visible=None): if values is not None: @@ -678,14 +688,14 @@ InputOptionMenu = OptionMenu # Listbox # # ---------------------------------------------------------------------- # class Listbox(Element): - def __init__(self, values, default_values=None, select_mode=None, change_submits=False, enable_events=False, bind_return_key=False, size=(None, None), disabled=False, auto_size_text=None, font=None, background_color=None, - text_color=None, key=None, pad=None, tooltip=None, visible=True, size_px=(None,None)): - ''' - Listbox Element + def __init__(self, values, default_values=None, select_mode=None, change_submits=False, enable_events=False, bind_return_key=False, size=(None, None), disabled=False, auto_size_text=None, font=None, background_color=None, text_color=None, key=None, pad=None, tooltip=None, visible=True, size_px=(None,None)): + """ + :param values: :param default_values: :param select_mode: :param change_submits: + :param enable_events: :param bind_return_key: :param size: :param disabled: @@ -696,7 +706,9 @@ class Listbox(Element): :param key: :param pad: :param tooltip: - ''' + :param visible: + :param size_px: + """ self.Values = values self.DefaultValues = default_values self.TKListbox = None @@ -725,11 +737,6 @@ class Listbox(Element): super().__init__(ELEM_TYPE_INPUT_LISTBOX, size=tsize, auto_size_text=auto_size_text, font=font, background_color=bg, text_color=fg, key=key, pad=pad, tooltip=tooltip, visible=visible, size_px=size_px) - def QtCurrentRowChanged(self, state): - if self.ChangeSubmits: - element_callback_quit_mainloop(self) - - def Update(self, values=None, disabled=None, set_to_index=None,background_color=None, text_color=None, font=None, visible=None): if values is not None: self.Values = values @@ -849,20 +856,21 @@ class Checkbox(Element): background_color=background_color, text_color=self.TextColor, key=key, pad=pad, tooltip=tooltip, visible=visible, size_px=size_px) + def ChangedCallback(self, widget:remi.Widget, value): + # print(f'text widget value = {widget.get_value()}') + self.ParentForm.LastButtonClicked = self.Key + self.ParentForm.MessageQueue.put(self.ParentForm.LastButtonClicked) + def Get(self): - return self.WxCheckbox.GetValue() + return self.Widget.get_value() def Update(self, value=None, disabled=None): if value is not None: - try: - self.WxCheckbox.SetValue(value) - self.InitialState = value - except: - pass + self.Widget.set_value(value) if disabled == True: - self.WxCheckbox.Disable() + self.Widget.set_enabled(False) elif disabled == False: - self.WxCheckbox.Enable() + self.Widget.set_enabled(True) def __del__(self): super().__del__() @@ -882,9 +890,8 @@ Check = Checkbox class Spin(Element): # Values = None # TKSpinBox = None - def __init__(self, values, initial_value=None, disabled=False, change_submits=False, size=(None, None), - auto_size_text=None, font=None, background_color=None, text_color=None, key=None, pad=None, - tooltip=None): + def __init__(self, values, initial_value=None, disabled=False, change_submits=False, enable_events=False, size=(None, None), readonly=True, auto_size_text=None, font=None, background_color=None, text_color=None, key=None, pad=None, + tooltip=None, visible=True, size_px=(None,None)): ''' Spinner Element :param values: @@ -901,50 +908,41 @@ class Spin(Element): :param tooltip: ''' self.Values = values - self.DefaultValue = initial_value - self.ChangeSubmits = change_submits - self.TKSpinBox = None + self.DefaultValue = initial_value or values[0] + self.ChangeSubmits = change_submits or enable_events self.Disabled = disabled 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.CurrentValue = self.DefaultValue + self.ReadOnly = readonly + self.Widget = None # type: remi.gui.SpinBox super().__init__(ELEM_TYPE_INPUT_SPIN, size, auto_size_text, font=font, background_color=bg, text_color=fg, - key=key, pad=pad, tooltip=tooltip) + key=key, pad=pad, tooltip=tooltip, visible=visible, size_px=size_px) return - def Update(self, value=None, values=None, disabled=None): + + def Update(self, value=None, values=None, disabled=None, background_color=None, text_color=None, font=None, visible=None): if values != None: - old_value = self.TKStringVar.get() self.Values = values - self.TKSpinBox.configure(values=values) - self.TKStringVar.set(old_value) + self.QT_Spinner.setStrings(values) + # self.QT_Spinner.setRange(self.Values[0], self.Values[1]) if value is not None: + # self.QT_Spinner.setValue(value) try: - self.TKStringVar.set(value) + self.QT_Spinner.setValue(self.QT_Spinner.valueFromText(value)) + self.DefaultValue = value except: pass - self.DefaultValue = value if disabled == True: - self.TKSpinBox.configure(state='disabled') + self.QT_Spinner.setDisabled(True) elif disabled == False: - self.TKSpinBox.configure(state='normal') + self.QT_Spinner.setDisabled(False) + super().Update(self.QT_Spinner, background_color=background_color, text_color=text_color, font=font, visible=visible) - def SpinChangedHandler(self, event): - # first, get the results table built - # modify the Results table in the parent FlexForm object - if self.Key is not None: - self.ParentForm.LastButtonClicked = self.Key - else: - self.ParentForm.LastButtonClicked = '' - self.ParentForm.FormRemainedOpen = True - if self.ParentForm.CurrentlyRunningMainloop: - self.ParentForm.TKroot.quit() # kick the users out of the mainloop + def Get(self): + return self.Widget.get_value() def __del__(self): - try: - self.TKSpinBox.__del__() - except: - pass super().__del__() @@ -953,9 +951,8 @@ class Spin(Element): # ---------------------------------------------------------------------- # class Multiline(Element): def __init__(self, default_text='', enter_submits=False, disabled=False, autoscroll=False, size=(None, None), - auto_size_text=None, background_color=None, text_color=None, change_submits=False, do_not_clear=False, - key=None, focus=False, - font=None, pad=None, tooltip=None): + auto_size_text=None, background_color=None, text_color=None, change_submits=False, enable_events=False, do_not_clear=False, + key=None, focus=False, font=None, pad=None, tooltip=None, visible=True, size_px=(None,None)): ''' Multiline Element :param default_text: @@ -981,42 +978,122 @@ class Multiline(Element): fg = text_color if text_color is not None else DEFAULT_INPUT_TEXT_COLOR self.Autoscroll = autoscroll self.Disabled = disabled - self.ChangeSubmits = change_submits + self.ChangeSubmits = change_submits or enable_events + tsize = size # convert tkinter size to pixels + if size[0] is not None and size[0] < 100: + tsize = size[0]*DEFAULT_PIXELS_TO_CHARS_SCALING[0], size[1]*DEFAULT_PIXELS_TO_CHARS_SCALING[1] + self.Widget = None # type: remi.gui.TextInput - super().__init__(ELEM_TYPE_INPUT_MULTILINE, size=size, auto_size_text=auto_size_text, background_color=bg, - text_color=fg, key=key, pad=pad, tooltip=tooltip, font=font or DEFAULT_FONT) + super().__init__(ELEM_TYPE_INPUT_MULTILINE, size=tsize, auto_size_text=auto_size_text, background_color=bg, + text_color=fg, key=key, pad=pad, tooltip=tooltip, font=font or DEFAULT_FONT, visible=visible, size_px=size_px) return - def Update(self, value=None, disabled=None, append=False, font=None, text_color=None, background_color=None): - if value is not None: - try: - if not append: - self.TKText.delete('1.0', tk.END) - self.TKText.insert(tk.END, value) - except: - pass - self.DefaultText = value - if self.Autoscroll: - self.TKText.see(tk.END) - if disabled == True: - self.TKText.configure(state='disabled') - elif disabled == False: - self.TKText.configure(state='normal') - if background_color is not None: - self.TKText.configure(background=background_color) - if text_color is not None: - self.TKText.configure(fg=text_color) - if font is not None: - self.TKText.configure(font=font) + def InputTextCallback(self, widget:remi.Widget, value, keycode): + # print(f'text widget value = {widget.get_value()}') + self.ParentForm.LastButtonClicked = chr(int(keycode)) + self.ParentForm.MessageQueue.put(self.ParentForm.LastButtonClicked) + + def Update(self, value=None, disabled=None, append=False, background_color=None, text_color=None, font=None, visible=None): + if value is not None and not append: + self.Widget.set_value(value) + elif value is not None and append: + text = self.Widget.get_value() + str(value) + self.Widget.set_value(text) + # if background_color is not None: + # self.WxTextCtrl.SetBackgroundColour(background_color) + # if text_color is not None: + # self.WxTextCtrl.SetForegroundColour(text_color) + # if font is not None: + # self.WxTextCtrl.SetFont(font) + # if disabled: + # self.WxTextCtrl.Enable(True) + # elif disabled is False: + # self.WxTextCtrl.Enable(False) + super().Update(self.Widget, background_color=background_color, text_color=text_color, font=font, visible=visible) + + # + # def Update(self, value=None, disabled=None, append=False, background_color=None, text_color=None, font=None, visible=None): + # if value is not None and not append: + # self.DefaultText = value + # self.QT_TextEdit.setText(str(value)) + # elif value is not None and append: + # self.DefaultText = value + # self.QT_TextEdit.setText(self.QT_TextEdit.toPlainText() + str(value)) + # if disabled == True: + # self.QT_TextEdit.setDisabled(True) + # elif disabled == False: + # self.QT_TextEdit.setDisabled(False) + # super().Update(self.QT_TextEdit, background_color=background_color, text_color=text_color, font=font, visible=visible) + def Get(self): - return self.TKText.get(1.0, tk.END) + self.WxTextCtrl.GetValue() def SetFocus(self): - try: - self.TKText.focus_set() - except: - pass + self.WxTextCtrl.SetFocus() + + + def __del__(self): + super().__del__() + + +# ---------------------------------------------------------------------- # +# Multiline Output # +# ---------------------------------------------------------------------- # +class MultilineOutput(Element): + def __init__(self, default_text='', enter_submits=False, disabled=False, autoscroll=False, size=(None, None), auto_size_text=None, background_color=None, text_color=None, change_submits=False, enable_events=False, do_not_clear=False, key=None, focus=False, font=None, pad=None, tooltip=None, visible=True, size_px=(None,None)): + ''' + Multiline Element + :param default_text: + :param enter_submits: + :param disabled: + :param autoscroll: + :param size: + :param auto_size_text: + :param background_color: + :param text_color: + :param do_not_clear: + :param key: + :param focus: + :param pad: + :param tooltip: + :param font: + ''' + self.DefaultText = default_text + self.EnterSubmits = enter_submits + bg = background_color if background_color else DEFAULT_INPUT_ELEMENTS_COLOR + self.Focus = focus + self.do_not_clear = do_not_clear + fg = text_color if text_color is not None else DEFAULT_INPUT_TEXT_COLOR + self.Autoscroll = autoscroll + self.Disabled = disabled + self.ChangeSubmits = change_submits or enable_events + tsize = size # convert tkinter size to pixels + if size[0] is not None and size[0] < 100: + tsize = size[0]*DEFAULT_PIXELS_TO_CHARS_SCALING[0], size[1]*DEFAULT_PIXELS_TO_CHARS_SCALING[1] + self.Widget = None # type: remi.gui.TextInput + self.CurrentValue = '' + + super().__init__(ELEM_TYPE_MULTILINE_OUTPUT, size=tsize, auto_size_text=auto_size_text, background_color=bg, + text_color=fg, key=key, pad=pad, tooltip=tooltip, font=font or DEFAULT_FONT, visible=visible, size_px=size_px) + return + + + def Update(self, value=None, disabled=None, append=False, background_color=None, text_color=None, font=None, visible=None): + if value is not None and not append: + self.Widget.set_value(value) + elif value is not None and append: + self.CurrentValue = self.CurrentValue + '\n' + value + self.Widget.set_value(self.CurrentValue) + + super().Update(self.Widget, background_color=background_color, text_color=text_color, font=font, visible=visible) + + + def Get(self): + self.WxTextCtrl.GetValue() + + def SetFocus(self): + self.WxTextCtrl.SetFocus() def __del__(self): super().__del__() @@ -2101,26 +2178,23 @@ class TabGroup(Element): # ---------------------------------------------------------------------- # class Slider(Element): def __init__(self, range=(None, None), default_value=None, resolution=None, tick_interval=None, orientation=None, - border_width=None, relief=None, change_submits=False, disabled=False, size=(None, None), font=None, - background_color=None, text_color=None, key=None, pad=None, tooltip=None): - ''' - Slider Element + border_width=None, relief=None, change_submits=False, enable_events=False, disabled=False, size=(None, None), font=None, + background_color=None, text_color=None, key=None, pad=None, tooltip=None, visible=True, size_px=(None,None)): + """ + :param range: :param default_value: :param resolution: + :param tick_interval: :param orientation: :param border_width: :param relief: :param change_submits: + :param enable_events: :param disabled: - :param size: - :param font: - :param background_color: - :param text_color: - :param key: - :param pad: - :param tooltip: - ''' + :param visible: + :param size_px: + """ self.TKScale = None self.Range = (1, 10) if range == (None, None) else range self.DefaultValue = self.Range[0] if default_value is None else default_value @@ -2128,15 +2202,18 @@ class Slider(Element): self.BorderWidth = border_width if border_width else DEFAULT_SLIDER_BORDER_WIDTH self.Relief = relief if relief else DEFAULT_SLIDER_RELIEF self.Resolution = 1 if resolution is None else resolution - self.ChangeSubmits = change_submits + self.ChangeSubmits = change_submits or enable_events self.Disabled = disabled self.TickInterval = tick_interval temp_size = size if temp_size == (None, None): - temp_size = (20, 20) if orientation.startswith('h') else (8, 20) + temp_size = (200, 20) if self.Orientation.startswith('h') else (200, 20) + elif size[0] is not None and size[0] < 100: + temp_size = size[0]*10, size[1]*3 + self.Widget = None # type: remi.gui.Slider super().__init__(ELEM_TYPE_INPUT_SLIDER, size=temp_size, font=font, background_color=background_color, - text_color=text_color, key=key, pad=pad, tooltip=tooltip) + text_color=text_color, key=key, pad=pad, tooltip=tooltip, visible=visible, size_px=size_px) return def Update(self, value=None, range=(None, None), disabled=None): @@ -2153,21 +2230,15 @@ class Slider(Element): elif disabled == False: self.TKScale['state'] = 'normal' - def SliderChangedHandler(self, event): - # first, get the results table built - # modify the Results table in the parent FlexForm object - if self.Key is not None: - self.ParentForm.LastButtonClicked = self.Key - else: - self.ParentForm.LastButtonClicked = '' - self.ParentForm.FormRemainedOpen = True - if self.ParentForm.CurrentlyRunningMainloop: - self.ParentForm.TKroot.quit() # kick the users out of the mainloop + def SliderCallback(self, widget:remi.Widget, value): + self.ParentForm.LastButtonClicked = self.Key if self.Key is not None else '' + self.ParentForm.MessageQueue.put(self.ParentForm.LastButtonClicked) def __del__(self): super().__del__() + # # ---------------------------------------------------------------------- # # Column # @@ -2544,6 +2615,7 @@ class Window: highest_level_app = None stdout_is_rerouted = False stdout_location = None + port_number = 6900 def __init__(self, title, default_element_size=DEFAULT_ELEMENT_SIZE, default_button_element_size=(None, None), auto_size_text=None, auto_size_buttons=None, location=(None, None), size=(None, None), @@ -2763,22 +2835,18 @@ class Window: # print("** REALTIME PROBLEM FOUND **", results) # print('****************** CALLING MESSAGE QUEUE GET ***********************') self.CurrentlyRunningMainloop = True - if timeout is not None and timeout != 0: + if timeout is not None: try: - self.LastButtonClicked = self.MessageQueue.get(timeout=timeout/1000) + self.LastButtonClicked = self.MessageQueue.get(timeout=(timeout if timeout else .001)/1000) + # print(f'Got event {self.LastButtonClicked}') except: # timeout self.LastButtonClicked = timeout_key - elif timeout == 0: - try: - self.LastButtonClicked = self.MessageQueue.get_nowait() - except: - self.LastButtonClicked = timeout_key else: self.LastButtonClicked = self.MessageQueue.get() + # print(f'Got event {self.LastButtonClicked}') # print('--------------------- BACK FROM MESSAGE QUEUE GET ----------------------') results = BuildResults(self, False, self) - # print('Results = ', results) return results # print(f'In main {self.Title}') ################################# CALL GUWxTextCtrlI MAINLOOP ############################ @@ -2815,7 +2883,6 @@ class Window: # print("*** Faking timeout ***") # self.ReturnValues = self.TimeoutKey, self.ReturnValues[1] # fake a timeout # return self.ReturnValues - return None, None def _ReadNonBlocking(self): if self.TKrootDestroyed: @@ -3001,7 +3068,8 @@ class Window: def Close(self): self.App.close() - + self.App.server.server_starter_instance._alive = False + self.App.server.server_starter_instance._sserver.shutdown() if self.TKrootDestroyed: return # try: @@ -3112,6 +3180,7 @@ class Window: def remi_thread(self): + logging.getLogger('remi').setLevel(logging.WARNING) logging.getLogger('remi').disabled = True logging.getLogger('remi.server.ws').disabled = True logging.getLogger('remi.server').disabled = True @@ -3122,8 +3191,15 @@ class Window: # logging.getLogger('remi').setLevel(level=logging.CRITICAL) # logging.getLogger('remi').disabled = True # logging.disable(logging.CRITICAL) - remi.start(self.MyApp, title=self.Title ,debug=False, address='0.0.0.0', port=0, start_browser=True, userdata=(self,)) # standalone=True) - self.MessageQueue.put(None) + # s = remi.server.StandaloneServer(self.MyApp, width=1100, height=600) + # s.start() + Window.port_number += 1 + remi.start(self.MyApp, title=self.Title ,debug=False, address='0.0.0.0', port=Window.port_number, start_browser=True, update_interval=.00001, userdata=(self,)) + # remi.start(self.MyApp, title=self.Title ,debug=False, userdata=(self,), standalone=True) # standalone=True) + print('Returned from Remi Start command... now sending None event') + # self.App.server_starter_instance._alive = False + # self.App.server_starter_instance._sserver.shutdown() + self.MessageQueue.put(None) # if returned from start call, then the window has been destroyed and a None event should be generated class MyApp(remi.App): @@ -3142,33 +3218,29 @@ class Window: wid.style['align-items'] = 'baseline' if self.window.BackgroundColor not in (None, COLOR_SYSTEM_DEFAULT): wid.style['background-color'] = self.window.BackgroundColor - - PackFormIntoFrame(self.window, wid, self.window) - # - # lbl = remi.gui.Label("Close or reload the page, the console thread will stop automatically.") - # wid.append(lbl) - # self.bt = remi.gui.Button('Close') - # self.bt.onclick.connect(self.on_close_button) - # wid.append(self.bt) - + try: + PackFormIntoFrame(self.window, wid, self.window) + except: + print('* ERROR PACKING FORM *') + print(traceback.format_exc()) # add the following 3 lines to your app and the on_window_close method to make the console close automatically tag = remi.gui.Tag(_type='script') tag.add_child("javascript", """window.onunload=function(e){sendCallback('%s','%s');return "close?";};""" % ( str(id(self)), "on_window_close")) wid.add_child("onunloadevent", tag) - # TODO: Send message that layout is complete - - self.window.MessageQueue.put('Layout complete') - # returning the root widget - return wid + self.window.MessageQueue.put('Layout complete') # signal the main code that the layout is all done + return wid # returning the root widget def on_window_close(self): # here you can handle the unload # print("app closing") - self.window.MessageQueue.put(None) self.close() + self.server.server_starter_instance._alive = False + self.server.server_starter_instance._sserver.shutdown() + self.window.MessageQueue.put(None) + print("server stopped") FlexForm = Window @@ -3184,8 +3256,11 @@ def element_callback_quit_mainloop(element): element.ParentForm.LastButtonClicked = element.Key else: element.ParentForm.LastButtonClicked = '' - element.ParentForm.FormRemainedOpen = True - element.ParentForm.LastButtonClicked = element.Key if element.Key is not None else element.ButtonText + try: + element.ParentForm.LastButtonClicked = element.Key if element.Key is not None else element.ButtonText + except: + element.ParentForm.LastButtonClicked = element.Key + # print(f'Putting into message queue {element.ParentForm.LastButtonClicked}') element.ParentForm.MessageQueue.put(element.ParentForm.LastButtonClicked) @@ -3628,22 +3703,14 @@ def BuildResultsForSubform(form, initialize_only, top_level_form): # items = element.TKListbox.curselection() # value = [element.Values[int(item)] for item in items] elif element.Type == ELEM_TYPE_INPUT_SPIN: - try: - value = element.TKStringVar.get() - except: - value = 0 + element = element # type: Spin + value = element.Widget.get_value() elif element.Type == ELEM_TYPE_INPUT_SLIDER: - try: - value = element.TKIntVar.get() - except: - value = 0 + element = element # type: Slider + value = element.Widget.get_value() elif element.Type == ELEM_TYPE_INPUT_MULTILINE: - try: - value = element.TKText.get(1.0, tk.END) - if not top_level_form.NonBlocking and not element.do_not_clear and not top_level_form.ReturnKeyboardEvents: - element.TKText.delete('1.0', tk.END) - except: - value = None + element = element # type: Multiline + value = element.Widget.get_value() elif element.Type == ELEM_TYPE_TAB_GROUP: try: value = element.TKNotebook.tab(element.TKNotebook.index('current'))['text'] @@ -3914,6 +3981,10 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form): widget.style['margin'] = '{}px {}px {}px {}px'.format(*full_element_pad) if element.Disabled: widget.set_enabled(False) + if not element.Visible: + widget.attributes['hidden'] = 'true' + if element.Tooltip is not None: + widget.attributes['title'] = element.Tooltip # # widget.SetMinSize(element_size) # if element.Disabled: @@ -3982,8 +4053,6 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form): else: full_element_pad[0], full_element_pad[2] = elementpad[1] - - # ------------------------- COLUMN element ------------------------- # if element_type == ELEM_TYPE_COLUMN: pass @@ -4022,55 +4091,11 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form): element.Widget.style['text-align'] = 'center' elif element.Justification.startswith('r'): element.Widget.style['text-align'] = 'right' + if element.ClickSubmits: + element.Widget.onclick.connect(element.ChangedCallback) tk_row_frame.append(element.Widget) - # auto_size_text = element.AutoSizeText - # display_text = element.DisplayText # text to display - # if auto_size_text is False: - # width, height = element_size - # else: - # lines = display_text.split('\n') - # max_line_len = max([len(l) for l in lines]) - # num_lines = len(lines) - # if max_line_len > element_size[0]: # if text exceeds element size, the will have to wrap - # width = element_size[0] - # else: - # width = max_line_len - # height = num_lines - # ---===--- LABEL widget create and place --- # - # stringvar = tk.StringVar() - # element.TKStringVar = stringvar - # stringvar.set(display_text) - # if auto_size_text: - # width = 0 - # if element.Justification is not None: - # justification = element.Justification - # elif toplevel_form.TextJustification is not None: - # justification = toplevel_form.TextJustification - # else: - # justification = DEFAULT_TEXT_JUSTIFICATION - # justify = tk.LEFT if justification == 'left' else tk.CENTER if justification == 'center' else tk.RIGHT - # anchor = tk.NW if justification == 'left' else tk.N if justification == 'center' else tk.NE - # tktext_label = tk.Label(tk_row_frame, textvariable=stringvar, width=width, height=height, - # justify=justify, bd=border_depth, font=font) - # Set wrap-length for text (in PIXELS) == PAIN IN THE ASS - # wraplen = tktext_label.winfo_reqwidth() + 40 # width of widget in Pixels - # if not auto_size_text and height == 1: - # wraplen = 0 - # print("wraplen, width, height", wraplen, width, height) - # tktext_label.configure(anchor=anchor, wraplen=wraplen) # set wrap to width of widget - # if element.Relief is not None: - # tktext_label.configure(relief=element.Relief) - # if element.BackgroundColor is not None and element.BackgroundColor != COLOR_SYSTEM_DEFAULT: - # tktext_label.configure(background=element.BackgroundColor) - # if element.TextColor != COLOR_SYSTEM_DEFAULT and element.TextColor is not None: - # tktext_label.configure(fg=element.TextColor) - # tktext_label.pack(side=tk.LEFT, padx=element.Pad[0], pady=element.Pad[1], expand=True) - # element.TKText = tktext_label - # if element.ClickSubmits: - # tktext_label.bind('', element.TextClickedHandler) - # if element.Tooltip is not None: - # element.TooltipObject = ToolTip(element.TKText, text=element.Tooltip, timeout=DEFAULT_TOOLTIP_TIME) + # ------------------------- BUTTON element ------------------------- # elif element_type == ELEM_TYPE_BUTTON: element = element # type: Button @@ -4153,18 +4178,15 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form): # if element.Tooltip is not None: # element.TooltipObject = ToolTip(element.TKButton, text=element.Tooltip, # timeout=DEFAULT_TOOLTIP_TIME) - # # ------------------------- INPUT (Single Line) element ------------------------- # + # # ------------------------- INPUT element ------------------------- # elif element_type == ELEM_TYPE_INPUT_TEXT: element = element # type: InputText - element.Widget = remi.gui.TextInput() - if element.DefaultText: - element.Widget.set_value(element.DefaultText) + element.Widget = remi.gui.TextInput(hint=element.DefaultText) do_font_and_color(element.Widget) + if element.ChangeSubmits: + element.Widget.onkeydown.connect(element.InputTextCallback) tk_row_frame.append(element.Widget) - # default_text = element.DefaultText - # element.TKStringVar = tk.StringVar() - # element.TKStringVar.set(default_text) # show = element.PasswordCharacter if element.PasswordCharacter else "" # if element.Justification is not None: # justification = element.Justification @@ -4196,93 +4218,20 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form): if element.DefaultValue is not None: element.Widget.select_by_value(element.DefaultValue) do_font_and_color(element.Widget) + if element.ChangeSubmits: + element.Widget.onchange.connect(element.ChangedCallback) tk_row_frame.append(element.Widget) - # max_line_len = max([len(str(l)) for l in element.Values]) - # if auto_size_text is False: - # width = element_size[0] - # else: - # width = max_line_len - # element.TKStringVar = tk.StringVar() - # if element.BackgroundColor is not None and element.BackgroundColor != COLOR_SYSTEM_DEFAULT: - # combostyle = ttk.Style() - # try: - # combostyle.theme_create('combostyle', - # settings={'TCombobox': - # {'configure': - # {'selectbackground': element.BackgroundColor, - # 'fieldbackground': element.BackgroundColor, - # 'foreground': text_color, - # 'background': element.BackgroundColor} - # }}) - # except: - # try: - # combostyle.theme_settings('combostyle', - # settings={'TCombobox': - # {'configure': - # {'selectbackground': element.BackgroundColor, - # 'fieldbackground': element.BackgroundColor, - # 'foreground': text_color, - # 'background': element.BackgroundColor} - # }}) - # except: - # pass - # # ATTENTION: this applies the new style 'combostyle' to all ttk.Combobox - # combostyle.theme_use('combostyle') - # element.TKCombo = ttk.Combobox(tk_row_frame, width=width, textvariable=element.TKStringVar, font=font) - # if element.Size[1] != 1 and element.Size[1] is not None: - # element.TKCombo.configure(height=element.Size[1]) - # # element.TKCombo['state']='readonly' - # element.TKCombo['values'] = element.Values - # - # # if element.InitializeAsDisabled: - # # element.TKCombo['state'] = 'disabled' - # # if element.BackgroundColor is not None: - # # element.TKCombo.configure(background=element.BackgroundColor) - # element.TKCombo.pack(side=tk.LEFT, padx=element.Pad[0], pady=element.Pad[1]) - # if element.DefaultValue: - # for i, v in enumerate(element.Values): - # if v == element.DefaultValue: - # element.TKCombo.current(i) - # break - # else: - # element.TKCombo.current(0) - # if element.ChangeSubmits: - # element.TKCombo.bind('<>', element.ComboboxSelectHandler) - # if element.Readonly: - # element.TKCombo['state'] = 'readonly' - # if element.Disabled is True: # note overrides readonly if disabled - # element.TKCombo['state'] = 'disabled' - # if element.Tooltip is not None: - # element.TooltipObject = ToolTip(element.TKCombo, text=element.Tooltip, timeout=DEFAULT_TOOLTIP_TIME) + # ------------------------- OPTION MENU (Like ComboBox but different) element ------------------------- # elif element_type == ELEM_TYPE_INPUT_OPTION_MENU: pass - # max_line_len = max([len(str(l)) for l in element.Values]) - # if auto_size_text is False: - # width = element_size[0] - # else: - # width = max_line_len - # element.TKStringVar = tk.StringVar() - # default = element.DefaultValue if element.DefaultValue else element.Values[0] - # element.TKStringVar.set(default) - # element.TKOptionMenu = tk.OptionMenu(tk_row_frame, element.TKStringVar, *element.Values) - # element.TKOptionMenu.config(highlightthickness=0, font=font, width=width) - # element.TKOptionMenu.config(borderwidth=border_depth) - # if element.BackgroundColor is not None and element.BackgroundColor != COLOR_SYSTEM_DEFAULT: - # element.TKOptionMenu.configure(background=element.BackgroundColor) - # if element.TextColor != COLOR_SYSTEM_DEFAULT and element.TextColor is not None: - # element.TKOptionMenu.configure(fg=element.TextColor) - # element.TKOptionMenu.pack(side=tk.LEFT, padx=element.Pad[0], pady=element.Pad[1]) - # if element.Disabled == True: - # element.TKOptionMenu['state'] = 'disabled' - # if element.Tooltip is not None: - # element.TooltipObject = ToolTip(element.TKOptionMenu, text=element.Tooltip, - # timeout=DEFAULT_TOOLTIP_TIME) - # # ------------------------- LISTBOX element ------------------------- # + # ------------------------- LISTBOX element ------------------------- # elif element_type == ELEM_TYPE_INPUT_LISTBOX: element = element # type: Listbox element.Widget = remi.gui.ListView.new_from_list(element.Values) do_font_and_color(element.Widget) + if element.ChangeSubmits: + element.Widget.onselection.connect(element.ChangedCallback) tk_row_frame.append(element.Widget) # max_line_len = max([len(str(l)) for l in element.Values]) if len(element.Values) != 0 else 0 # if auto_size_text is False: @@ -4318,7 +4267,12 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form): # timeout=DEFAULT_TOOLTIP_TIME) # ------------------------- INPUT MULTI LINE element ------------------------- # elif element_type == ELEM_TYPE_INPUT_MULTILINE: - pass + element = element # type: Multiline + element.Widget = remi.gui.TextInput(single_line=False, hint=element.DefaultText) + do_font_and_color(element.Widget) + if element.ChangeSubmits: + element.Widget.onkeydown.connect(element.InputTextCallback) + tk_row_frame.append(element.Widget) # default_text = element.DefaultText # width, height = element_size # element.TKText = tk.scrolledtext.ScrolledText(tk_row_frame, width=width, height=height, wrap='word', @@ -4341,12 +4295,23 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form): # element.TKText['state'] = 'disabled' # if element.Tooltip is not None: # element.TooltipObject = ToolTip(element.TKText, text=element.Tooltip, timeout=DEFAULT_TOOLTIP_TIME) + # ------------------------- OUTPUT MULTI LINE element ------------------------- # + elif element_type == ELEM_TYPE_MULTILINE_OUTPUT: + element = element # type: MultilineOutput + element.Widget = remi.gui.TextInput(single_line=False) + element.Disabled = True + if element.DefaultText: + element.Widget.set_value(element.DefaultText) + do_font_and_color(element.Widget) + tk_row_frame.append(element.Widget) # ------------------------- INPUT CHECKBOX element ------------------------- # elif element_type == ELEM_TYPE_INPUT_CHECKBOX: element = element # type: Checkbox element.Widget = remi.gui.CheckBoxLabel(element.Text) if element.InitialState: element.Widget.set_value(element.InitialState) + if element.ChangeSubmits: + element.Widget.onchange.connect(element.ChangedCallback) do_font_and_color(element.Widget) tk_row_frame.append(element.Widget) @@ -4425,9 +4390,16 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form): # element.TKRadio.pack(side=tk.LEFT, padx=element.Pad[0], pady=element.Pad[1]) # if element.Tooltip is not None: # element.TooltipObject = ToolTip(element.TKRadio, text=element.Tooltip, timeout=DEFAULT_TOOLTIP_TIME) - # ------------------------- INPUT SPIN Box element ------------------------- # + # ------------------------- INPUT SPIN element ------------------------- # elif element_type == ELEM_TYPE_INPUT_SPIN: - pass + element = element # type: Spin + element.Widget = remi.gui.SpinBox(50, 0, 100) + if element.DefaultValue is not None: + element.Widget.set_value(element.DefaultValue) + do_font_and_color(element.Widget) + if element.ChangeSubmits: + element.Widget.onchange.connect(element.ChangedCallback) + tk_row_frame.append(element.Widget) # width, height = element_size # width = 0 if auto_size_text else element_size[0] # element.TKStringVar = tk.StringVar() @@ -4640,10 +4612,18 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form): # if element.Tooltip is not None: # element.TooltipObject = ToolTip(element.TKNotebook, text=element.Tooltip, # timeout=DEFAULT_TOOLTIP_TIME) - # ------------------------- SLIDER Box element ------------------------- # + # ------------------------- SLIDER element ------------------------- # elif element_type == ELEM_TYPE_INPUT_SLIDER: - pass - # slider_length = element_size[0] * CharWidthInPixels() + element = element # type: Slider + element.Widget = remi.gui.Slider(layout_orientation = remi.gui.Widget.LAYOUT_HORIZONTAL, default_value=element.DefaultValue, min=element.Range[0], max=element.Range[1],step=element.Resolution) + if element.DefaultValue: + element.Widget.set_value(element.DefaultValue) + # if element.Orientation.startswith('v'): + # element.Widget.layout_orientation = remi.gui.Widget.LAYOUT_VERTICAL + do_font_and_color(element.Widget) + if element.ChangeSubmits: + element.Widget.onchange.connect(element.SliderCallback) + tk_row_frame.append(element.Widget) # slider_length = element_size[0] * CharWidthInPixels() # slider_width = element_size[1] # element.TKIntVar = tk.IntVar() # element.TKIntVar.set(element.DefaultValue) @@ -6499,25 +6479,52 @@ def PopupGetText(message, default_text='', password_char='', size=(None, None), def main(): ChangeLookAndFeel('GreenTan' ) - # SetOptions(background_color='blue', text_element_background_color='blue', text_color='white') - layout = [[Text('You are running the PySimpleGUI.py file itself', font='Courier 20')], - [Text('You should be importing it rather than running it', size=(60, 1))], - [Text('Here is your sample window....')], - [Text('Source Folder', justification='right', size=(40,1)), InputText('Source', focus=True, disabled=True), - FolderBrowse()], - [Text('Destination Folder', justification='right', size=(40,1)), InputText('Dest'), FolderBrowse()], - [Ok(), Cancel(disabled=True), Exit()]] - window = Window('Demo window..', font='Arial 18').Layout(layout) + # Popup('Popup Test') + + # SetOptions(background_color='blue', text_element_background_color='blue', text_color='white') + # layout = [[Text('You are running the PySimpleGUI.py file itself', font='Any 25', size=(60,1), tooltip='My tooltip!')], + # [Text('You should be importing it rather than running it', size=(60, 1))], + # [Text('Here is your sample window....')], + # [Text('Source Folder', justification='right', size=(40,1)), InputText('Source', focus=True, disabled=True), + # FolderBrowse()], + # [Text('Destination Folder', justification='right', size=(40,1)), InputText('Dest'), FolderBrowse()], + # [Ok(), Cancel(disabled=True), Exit(tooltip='Exit button'), Button('Hidden Button', visible=False)]] + + layout = [ + [Text('PySimpleGUIWeb Demo of Working Elements', tooltip='text', font=('Comic sans ms', 20), text_color='red', enable_events=True, key='_PySimpleGUIWeb_')], + [T('Current Time '), Text('Text', key='_TEXT_', font='Arial 18', text_color='black', size=(30,1))], + [T('Up Time'), Text('Text', key='_TEXT_UPTIME_', font='Arial 18', text_color='black', size=(30,1))], + [Input('Single Line Input', do_not_clear=True, enable_events=True, size=(30, 1))], + [Multiline('Multiline Input', do_not_clear=True, size=(40, 4), enable_events=True, key='_MULTI_IN_')], + [MultilineOutput('Multiline Output', size=(80, 8), text_color='blue', font='Courier 12', key='_MULTIOUT_')], + [Checkbox('Checkbox 1', enable_events=True, key='_CB1_'), Checkbox('Checkbox 2', default=True, key='_CB2_', enable_events=True)], + [Combo(values=['Combo 1', 'Combo 2', 'Combo 3'], default_value='Combo 2', key='_COMBO_', enable_events=True, + readonly=False, tooltip='Combo box', disabled=False, size=(12, 1))], + [Listbox(values=('Listbox 1', 'Listbox 2', 'Listbox 3'), enable_events =True, size=(10, 3), key='_LIST_')], + [Slider((1, 100), default_value=80, key='_SLIDER_', visible=True, enable_events=True)], + [Spin(values=(1, 2, 3), initial_value='2', size=(4, 1), key='_SPIN_', enable_events=True)], + [OK(), Button('Hidden', visible=False), Button('Exit', button_color=('white', 'red'))] + ] + + + window = Window('PySimpleGUIWeb Window', font='Arial 18', default_element_size=(12,1)).Layout(layout) + + start_time = datetime.datetime.now() while True: - event, values = window.Read() - print(event, values) + event, values = window.Read(timeout=0) + window.Element('_TEXT_').Update(str(datetime.datetime.now())) + window.Element('_TEXT_UPTIME_').Update(str(datetime.datetime.now()-start_time)) + print(event, values) if event != TIMEOUT_KEY else None if event in (None, 'Exit'): break + elif event == 'OK': + window.Element('_MULTIOUT_').Update('You clicked the OK button', append=True) + elif event != TIMEOUT_KEY: + window.Element('_MULTIOUT_').Update(str(event) + '\n'+str(values), append=True) window.Close() if __name__ == '__main__': main() - print('back from main') exit(69) From 0488a5e95e67d32ab0294f5c5cec6c686a208b61 Mon Sep 17 00:00:00 2001 From: MikeTheWatchGuy <13696193+MikeTheWatchGuy@users.noreply.github.com> Date: Sat, 26 Jan 2019 14:34:40 -0500 Subject: [PATCH 2/6] PySimpleGUIWeb/readme.md updated from https://stackedit.io/ --- PySimpleGUIWeb/readme.md | 41 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/PySimpleGUIWeb/readme.md b/PySimpleGUIWeb/readme.md index ed549778..2ab2b5d6 100644 --- a/PySimpleGUIWeb/readme.md +++ b/PySimpleGUIWeb/readme.md @@ -26,9 +26,18 @@ This Readme is for information ***specific to*** the Web port of PySimpleGUI. PySimpleGUIWeb enables you to run your PySimpleGUI programs in your web browser. It utilizes a package called Remi to achieve this amazing package. -At the moment (22-Jan-2019) the port has barely begun but it's far enough along to see that it's going to work. The Text, Input Text and Button elements are "functional". You can run simple PySimpleGUI programs and they actually WORK correctly. +At the moment (Jan 26 2019) these elements are operational: +* Text +* Single line text input +* Multiline Input +* Multiline Output +* Listbox +* Combobox +* Checkbox +* Slider +* Spinner (numbers only...hardcoded to 0 to 100) -## Engineering Pre-Release Version 0.2.0 +## Engineering Pre-Release Version 0.4.0 [Announcements of Latest Developments](https://github.com/MikeTheWatchGuy/PySimpleGUI/issues/142) @@ -63,7 +72,7 @@ You can learn more about Remi on its homepage. https://github.com/dddomodossola/remi -PySimpleGUIWeb runs only on Python 3. Legacy Python is not supported. +PySimpleGUIWeb runs only on Python 3. Legacy Python (2.7) is not supported. @@ -115,6 +124,30 @@ Day 2 of development brings fonts, sizes, and colors... * Listbox Element * Element padding for all elements +## 0.4.0 PySimpleGUIWeb 26-Jan-2019 + +Functioning Elements +* Text +* Single line text input +* Multiline Input +* Multiline Output +* Listbox +* Combobox +* Checkbox +* Slider +* Spinner (numbers only...hardcoded to 0 to 100) + +New features +* Tooltips for all elements (so cool this works) +* Input Text events +* Text clicked event +* Listbox selected event +* Combobox selected event +* Checkbox Update +* Disable parameter for all elements +* Window.Close shuts down the server +* Enabled exceptions during packing operation +* New test harness exercises all element types @@ -128,5 +161,5 @@ Day 2 of development brings fonts, sizes, and colors... # Acknowledgments \ No newline at end of file From e69f0d52473eb6f913ee00b003fb781c3efc2dbc Mon Sep 17 00:00:00 2001 From: MikeTheWatchGuy <13696193+MikeTheWatchGuy@users.noreply.github.com> Date: Sat, 26 Jan 2019 14:36:13 -0500 Subject: [PATCH 3/6] PySimpleGUIWeb/readme.md updated from https://stackedit.io/ --- PySimpleGUIWeb/readme.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/PySimpleGUIWeb/readme.md b/PySimpleGUIWeb/readme.md index 2ab2b5d6..8f26d844 100644 --- a/PySimpleGUIWeb/readme.md +++ b/PySimpleGUIWeb/readme.md @@ -74,8 +74,7 @@ https://github.com/dddomodossola/remi PySimpleGUIWeb runs only on Python 3. Legacy Python (2.7) is not supported. - - + ## What Works * Text Element @@ -161,5 +160,5 @@ New features # Acknowledgments \ No newline at end of file From 871b441a5353bd85fa3cd3bbfc1864aeb3b1b0f9 Mon Sep 17 00:00:00 2001 From: MikeTheWatchGuy <13696193+MikeTheWatchGuy@users.noreply.github.com> Date: Sat, 26 Jan 2019 14:42:55 -0500 Subject: [PATCH 4/6] PySimpleGUIWeb/readme.md updated from https://stackedit.io/ --- PySimpleGUIWeb/readme.md | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/PySimpleGUIWeb/readme.md b/PySimpleGUIWeb/readme.md index 8f26d844..9de9e8c8 100644 --- a/PySimpleGUIWeb/readme.md +++ b/PySimpleGUIWeb/readme.md @@ -8,13 +8,15 @@ ![Python Version](https://img.shields.io/badge/Python-3.x-yellow.svg) -![Python Version](https://img.shields.io/badge/PySimpleGUIWeb_Version-0.3.0-orange.svg?longCache=true&style=for-the-badge) +![Python Version](https://img.shields.io/badge/PySimpleGUIWeb_Version-0.4.0-orange.svg?longCache=true&style=for-the-badge) # PySimpleGUIWeb -PySimpleGUI running in your web browser +PySimpleGUI running in your web browser! + +Your source code will work on tkinter, Qt, W ## Primary PySimpleGUI Documentation @@ -52,14 +54,14 @@ Installation is quite simple: Should this not work, you can copy and paste the file PySimpleGUIWeb.py into your application folder. -## Using +## Using PySimpleGUIWeb There are a lot of examples in the PySimpleGUI Cookbook as well as on the GitHub site. At the moment very few will work due to the limited number of features of the 0.1.0 release. It shouldn't be too long before they'll work. To use PySimpleGUIWeb you need to import it: `import PySimpleGUIWeb as sg` -From there follow the code examples in the Coookbook and the Demo Programs. The only difference in those programs is the import statement. The remainder of the code should work without modification. +From there follow the code examples in the Cookbook and the Demo Programs. The only difference in those programs is the import statement. The remainder of the code should work without modification. ## Requirements @@ -160,5 +162,6 @@ New features # Acknowledgments \ No newline at end of file From 9c3f0d7a4699555afafe9567193d8d77725a90b5 Mon Sep 17 00:00:00 2001 From: MikeTheWatchGuy <13696193+MikeTheWatchGuy@users.noreply.github.com> Date: Sat, 26 Jan 2019 14:44:28 -0500 Subject: [PATCH 5/6] PySimpleGUIWeb/readme.md updated from https://stackedit.io/ --- PySimpleGUIWeb/readme.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/PySimpleGUIWeb/readme.md b/PySimpleGUIWeb/readme.md index 9de9e8c8..8c665a36 100644 --- a/PySimpleGUIWeb/readme.md +++ b/PySimpleGUIWeb/readme.md @@ -1,14 +1,14 @@ ![pysimplegui_logo](https://user-images.githubusercontent.com/13696193/43165867-fe02e3b2-8f62-11e8-9fd0-cc7c86b11772.png) -![Downloads](http://pepy.tech/badge/pysimpleguiweb)] +![Downloads](http://pepy.tech/badge/pysimpleguiweb)] - ![Awesome Meter](https://img.shields.io/badge/Awesome_meter-1000-yellow.svg) + ![Awesome Meter](https://img.shields.io/badge/Awesome_meter-10,000-yellow.svg) ![Python Version](https://img.shields.io/badge/Python-3.x-yellow.svg) -![Python Version](https://img.shields.io/badge/PySimpleGUIWeb_Version-0.4.0-orange.svg?longCache=true&style=for-the-badge) +![Python Version](https://img.shields.io/badge/PySimpleGUIWeb_-0.4.0-orange.svg?longCache=true&style=for-the-badge) @@ -16,7 +16,7 @@ PySimpleGUI running in your web browser! -Your source code will work on tkinter, Qt, W +Your source code will work on tkinter, Qt, WxPython and now in a browser (thanks to Remi) ## Primary PySimpleGUI Documentation @@ -162,6 +162,6 @@ New features # Acknowledgments \ No newline at end of file From d721437a00f5cbee5b70a322380aa3cd0b8f5c98 Mon Sep 17 00:00:00 2001 From: MikeTheWatchGuy <13696193+MikeTheWatchGuy@users.noreply.github.com> Date: Sat, 26 Jan 2019 14:44:47 -0500 Subject: [PATCH 6/6] PySimpleGUIWeb/readme.md updated from https://stackedit.io/ --- PySimpleGUIWeb/readme.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/PySimpleGUIWeb/readme.md b/PySimpleGUIWeb/readme.md index 8c665a36..3383b6f9 100644 --- a/PySimpleGUIWeb/readme.md +++ b/PySimpleGUIWeb/readme.md @@ -1,7 +1,7 @@ ![pysimplegui_logo](https://user-images.githubusercontent.com/13696193/43165867-fe02e3b2-8f62-11e8-9fd0-cc7c86b11772.png) -![Downloads](http://pepy.tech/badge/pysimpleguiweb)] +![Downloads](http://pepy.tech/badge/pysimpleguiweb) ![Awesome Meter](https://img.shields.io/badge/Awesome_meter-10,000-yellow.svg) @@ -162,6 +162,6 @@ New features # Acknowledgments \ No newline at end of file