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
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('<Button-1>', 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('<<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 ------------------------- #
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)

View File

@ -1,20 +1,22 @@
![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.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
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
@ -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.
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)
@ -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.
## 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
@ -63,10 +74,9 @@ 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.
## What Works
* Text Element
@ -115,6 +125,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 +162,6 @@ Day 2 of development brings fonts, sizes, and colors...
# Acknowledgments
<!--stackedit_data:
eyJoaXN0b3J5IjpbLTExNjA2ODQzMzldfQ==
eyJoaXN0b3J5IjpbLTEwNTcxMDM2NDMsMTIxMzM1MjYzNiwtMT
E2MDY4NDMzOV19
-->