Merge pull request #1115 from PySimpleGUI/Dev-latest

Dev latest
This commit is contained in:
MikeTheWatchGuy 2019-01-26 14:48:09 -05:00 committed by GitHub
commit 3bb8380f40
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 361 additions and 319 deletions

View File

@ -12,6 +12,8 @@ from queue import Queue
# from remi import start, App # from remi import start, App
import remi import remi
import logging import logging
import traceback
g_time_start = 0 g_time_start = 0
g_time_end = 0 g_time_end = 0
@ -229,9 +231,11 @@ ELEM_TYPE_INPUT_COMBO = 'combo'
ELEM_TYPE_INPUT_OPTION_MENU = 'option menu' ELEM_TYPE_INPUT_OPTION_MENU = 'option menu'
ELEM_TYPE_INPUT_RADIO = 'radio' ELEM_TYPE_INPUT_RADIO = 'radio'
ELEM_TYPE_INPUT_MULTILINE = 'multiline' ELEM_TYPE_INPUT_MULTILINE = 'multiline'
ELEM_TYPE_MULTILINE_OUTPUT = 'multioutput'
ELEM_TYPE_INPUT_CHECKBOX = 'checkbox' ELEM_TYPE_INPUT_CHECKBOX = 'checkbox'
ELEM_TYPE_INPUT_SPIN = 'spind' ELEM_TYPE_INPUT_SPIN = 'spin'
ELEM_TYPE_BUTTON = 'button' ELEM_TYPE_BUTTON = 'button'
ELEM_TYPE_BUTTONMENU = 'buttonmenu'
ELEM_TYPE_IMAGE = 'image' ELEM_TYPE_IMAGE = 'image'
ELEM_TYPE_CANVAS = 'canvas' ELEM_TYPE_CANVAS = 'canvas'
ELEM_TYPE_FRAME = 'frame' ELEM_TYPE_FRAME = 'frame'
@ -387,6 +391,12 @@ class Element():
return rc return rc
return None 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): def TextClickedHandler(self, event):
if self.Key is not None: if self.Key is not None:
self.ParentForm.LastButtonClicked = self.Key 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, 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) 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): def Update(self, value=None, disabled=None):
if disabled is True: if disabled is True:
self.TKEntry['state'] = 'disabled' 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) 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): 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: if values is not None:
@ -678,14 +688,14 @@ InputOptionMenu = OptionMenu
# Listbox # # Listbox #
# ---------------------------------------------------------------------- # # ---------------------------------------------------------------------- #
class Listbox(Element): 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, 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)):
text_color=None, key=None, pad=None, tooltip=None, visible=True, size_px=(None,None)): """
'''
Listbox Element
:param values: :param values:
:param default_values: :param default_values:
:param select_mode: :param select_mode:
:param change_submits: :param change_submits:
:param enable_events:
:param bind_return_key: :param bind_return_key:
:param size: :param size:
:param disabled: :param disabled:
@ -696,7 +706,9 @@ class Listbox(Element):
:param key: :param key:
:param pad: :param pad:
:param tooltip: :param tooltip:
''' :param visible:
:param size_px:
"""
self.Values = values self.Values = values
self.DefaultValues = default_values self.DefaultValues = default_values
self.TKListbox = None 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, 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) 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): 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: if values is not None:
self.Values = values self.Values = values
@ -849,20 +856,21 @@ class Checkbox(Element):
background_color=background_color, text_color=self.TextColor, key=key, pad=pad, background_color=background_color, text_color=self.TextColor, key=key, pad=pad,
tooltip=tooltip, visible=visible, size_px=size_px) 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): def Get(self):
return self.WxCheckbox.GetValue() return self.Widget.get_value()
def Update(self, value=None, disabled=None): def Update(self, value=None, disabled=None):
if value is not None: if value is not None:
try: self.Widget.set_value(value)
self.WxCheckbox.SetValue(value)
self.InitialState = value
except:
pass
if disabled == True: if disabled == True:
self.WxCheckbox.Disable() self.Widget.set_enabled(False)
elif disabled == False: elif disabled == False:
self.WxCheckbox.Enable() self.Widget.set_enabled(True)
def __del__(self): def __del__(self):
super().__del__() super().__del__()
@ -882,9 +890,8 @@ Check = Checkbox
class Spin(Element): class Spin(Element):
# Values = None # Values = None
# TKSpinBox = None # TKSpinBox = None
def __init__(self, values, initial_value=None, disabled=False, change_submits=False, size=(None, 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,
auto_size_text=None, font=None, background_color=None, text_color=None, key=None, pad=None, tooltip=None, visible=True, size_px=(None,None)):
tooltip=None):
''' '''
Spinner Element Spinner Element
:param values: :param values:
@ -901,50 +908,41 @@ class Spin(Element):
:param tooltip: :param tooltip:
''' '''
self.Values = values self.Values = values
self.DefaultValue = initial_value self.DefaultValue = initial_value or values[0]
self.ChangeSubmits = change_submits self.ChangeSubmits = change_submits or enable_events
self.TKSpinBox = None
self.Disabled = disabled self.Disabled = disabled
bg = background_color if background_color else DEFAULT_INPUT_ELEMENTS_COLOR 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 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, 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 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: if values != None:
old_value = self.TKStringVar.get()
self.Values = values self.Values = values
self.TKSpinBox.configure(values=values) self.QT_Spinner.setStrings(values)
self.TKStringVar.set(old_value) # self.QT_Spinner.setRange(self.Values[0], self.Values[1])
if value is not None: if value is not None:
# self.QT_Spinner.setValue(value)
try: try:
self.TKStringVar.set(value) self.QT_Spinner.setValue(self.QT_Spinner.valueFromText(value))
self.DefaultValue = value
except: except:
pass pass
self.DefaultValue = value
if disabled == True: if disabled == True:
self.TKSpinBox.configure(state='disabled') self.QT_Spinner.setDisabled(True)
elif disabled == False: 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): def Get(self):
# first, get the results table built return self.Widget.get_value()
# 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 __del__(self): def __del__(self):
try:
self.TKSpinBox.__del__()
except:
pass
super().__del__() super().__del__()
@ -953,9 +951,8 @@ class Spin(Element):
# ---------------------------------------------------------------------- # # ---------------------------------------------------------------------- #
class Multiline(Element): class Multiline(Element):
def __init__(self, default_text='', enter_submits=False, disabled=False, autoscroll=False, size=(None, None), 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, auto_size_text=None, background_color=None, text_color=None, change_submits=False, enable_events=False, do_not_clear=False,
key=None, focus=False, key=None, focus=False, font=None, pad=None, tooltip=None, visible=True, size_px=(None,None)):
font=None, pad=None, tooltip=None):
''' '''
Multiline Element Multiline Element
:param default_text: :param default_text:
@ -981,42 +978,122 @@ class Multiline(Element):
fg = text_color if text_color is not None else DEFAULT_INPUT_TEXT_COLOR fg = text_color if text_color is not None else DEFAULT_INPUT_TEXT_COLOR
self.Autoscroll = autoscroll self.Autoscroll = autoscroll
self.Disabled = disabled 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, 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) text_color=fg, key=key, pad=pad, tooltip=tooltip, font=font or DEFAULT_FONT, visible=visible, size_px=size_px)
return return
def Update(self, value=None, disabled=None, append=False, font=None, text_color=None, background_color=None): def InputTextCallback(self, widget:remi.Widget, value, keycode):
if value is not None: # print(f'text widget value = {widget.get_value()}')
try: self.ParentForm.LastButtonClicked = chr(int(keycode))
if not append: self.ParentForm.MessageQueue.put(self.ParentForm.LastButtonClicked)
self.TKText.delete('1.0', tk.END)
self.TKText.insert(tk.END, value) def Update(self, value=None, disabled=None, append=False, background_color=None, text_color=None, font=None, visible=None):
except: if value is not None and not append:
pass self.Widget.set_value(value)
self.DefaultText = value elif value is not None and append:
if self.Autoscroll: text = self.Widget.get_value() + str(value)
self.TKText.see(tk.END) self.Widget.set_value(text)
if disabled == True: # if background_color is not None:
self.TKText.configure(state='disabled') # self.WxTextCtrl.SetBackgroundColour(background_color)
elif disabled == False: # if text_color is not None:
self.TKText.configure(state='normal') # self.WxTextCtrl.SetForegroundColour(text_color)
if background_color is not None: # if font is not None:
self.TKText.configure(background=background_color) # self.WxTextCtrl.SetFont(font)
if text_color is not None: # if disabled:
self.TKText.configure(fg=text_color) # self.WxTextCtrl.Enable(True)
if font is not None: # elif disabled is False:
self.TKText.configure(font=font) # 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): def Get(self):
return self.TKText.get(1.0, tk.END) self.WxTextCtrl.GetValue()
def SetFocus(self): def SetFocus(self):
try: self.WxTextCtrl.SetFocus()
self.TKText.focus_set()
except:
pass 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): def __del__(self):
super().__del__() super().__del__()
@ -2101,26 +2178,23 @@ class TabGroup(Element):
# ---------------------------------------------------------------------- # # ---------------------------------------------------------------------- #
class Slider(Element): class Slider(Element):
def __init__(self, range=(None, None), default_value=None, resolution=None, tick_interval=None, orientation=None, 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, 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): background_color=None, text_color=None, key=None, pad=None, tooltip=None, visible=True, size_px=(None,None)):
''' """
Slider Element
:param range: :param range:
:param default_value: :param default_value:
:param resolution: :param resolution:
:param tick_interval:
:param orientation: :param orientation:
:param border_width: :param border_width:
:param relief: :param relief:
:param change_submits: :param change_submits:
:param enable_events:
:param disabled: :param disabled:
:param size: :param visible:
:param font: :param size_px:
:param background_color: """
:param text_color:
:param key:
:param pad:
:param tooltip:
'''
self.TKScale = None self.TKScale = None
self.Range = (1, 10) if range == (None, None) else range self.Range = (1, 10) if range == (None, None) else range
self.DefaultValue = self.Range[0] if default_value is None else default_value 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.BorderWidth = border_width if border_width else DEFAULT_SLIDER_BORDER_WIDTH
self.Relief = relief if relief else DEFAULT_SLIDER_RELIEF self.Relief = relief if relief else DEFAULT_SLIDER_RELIEF
self.Resolution = 1 if resolution is None else resolution self.Resolution = 1 if resolution is None else resolution
self.ChangeSubmits = change_submits self.ChangeSubmits = change_submits or enable_events
self.Disabled = disabled self.Disabled = disabled
self.TickInterval = tick_interval self.TickInterval = tick_interval
temp_size = size temp_size = size
if temp_size == (None, None): 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, 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 return
def Update(self, value=None, range=(None, None), disabled=None): def Update(self, value=None, range=(None, None), disabled=None):
@ -2153,21 +2230,15 @@ class Slider(Element):
elif disabled == False: elif disabled == False:
self.TKScale['state'] = 'normal' self.TKScale['state'] = 'normal'
def SliderChangedHandler(self, event): def SliderCallback(self, widget:remi.Widget, value):
# first, get the results table built self.ParentForm.LastButtonClicked = self.Key if self.Key is not None else ''
# modify the Results table in the parent FlexForm object self.ParentForm.MessageQueue.put(self.ParentForm.LastButtonClicked)
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 __del__(self): def __del__(self):
super().__del__() super().__del__()
# #
# ---------------------------------------------------------------------- # # ---------------------------------------------------------------------- #
# Column # # Column #
@ -2544,6 +2615,7 @@ class Window:
highest_level_app = None highest_level_app = None
stdout_is_rerouted = False stdout_is_rerouted = False
stdout_location = None stdout_location = None
port_number = 6900
def __init__(self, title, default_element_size=DEFAULT_ELEMENT_SIZE, default_button_element_size=(None, None), 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), 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("** REALTIME PROBLEM FOUND **", results)
# print('****************** CALLING MESSAGE QUEUE GET ***********************') # print('****************** CALLING MESSAGE QUEUE GET ***********************')
self.CurrentlyRunningMainloop = True self.CurrentlyRunningMainloop = True
if timeout is not None and timeout != 0: if timeout is not None:
try: 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 except: # timeout
self.LastButtonClicked = timeout_key self.LastButtonClicked = timeout_key
elif timeout == 0:
try:
self.LastButtonClicked = self.MessageQueue.get_nowait()
except:
self.LastButtonClicked = timeout_key
else: else:
self.LastButtonClicked = self.MessageQueue.get() self.LastButtonClicked = self.MessageQueue.get()
# print(f'Got event {self.LastButtonClicked}')
# print('--------------------- BACK FROM MESSAGE QUEUE GET ----------------------') # print('--------------------- BACK FROM MESSAGE QUEUE GET ----------------------')
results = BuildResults(self, False, self) results = BuildResults(self, False, self)
# print('Results = ', results)
return results return results
# print(f'In main {self.Title}') # print(f'In main {self.Title}')
################################# CALL GUWxTextCtrlI MAINLOOP ############################ ################################# CALL GUWxTextCtrlI MAINLOOP ############################
@ -2815,7 +2883,6 @@ class Window:
# print("*** Faking timeout ***") # print("*** Faking timeout ***")
# self.ReturnValues = self.TimeoutKey, self.ReturnValues[1] # fake a timeout # self.ReturnValues = self.TimeoutKey, self.ReturnValues[1] # fake a timeout
# return self.ReturnValues # return self.ReturnValues
return None, None
def _ReadNonBlocking(self): def _ReadNonBlocking(self):
if self.TKrootDestroyed: if self.TKrootDestroyed:
@ -3001,7 +3068,8 @@ class Window:
def Close(self): def Close(self):
self.App.close() self.App.close()
self.App.server.server_starter_instance._alive = False
self.App.server.server_starter_instance._sserver.shutdown()
if self.TKrootDestroyed: if self.TKrootDestroyed:
return return
# try: # try:
@ -3112,6 +3180,7 @@ class Window:
def remi_thread(self): def remi_thread(self):
logging.getLogger('remi').setLevel(logging.WARNING)
logging.getLogger('remi').disabled = True logging.getLogger('remi').disabled = True
logging.getLogger('remi.server.ws').disabled = True logging.getLogger('remi.server.ws').disabled = True
logging.getLogger('remi.server').disabled = True logging.getLogger('remi.server').disabled = True
@ -3122,8 +3191,15 @@ class Window:
# logging.getLogger('remi').setLevel(level=logging.CRITICAL) # logging.getLogger('remi').setLevel(level=logging.CRITICAL)
# logging.getLogger('remi').disabled = True # logging.getLogger('remi').disabled = True
# logging.disable(logging.CRITICAL) # 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) # s = remi.server.StandaloneServer(self.MyApp, width=1100, height=600)
self.MessageQueue.put(None) # 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): class MyApp(remi.App):
@ -3142,33 +3218,29 @@ class Window:
wid.style['align-items'] = 'baseline' wid.style['align-items'] = 'baseline'
if self.window.BackgroundColor not in (None, COLOR_SYSTEM_DEFAULT): if self.window.BackgroundColor not in (None, COLOR_SYSTEM_DEFAULT):
wid.style['background-color'] = self.window.BackgroundColor wid.style['background-color'] = self.window.BackgroundColor
try:
PackFormIntoFrame(self.window, wid, self.window) PackFormIntoFrame(self.window, wid, self.window)
# except:
# lbl = remi.gui.Label("Close or reload the page, the console thread will stop automatically.") print('* ERROR PACKING FORM *')
# wid.append(lbl) print(traceback.format_exc())
# self.bt = remi.gui.Button('Close')
# self.bt.onclick.connect(self.on_close_button)
# wid.append(self.bt)
# add the following 3 lines to your app and the on_window_close method to make the console close automatically # 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 = remi.gui.Tag(_type='script')
tag.add_child("javascript", """window.onunload=function(e){sendCallback('%s','%s');return "close?";};""" % ( tag.add_child("javascript", """window.onunload=function(e){sendCallback('%s','%s');return "close?";};""" % (
str(id(self)), "on_window_close")) str(id(self)), "on_window_close"))
wid.add_child("onunloadevent", tag) wid.add_child("onunloadevent", tag)
# TODO: Send message that layout is complete self.window.MessageQueue.put('Layout complete') # signal the main code that the layout is all done
return wid # returning the root widget
self.window.MessageQueue.put('Layout complete')
# returning the root widget
return wid
def on_window_close(self): def on_window_close(self):
# here you can handle the unload # here you can handle the unload
# print("app closing") # print("app closing")
self.window.MessageQueue.put(None)
self.close() 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 FlexForm = Window
@ -3184,8 +3256,11 @@ def element_callback_quit_mainloop(element):
element.ParentForm.LastButtonClicked = element.Key element.ParentForm.LastButtonClicked = element.Key
else: else:
element.ParentForm.LastButtonClicked = '' element.ParentForm.LastButtonClicked = ''
element.ParentForm.FormRemainedOpen = True try:
element.ParentForm.LastButtonClicked = element.Key if element.Key is not None else element.ButtonText 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) element.ParentForm.MessageQueue.put(element.ParentForm.LastButtonClicked)
@ -3628,22 +3703,14 @@ def BuildResultsForSubform(form, initialize_only, top_level_form):
# items = element.TKListbox.curselection() # items = element.TKListbox.curselection()
# value = [element.Values[int(item)] for item in items] # value = [element.Values[int(item)] for item in items]
elif element.Type == ELEM_TYPE_INPUT_SPIN: elif element.Type == ELEM_TYPE_INPUT_SPIN:
try: element = element # type: Spin
value = element.TKStringVar.get() value = element.Widget.get_value()
except:
value = 0
elif element.Type == ELEM_TYPE_INPUT_SLIDER: elif element.Type == ELEM_TYPE_INPUT_SLIDER:
try: element = element # type: Slider
value = element.TKIntVar.get() value = element.Widget.get_value()
except:
value = 0
elif element.Type == ELEM_TYPE_INPUT_MULTILINE: elif element.Type == ELEM_TYPE_INPUT_MULTILINE:
try: element = element # type: Multiline
value = element.TKText.get(1.0, tk.END) value = element.Widget.get_value()
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
elif element.Type == ELEM_TYPE_TAB_GROUP: elif element.Type == ELEM_TYPE_TAB_GROUP:
try: try:
value = element.TKNotebook.tab(element.TKNotebook.index('current'))['text'] 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) widget.style['margin'] = '{}px {}px {}px {}px'.format(*full_element_pad)
if element.Disabled: if element.Disabled:
widget.set_enabled(False) 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) # widget.SetMinSize(element_size)
# if element.Disabled: # if element.Disabled:
@ -3982,8 +4053,6 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form):
else: else:
full_element_pad[0], full_element_pad[2] = elementpad[1] full_element_pad[0], full_element_pad[2] = elementpad[1]
# ------------------------- COLUMN element ------------------------- # # ------------------------- COLUMN element ------------------------- #
if element_type == ELEM_TYPE_COLUMN: if element_type == ELEM_TYPE_COLUMN:
pass pass
@ -4022,55 +4091,11 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form):
element.Widget.style['text-align'] = 'center' element.Widget.style['text-align'] = 'center'
elif element.Justification.startswith('r'): elif element.Justification.startswith('r'):
element.Widget.style['text-align'] = 'right' element.Widget.style['text-align'] = 'right'
if element.ClickSubmits:
element.Widget.onclick.connect(element.ChangedCallback)
tk_row_frame.append(element.Widget) 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('<Button-1>', element.TextClickedHandler)
# if element.Tooltip is not None:
# element.TooltipObject = ToolTip(element.TKText, text=element.Tooltip, timeout=DEFAULT_TOOLTIP_TIME)
# ------------------------- BUTTON element ------------------------- # # ------------------------- BUTTON element ------------------------- #
elif element_type == ELEM_TYPE_BUTTON: elif element_type == ELEM_TYPE_BUTTON:
element = element # type: Button element = element # type: Button
@ -4153,18 +4178,15 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form):
# if element.Tooltip is not None: # if element.Tooltip is not None:
# element.TooltipObject = ToolTip(element.TKButton, text=element.Tooltip, # element.TooltipObject = ToolTip(element.TKButton, text=element.Tooltip,
# timeout=DEFAULT_TOOLTIP_TIME) # timeout=DEFAULT_TOOLTIP_TIME)
# # ------------------------- INPUT (Single Line) element ------------------------- # # # ------------------------- INPUT element ------------------------- #
elif element_type == ELEM_TYPE_INPUT_TEXT: elif element_type == ELEM_TYPE_INPUT_TEXT:
element = element # type: InputText element = element # type: InputText
element.Widget = remi.gui.TextInput() element.Widget = remi.gui.TextInput(hint=element.DefaultText)
if element.DefaultText:
element.Widget.set_value(element.DefaultText)
do_font_and_color(element.Widget) do_font_and_color(element.Widget)
if element.ChangeSubmits:
element.Widget.onkeydown.connect(element.InputTextCallback)
tk_row_frame.append(element.Widget) 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 "" # show = element.PasswordCharacter if element.PasswordCharacter else ""
# if element.Justification is not None: # if element.Justification is not None:
# justification = element.Justification # justification = element.Justification
@ -4196,93 +4218,20 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form):
if element.DefaultValue is not None: if element.DefaultValue is not None:
element.Widget.select_by_value(element.DefaultValue) element.Widget.select_by_value(element.DefaultValue)
do_font_and_color(element.Widget) do_font_and_color(element.Widget)
if element.ChangeSubmits:
element.Widget.onchange.connect(element.ChangedCallback)
tk_row_frame.append(element.Widget) 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('<<ComboboxSelected>>', 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 ------------------------- # # ------------------------- OPTION MENU (Like ComboBox but different) element ------------------------- #
elif element_type == ELEM_TYPE_INPUT_OPTION_MENU: elif element_type == ELEM_TYPE_INPUT_OPTION_MENU:
pass pass
# max_line_len = max([len(str(l)) for l in element.Values]) # ------------------------- LISTBOX element ------------------------- #
# 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 ------------------------- #
elif element_type == ELEM_TYPE_INPUT_LISTBOX: elif element_type == ELEM_TYPE_INPUT_LISTBOX:
element = element # type: Listbox element = element # type: Listbox
element.Widget = remi.gui.ListView.new_from_list(element.Values) element.Widget = remi.gui.ListView.new_from_list(element.Values)
do_font_and_color(element.Widget) do_font_and_color(element.Widget)
if element.ChangeSubmits:
element.Widget.onselection.connect(element.ChangedCallback)
tk_row_frame.append(element.Widget) 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 # 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: # if auto_size_text is False:
@ -4318,7 +4267,12 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form):
# timeout=DEFAULT_TOOLTIP_TIME) # timeout=DEFAULT_TOOLTIP_TIME)
# ------------------------- INPUT MULTI LINE element ------------------------- # # ------------------------- INPUT MULTI LINE element ------------------------- #
elif element_type == ELEM_TYPE_INPUT_MULTILINE: 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 # default_text = element.DefaultText
# width, height = element_size # width, height = element_size
# element.TKText = tk.scrolledtext.ScrolledText(tk_row_frame, width=width, height=height, wrap='word', # 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' # element.TKText['state'] = 'disabled'
# if element.Tooltip is not None: # if element.Tooltip is not None:
# element.TooltipObject = ToolTip(element.TKText, text=element.Tooltip, timeout=DEFAULT_TOOLTIP_TIME) # 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 ------------------------- # # ------------------------- INPUT CHECKBOX element ------------------------- #
elif element_type == ELEM_TYPE_INPUT_CHECKBOX: elif element_type == ELEM_TYPE_INPUT_CHECKBOX:
element = element # type: Checkbox element = element # type: Checkbox
element.Widget = remi.gui.CheckBoxLabel(element.Text) element.Widget = remi.gui.CheckBoxLabel(element.Text)
if element.InitialState: if element.InitialState:
element.Widget.set_value(element.InitialState) element.Widget.set_value(element.InitialState)
if element.ChangeSubmits:
element.Widget.onchange.connect(element.ChangedCallback)
do_font_and_color(element.Widget) do_font_and_color(element.Widget)
tk_row_frame.append(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]) # element.TKRadio.pack(side=tk.LEFT, padx=element.Pad[0], pady=element.Pad[1])
# if element.Tooltip is not None: # if element.Tooltip is not None:
# element.TooltipObject = ToolTip(element.TKRadio, text=element.Tooltip, timeout=DEFAULT_TOOLTIP_TIME) # 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: 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, height = element_size
# width = 0 if auto_size_text else element_size[0] # width = 0 if auto_size_text else element_size[0]
# element.TKStringVar = tk.StringVar() # element.TKStringVar = tk.StringVar()
@ -4640,10 +4612,18 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form):
# if element.Tooltip is not None: # if element.Tooltip is not None:
# element.TooltipObject = ToolTip(element.TKNotebook, text=element.Tooltip, # element.TooltipObject = ToolTip(element.TKNotebook, text=element.Tooltip,
# timeout=DEFAULT_TOOLTIP_TIME) # timeout=DEFAULT_TOOLTIP_TIME)
# ------------------------- SLIDER Box element ------------------------- # # ------------------------- SLIDER element ------------------------- #
elif element_type == ELEM_TYPE_INPUT_SLIDER: elif element_type == ELEM_TYPE_INPUT_SLIDER:
pass element = element # type: Slider
# slider_length = element_size[0] * CharWidthInPixels() 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] # slider_width = element_size[1]
# element.TKIntVar = tk.IntVar() # element.TKIntVar = tk.IntVar()
# element.TKIntVar.set(element.DefaultValue) # element.TKIntVar.set(element.DefaultValue)
@ -6499,25 +6479,52 @@ def PopupGetText(message, default_text='', password_char='', size=(None, None),
def main(): def main():
ChangeLookAndFeel('GreenTan' ) 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: while True:
event, values = window.Read() event, values = window.Read(timeout=0)
print(event, values) 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'): if event in (None, 'Exit'):
break 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() window.Close()
if __name__ == '__main__': if __name__ == '__main__':
main() main()
print('back from main')
exit(69) exit(69)

View File

@ -1,20 +1,22 @@
![pysimplegui_logo](https://user-images.githubusercontent.com/13696193/43165867-fe02e3b2-8f62-11e8-9fd0-cc7c86b11772.png) ![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/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_-0.4.0-orange.svg?longCache=true&style=for-the-badge)
# PySimpleGUIWeb # PySimpleGUIWeb
PySimpleGUI running in your web browser PySimpleGUI running in your web browser!
Your source code will work on tkinter, Qt, WxPython and now in a browser (thanks to Remi)
## Primary PySimpleGUI Documentation ## Primary PySimpleGUI Documentation
@ -26,9 +28,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. 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) [Announcements of Latest Developments](https://github.com/MikeTheWatchGuy/PySimpleGUI/issues/142)
@ -43,14 +54,14 @@ Installation is quite simple:
Should this not work, you can copy and paste the file PySimpleGUIWeb.py into your application folder. 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. 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: To use PySimpleGUIWeb you need to import it:
`import PySimpleGUIWeb as sg` `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 ## Requirements
@ -63,10 +74,9 @@ You can learn more about Remi on its homepage.
https://github.com/dddomodossola/remi 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.
## What Works ## What Works
* Text Element * Text Element
@ -115,6 +125,30 @@ Day 2 of development brings fonts, sizes, and colors...
* Listbox Element * Listbox Element
* Element padding for all elements * 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 +162,6 @@ Day 2 of development brings fonts, sizes, and colors...
# Acknowledgments # Acknowledgments
<!--stackedit_data: <!--stackedit_data:
eyJoaXN0b3J5IjpbLTExNjA2ODQzMzldfQ== eyJoaXN0b3J5IjpbLTEwNTcxMDM2NDMsMTIxMzM1MjYzNiwtMT
E2MDY4NDMzOV19
--> -->